본문 바로가기

Server Programming/Spring Boot Backend Programming

[Spring 부트 - 영화 리뷰 프로젝트] 6. Ajax로 영화 리뷰 처리 (2) 리뷰 등록 / 수정 /삭제

반응형

진행 순서

  1. 리뷰 등록하기
  2. 리뷰 리스트 보여주기
  3. 특정 리뷰 선택하기
  4. 리뷰 수정과 삭제
  5. 원본 이미지 보기
  6. (+) 영화 삭제, 수정, 검색

1. 리뷰 등록하기

: 화면에 버튼 추가 후, 버튼 클릭시 reviewModal을 통해 등록 수행

 

(1) <div> class 속성값이 uploadResult 밑에 버튼을 추가

(2) 리뷰 숫자 보여주는 부분 아래 쪽에 Review Register 글자를 가지는 버튼 추가

(3) 영화 리뷰를 보여주는 공간인 reviewList 클래스로 div 생성

<!-- 리뷰 등록을 위한 review count, review register 버튼 추가-->
        <button type="button" class="btn btn-primary">
            Review Count <span class="badge badge-light">[[${dto.reviewCnt}]]</span>
        </button>

        <button type="button" class="btn btn-info addReviewBtn">
            Review Register
        </button>
<!-- 영화 리뷰를 보여주는 공간으로 활용-->
        <div class="list-group reviewList">

        </div>

 

(4) addReviewBtn을 이용해 reviewModal창 처리

1. 입력 부분을 통해 추가할 기능이 필요하므로 변수로 외부로 빼서 선언

2. 모달창 내부에 필요없는 버튼 보이지 않게 처리한 후에 띄우게 설정

 //addReviewBtn 클릭시 reviewModal창 보여주도록
                $(".reviewModal").modal("show"); //미리 보기용
//1. 입력 부분을 통해 추가할 기능이 필요하므로 변수로 외부로 빼서 선언
                var reviewModal = $(".reviewModal");
                var inputMid = $('input[name="mid"]');
                var inputText = $('input[name="text"]');


                $(".addReviewBtn").click(function () {
                    inputMid.val("");
                    inputText.val("");
//2. 모달창 내부에 필요없는 버튼 보이지 않게 처리한 후에 띄우게 설정
                    $(".removeBtn ,  .modifyBtn").hide();
                    $(".reviewSaveBtn").show();

                    reviewModal.modal('show');
                });

 

(5) Save changes 클릭시 영화 리뷰 등록 처리하도록 이벤트 처리

: 버튼 클래스 속성값으로 reviewSaveBtn 지정

// (5) Save changes 클릭시 영화 리뷰 등록 처리하도록 이벤트 처리
                // : 버튼 클래스 속성값으로 reviewSaveBtn 지정
                $('.reviewSaveBtn').click(function() {

                    //버튼 클릭시 회원의 데이터를 JSON 데이터로 만들어 전송
                    var data = {mno:mno, grade:grade, text:inputText.val(), mid: inputMid.val() };

                    console.log(data);

                    $.ajax({
                        url:'/reviews/'+mno,
                        type:"POST",
                        data:JSON.stringify(data),
                        contentType:"application/json; charset=utf-8",
                        dataType:"text",
                        //전송 성공시 reload를 통해 URL 다시 호출
                        success: function(result){

                            console.log("result: " + result);

                            self.location.reload();

                        }
                    })
                    //이후 모달창 숨기기
                    reviewModal.modal('hide');

                });

2. 리뷰 리스트 보여주기

(1) 새로고침시 평균 평점과 리뷰 개수는 가져온다.

(2) 실제 리뷰 내용을 가져오지 않는 문제

-> Ajax를 이용해 MovieReviewDTO 리스트를 JSON으로 받는데, 함수를 작성해 페이지 열릴 때마다 호출하도록 처리

 

  //리뷰 리스트 보여주는 함수 작성
