[프로젝트 기술서] 2차 개인프로젝트 DevNews

Glen(OH TaekJoo)·2023년 9월 14일
0

Study

목록 보기
37/53
post-thumbnail
configtitle:💼DevNews(1인프로젝트)기술서config title: 💼DevNews (1인 프로젝트 ) 기술서

👌개요

소개

  • 'DevNews' 는 개발관련 뉴스 페이지 입니다.
  • 관리자는 뉴스를 등록할 수 있으며 모든 회원은 해당뉴스를 확인 , 해당뉴스에 대한 리뷰작성 , 리뷰에 대한 댓글작성 을 할 수 있습니다.
  • 해당 프로젝트를 진행한 목적은 프론트엔드 새로운 기능의 구현 , 이전 3차 팀프로젝트 에 다른 조원이 작성한 코드를 리뷰, 커스텀 하며 공부하기 위해 해당 프로젝트를 진행하게 되었습니다.

유튜브 발표영상

movie

😋환경

형상관리

개발도구

- SpringBoot
- JAVA
- MariaDB
- HTML
- CSS
- JS
- AJax
- jQuery

개발환경

- IntelliJ IDEA
- DBeaver

😸요구사항과 기능구현 여부

요구사항 정의서

🤘ERD 구성

초기 ERD

최종 ERD

👩🏽‍🦱구현화면 및 주요 소스코드

메인화면

[codepen_embed height="300" theme_id="dark" default_tab="html,result" slug_hash="ExGZygZ" user="ohtj6644"]See the Pen
Untitled
by ohtj6644 (@ohtj6644)
on CodePen.[/codepen_embed]

 (function() {

    var slidersContainer = document.querySelector('.sliders-container');

    // 슬라이더 숫자
    var msNumbers = new MomentumSlider({
        el: slidersContainer,
        cssClass: 'ms--numbers',
        range: [1, 3],
        rangeContent: function (i) {
            return '0' + i;
        },
        style: {
            transform: [{scale: [0.4, 1]}],
            opacity: [0, 1]
        },
        interactive: false
    });

    // 슬라이더 제목
    var titles = [
        'Developer News ',
        'News Review',
        'Contact'
    ];
    var msTitles = new MomentumSlider({
        el: slidersContainer,
        cssClass: 'ms--titles',
        range: [0, 2],
        rangeContent: function (i) {
            return '<h3>'+ titles[i] +'</h3>';
        },
        vertical: true,
        reverse: true,
        style: {
            opacity: [0, 1]
        },
        interactive: false
    });

    // 링크 슬라이더 초기화 중
    var msLinks = new MomentumSlider({
    el: slidersContainer,
    cssClass: 'ms--links',
    range: [0, 2],
    rangeContent: function (i) {
        var href;
        switch (i) {
            case 0:
                href = '/news/list';
                break;
            case 1:
                href = '/review/list';
                break;
            
            default:
                href = '';
        }
        return '<a class="ms-slide__link" href="' + href + '">View Case</a>';
    },
    vertical: true,
    interactive: false
});

    // 페이지 표시 항목 가져오기
    var pagination = document.querySelector('.pagination');
    var paginationItems = [].slice.call(pagination.children);

    // 영상 슬라이더 초기화
    var msImages = new MomentumSlider({
        // 슬라이더를 추가할 요소
        el: slidersContainer,
        // 슬라이더를 참조할 CSS 클래스
        cssClass: 'ms--images',
        // 필요한 슬라이드 3개 생성
        range: [0, 2],
        rangeContent: function () {
            return '<div class="ms-slide__image-container"><div class="ms-slide__image"></div></div>';
        },
        // 다른 슬라이더 동기화
        sync: [msNumbers, msTitles, msLinks],
        // 슬라이더를 이동할 때 보간할 스타일
        style: {
            '.ms-slide__image': {
                transform: [{scale: [1.5, 1]}]
            }
        },
        // 슬라이더가 변경되면 페이지 표시 업데이트
        change: function(newIndex, oldIndex) {
            if (typeof oldIndex !== 'undefined') {
                paginationItems[oldIndex].classList.remove('pagination__item--active');
            }
            paginationItems[newIndex].classList.add('pagination__item--active');
        }
    });

    // 페이지 구성 버튼을 클릭할 때 해당 슬라이더 항목 선택
    pagination.addEventListener('click', function(e) {
        if (e.target.matches('.pagination__button')) {
            var index = paginationItems.indexOf(e.target.parentNode);
            msImages.select(index);
        }
    });

})();