//(1) 새로고침시 평균 평점과 리뷰 개수는 가져온다.
//(2) 실제 리뷰 내용을 가져오지 않는 문제
//-> 페이지가 열리면 바로 리뷰 데이터들을 가져와서 사용한다.
function getMovieReviews() {

    function formatTime(str){
        var date = new Date(str);

        return date.getFullYear() + '/' +
            (date.getMonth() + 1) + '/' +
            date.getDate() + ' ' +
            date.getHours() + ':' +
            date.getMinutes();
    }

    $.getJSON("/reviews/"+ mno +"/all", function(arr){
        var str ="";

        $.each(arr, function(idx, review){

            console.log(review);

            str += '    <div class="card-body" data-reviewnum='+review.reviewnum+' data-mid='+review.mid+'>';
            str += '    <h5 class="card-title">'+review.text+' <span>'+ review.grade+'</span></h5>';
            str += '    <h6 class="card-subtitle mb-2 text-muted">'+review.nickname+'</h6>';
            str += '    <p class="card-text">'+ formatTime(review.regDate) +'</p>';
            str += '    </div>';
        });

        $(".reviewList").html(str);
    });
}

//페이지 열리면 jQuery의 getJSON()을 이용해 MovieReviewController호출해 reviewList 클래스 속성 지정된 <div> 내용을 채운다.
//따라서 영화와 함께 리뷰도 같이 출력
getMovieReviews();

3. 특정 리뷰 선택하기

: 리뷰 목록에서 특정 리뷰 선택시 수정 삭제 가능하도록 reviewModal 창 보이게 처리

-> 모달창 내부 버튼을 상황에 맞게 노출 / 비노출 처리

  //특정 리뷰 선택하기
    //-> 수정 삭제 가능하도록 모달창 띄우기

    //외부에 선언해 나중에 수정이나 삭제 작업에 사용할 수 있도록
    var reviewnum;

    $(".reviewList").on("click", ".card-body", function() {

        //상황에 따라 버튼 노출 처리
        $(".reviewSaveBtn").hide();
        $(".removeBtn , .modifyBtn").show();


        //리뷰 선택시 해당 리뷰의 정보를 가져온다.
        var targetReview = $(this);

        reviewnum = targetReview.data("reviewnum");
        console.log("reviewnum: "+ reviewnum);
        inputMid.val(targetReview.data("mid"));
        inputText.val(targetReview.find('.card-title').clone().children().remove().end().text());

        var grade = targetReview.find('.card-title span').html();
        $(".starrr a:nth-child("+grade+")").trigger('click');

        //수정 작업을 할 수 있도록 모달창을 띄운다
        $('.reviewModal').modal('show');
    });

4. 영화 리뷰 수정과 삭제

: Ajax를 통한 PUT방식 / DELETE 방식으로 동작하는데

처리 후에 현재 페이지를 재호출해 서버로부터 변경된 데이터를 받도록 처리

 //4. 영화 리뷰 수정과 삭제
// : Ajax를 통한 PUT방식 / DELETE 방식으로 동작하는데
// 처리 후에 현재 페이지를 재호출해 서버로부터 변경된 데이터를 받도록 처리
$(".modifyBtn").on("click", function(){

    var data = {reviewnum: reviewnum, mno:mno, grade:grade, text:inputText.val(), mid: inputMid.val() };

    console.log(data);

    $.ajax({
        url:'/reviews/'+mno +"/"+ reviewnum ,
        type:"PUT",
        data:JSON.stringify(data),
        contentType:"application/json; charset=utf-8",
        dataType:"text",
        success: function(result){

            console.log("result: " + result);

            self.location.reload();

        }
    })
    reviewModal.modal('hide');
});

$(".removeBtn").on("click", function(){

    var data = {reviewnum: reviewnum};

    console.log(data);

    $.ajax({
        url:'/reviews/'+mno +"/"+ reviewnum ,
        type:"DELETE",
        contentType:"application/json; charset=utf-8",
        dataType:"text",
        success: function(result){

            console.log("result: " + result);

            self.location.reload();

        }
    })
    reviewModal.modal('hide');
});

5. 원본 이미지 보기

: 섬네일 클릭시 원본 파일 볼 수 있는 방법 제공

 

섬네일 클릭시 원본 파일 볼 수 있는 방식

  1. UploadController에 지정된 이미지 조회 기능 활용
  2. 섬네일 파일 호출시 이름규칙을 이용해 특정 파라미터값 존재시 's_'없는 원본 파일 전달

2번 방식으로 구현

 

변경 전의 UploadController의 getFile()

    //URL로 이미지 전송을 위한 메서드
    @GetMapping("/display")
    public ResponseEntity<byte[]> getFile(String fileName){
        ResponseEntity<byte[]> result = null;

        try{
            String srcFileName = URLDecoder.decode(fileName, "UTF-8");

            log.info("fileName : "+ srcFileName);
            File file = new File(uploadPath+File.separator+srcFileName);

     
            log.info("file : "+ file);

            HttpHeaders header = new HttpHeaders();

            //MIME타입 처리
            //: 마임 타입이란 클라이언트에게 전송된 문서의 다양성을 알려주기 위한 메커니즘 -> 올바른 마임타입 전송하도록 설정필요
            header.add("Content-Type", Files.probeContentType(file.toPath()));
            //: 확장자를 통해 마임타입을 판단하는데, 확장자가 없으면 null을 반환한다.

            //파일 데이터 처리
            result = new ResponseEntity<>(FileCopyUtils.copyToByteArray(file), header, HttpStatus.OK);
            //ResponseEntity에 바이트 배열로 만든 파일, 헤더, 상태코드 전달
        }
        catch (Exception e){
            log.error(e.getMessage());
            return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
        }
        return result;
    }

 

 

변경 후의 UploadController의 getFile()

    //URL로 이미지 전송을 위한 메서드
    //-> 섬네일 클릭시 원본 이미지 반환을 위한 size 인자 추가
    @GetMapping("/display")
    public ResponseEntity<byte[]> getFile(String fileName , String size){
        ResponseEntity<byte[]> result = null;

        try{
            String srcFileName = URLDecoder.decode(fileName, "UTF-8");

            log.info("fileName : "+ srcFileName);
            File file = new File(uploadPath+File.separator+srcFileName);

            //섬네일 클릭시 원본 이미지 출력 위한 추가
            if(size !=null && size.equals("1")){
                file = new File(file.getParent(), file.getName().substring(2));
            }
            
            log.info("file : "+ file);

            HttpHeaders header = new HttpHeaders();

            //MIME타입 처리
            //: 마임 타입이란 클라이언트에게 전송된 문서의 다양성을 알려주기 위한 메커니즘 -> 올바른 마임타입 전송하도록 설정필요
            header.add("Content-Type", Files.probeContentType(file.toPath()));
            //: 확장자를 통해 마임타입을 판단하는데, 확장자가 없으면 null을 반환한다.

            //파일 데이터 처리
            result = new ResponseEntity<>(FileCopyUtils.copyToByteArray(file), header, HttpStatus.OK);
            //ResponseEntity에 바이트 배열로 만든 파일, 헤더, 상태코드 전달
        }
        catch (Exception e){
            log.error(e.getMessage());
            return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
        }
        return result;
    }

: File에 원본이미지 이름이 담는다.

 

Read.html

<!-- 섬네일 파일 클릭시 원본 이미지 반환을 위해, 섬네일 이름을 알 수 있도록 data-file 속성 추가-->
        <div class="uploadResult">
            <ul >
                <li th:each="movieImage: ${dto.imageDTOList}" th:data-file="${movieImage.getThumbnailURL()}">
                    <img  th:if="${movieImage.path != null}" th:src="|/display?fileName=${movieImage.getThumbnailURL()}|">
                </li>
            </ul>
        </div>

 

(1) th:data-file의 섬네일 이름을 가져와 ImageModal창에 이미지 태그로 추가

(2) size 파라미터 값을 이용해 원본 이미지를 가져오도록

  //섬네일 클릭시 원본 이미지 출력을 위해, data의 file속성에 추가한 섬네일 파일 이름을 가져와서 원본 이미지 출력
$(".uploadResult li").click(function() {

    var file = $(this).data('file');

    console.log(file);

    $('.imageModal .modal-body').html("<img style='width:100%' src='/display?fileName="+file+"&size=1' >")

    $(".imageModal").modal("show");

});

 

(1)  size 파라미터를 이용해 원본 파일인지 섬네일인지 구분

(2) size 변수 값이 1일 경우 원본 파일 전송

(3) read.html 에서 섬네일 부분의 <li> 태그에 data-file 속성으로 섬네일 이름 알 수 있도록 처리


6. 영화의 수정, 삭제, 검색 구현

-> 이미지 데이터를 업데이트 하는 게 아니라 새로 구성하도록 설정

 

반응형