회원가입

controller

 @GetMapping("/signup")
    public String signup(UserCreateForm userCreateForm) {
        return "signup";
    }

    @PostMapping("/signup")
    public String signup(@Valid UserCreateForm userCreateForm, BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            return "signup";
        }

        if (!userCreateForm.getPassword1().equals(userCreateForm.getPassword2())) {
            bindingResult.rejectValue("password2", "passwordInCorrect",
                    "2개의 패스워드가 일치하지 않습니다.");
            return "signup";
        }

        userService.create(userCreateForm.getUsername(),
                userCreateForm.getEmail(), userCreateForm.getPassword1() ,userCreateForm.getNickname());

        return "redirect:/";
    }

service

public SiteUser create(String username, String email, String password ,String nickname) {
      SiteUser user = new SiteUser();
      user.setUsername(username);
      user.setEmail(email);
      user.setPassword(passwordEncoder.encode(password));
      user.setNickname(nickname);
      user.setCreateDate(LocalDate.now());
      this.userRepository.save(user);
      return user;
  }

로그인

뉴스 리스트

리스트 좌측 상세 슬라이드

<main class="sliders-container">
    <div class="list-box">
      <div class="sideNews" th:each="firstNews, iterStat : ${paging}" th:if="${iterStat.index == 0}">
        <div id="slider">
          <a href="#" class="control_next">></a>
          <a href="#" class="control_prev"><</a>
          <ul >
            <li th:each="news, stat : ${paging}" th:if="${stat.index < 4}" class="slideLi">
              <a th:href="@{|/news/detail/${news.id}|}">
                  <div class="textConDiv">
                    <p class="NewstitleF" th:text="${news.subject}">뉴스제목</p>
                  </div>
                <img class="slideNewsImg" th:src="${news.filepaths[0]}" />
                  <div class="textConDiv2">
                    <span th:text="${news.content}">.</span>
                  </div>
              </a>
            </li>

          </ul>
        </div>
      </div>
jQuery(document).ready(function ($) {

  $('#checkbox').change(function(){
    setInterval(function () {
        moveRight();
    }, 3000);
  });

	var slideCount = $('#slider ul li').length;
	var slideWidth = $('#slider ul li').width();
	var slideHeight = $('#slider ul li').height();
	var sliderUlWidth = slideCount * slideWidth;

	$('#slider').css({ width: slideWidth, height: slideHeight });

	$('#slider ul').css({ width: sliderUlWidth, marginLeft: - slideWidth });

    $('#slider ul li:last-child').prependTo('#slider ul');

    function moveLeft() {
        $('#slider ul').animate({
            left: + slideWidth
        }, 200, function () {
            $('#slider ul li:last-child').prependTo('#slider ul');
            $('#slider ul').css('left', '');
        });
    };

    function moveRight() {
        $('#slider ul').animate({
            left: - slideWidth
        }, 200, function () {
            $('#slider ul li:first-child').appendTo('#slider ul');
            $('#slider ul').css('left', '');
        });
    };

    $('a.control_prev').click(function () {
        moveLeft();
    });

    $('a.control_next').click(function () {
        moveRight();
    });

});

뉴스 상세페이지

상세사진 선택 기능
(오픈소스 코드 커스텀)

<div class="container-fluid">
          <div class="row row-sm">
            <div class="col-md-6 _boxzoom">
              <div class="zoom-thumb">
                <ul class="piclist">
                  <li th:if="${#lists.size(news1.filepaths) > 0}"><img th:src="${news1.filepaths[0]}" alt=""></li>
                  <li th:if="${#lists.size(news1.filepaths) > 1}"><img th:src="${news1.filepaths[1]}" alt=""></li>
                  <li th:if="${#lists.size(news1.filepaths) > 2}"><img th:src="${news1.filepaths[2]}" alt=""></li>
                  <li th:if="${#lists.size(news1.filepaths) > 3}"><img th:src="${news1.filepaths[3]}" alt=""></li>
                </ul>
              </div>
              <div class="_product-images">
                <div class="picZoomer">
                  <img class="my_img" th:src="${news1.filepaths[0]}" style="width:450px ; height:450px;">
                </div>
              </div>
            </div>
;(function($){
	$.fn.picZoomer = function(options){
		var opts = $.extend({}, $.fn.picZoomer.defaults, options),
			$this = this,
			$picBD = $('<div class="picZoomer-pic-wp"></div>').css({'width':opts.picWidth+'px', 'height':opts.picHeight+'px'}).appendTo($this),
			$pic = $this.children('img').addClass('picZoomer-pic').appendTo($picBD),
			$cursor = $('<div class="picZoomer-cursor"><i class="f-is picZoomCursor-ico"></i></div>').appendTo($picBD),
			cursorSizeHalf = {w:$cursor.width()/2 ,h:$cursor.height()/2},
			$zoomWP = $('<div class="picZoomer-zoom-wp"><img src="" alt="" class="picZoomer-zoom-pic"></div>').appendTo($this),
			$zoomPic = $zoomWP.find('.picZoomer-zoom-pic'),
			picBDOffset = {x:$picBD.offset().left,y:$picBD.offset().top};


		opts.zoomWidth = opts.zoomWidth||opts.picWidth;
		opts.zoomHeight = opts.zoomHeight||opts.picHeight;
		var zoomWPSizeHalf = {w:opts.zoomWidth/2 ,h:opts.zoomHeight/2};


		$zoomPic.css({'width':opts.picWidth*opts.scale+'px', 'height':opts.picHeight*opts.scale+'px'});

		//초기화 이벤트
		$picBD.on('mouseenter',function(event){
			$zoomPic.attr('src',$pic.attr('src'))
		}).on('mouseleave',function(event){
			$cursor.hide();
			$zoomWP.hide();
		}).on('mousemove', function(event){
			var x = event.pageX-picBDOffset.x,
				y = event.pageY-picBDOffset.y;

			$cursor.css({'left':x-cursorSizeHalf.w+'px', 'top':y-cursorSizeHalf.h+'px'});
			$zoomPic.css({'left':-(x*opts.scale-zoomWPSizeHalf.w)+'px', 'top':-(y*opts.scale-zoomWPSizeHalf.h)+'px'});

		});
		return $this;

	};
	$.fn.picZoomer.defaults = {
        picHeight: 450,
		scale: 2.5,
		zoomerPosition: {top: '0', left: '380px'},

		zoomWidth: 460,
		zoomHeight: 460
	};
})(jQuery);



$(document).ready(function () {
     $('.picZoomer').picZoomer();
    $('.piclist li').on('click', function (event) {
        var $pic = $(this).find('img');
        $('.picZoomer-pic').attr('src', $pic.attr('src'));
    });

  });

뉴스작성

리뷰작성

리뷰상세

댓글 작성 팝업창 기능


$(document).ready(function () {
  $(".question-content").hide();
});
$(document).ready(function () {

var productId = [[${reviewId}]];


  $(".question-create-btn").click(function () {
    if (productId !== null) {
       var width = 700; // 새 창의 너비
       var height = 500; // 새 창의 높이
       var left = (window.screen.width - width) / 2;
       var top = (window.screen.height - height) / 2;
       var options =
      "width=" + width + ",height=" + height + ",left=" + left + ",top=" + top;

      var url = "/answer/create?productId=" + productId;

      window.open(url, "_blank", options);
    } else {
      console.error("리뷰 ID를 찾지 못했습니다.");
    }
  });

리뷰리스트

작업 후 느낀 점

3차 팀프로젝트의 영향으로 초기 기획한 기간에 작업을 하지못하여
팀프로젝트가 끝난 후 작업을 하게 되었고 , 다른 프로젝트를 진행하는 것이 또 생기게 되어
해당 프로젝트에는 시간을 많이 쓰지 못하였던 것 같습니다.
기대치만큼 나오지는 못했지만 해당 프로젝트를 진행하며 아쉬운점은 다음 프로젝트를 진행할때 참고하여 성장하도록 하겠습니다.

profile
병아리 개발자 의 우당탕탕 성장기

0개의 댓글