프로젝트

영화 예매 사이트 (MoVieCinema) - 댓글 조회 및 페이징 (AJAX)

sejin2 2024. 4. 13. 20:03

ReviewController

@GetMapping("/reviewList")
@ResponseBody
public ResponseEntity<Page<Review>> getReviewsByMovieId
              (@RequestParam("movieNo") Long movieId,
               @PageableDefault(page=0, size=4, sort="reviewId", direction= Sort.Direction.DESC) Pageable pageable,
               Model model) {

  Page<Review> reviewList = reviewService.selectReviewListByMovieNo(movieId, pageable);

  int nowPage = reviewList.getPageable().getPageNumber();
  int pageGroupSize = 7;
  int totalPageGroups = (int)Math.ceil((double)reviewList.getTotalPages() / pageGroupSize);
  int currentPageGroup = (nowPage-1) / pageGroupSize;
  int startPage = currentPageGroup * pageGroupSize + 1;
  int endPage = Math.min(startPage + pageGroupSize -1, reviewList.getTotalPages());


  model.addAttribute("nowPage", nowPage);
  model.addAttribute("startPage", startPage);
  model.addAttribute("endPage", endPage);

  return ResponseEntity.ok(reviewList);

페이징 설정 부분을 자세히 살펴보면 아래와 같다.

// 현재 페이지 계산
int nowPage = reviewList.getPageable().getPageNumber();
// 한 페이지 그룹에서 보여줄 페이지 수
int pageGroupSize = 7;
// 전체 페이지 그룹 수
int totalPageGroups = (int)Math.ceil((double)reviewList.getTotalPages() / pageGroupSize);
// 현재 페이지가 속한 페이지 그룹
int currentPageGroup = (nowPage-1) / pageGroupSize;
// 현재 페이지
int startPage = currentPageGroup * pageGroupSize + 1;
// 현재 페이지 그룹의 마지막 페이지
int endPage = Math.min(startPage + pageGroupSize -1, reviewList.getTotalPages());

 

ReviewService

public Page<Review> selectReviewListByMovieNo(Long movieId, Pageable pageable) {
    return reviewRepository.findAllByMovieId(movieId, pageable);
}

ReviewRepository

@Query("SELECT r FROM Review r WHERE r.movieNo = :movieId ORDER BY r.createReviewDate DESC")
Page<Review> findAllByMovieId(@Param("movieId") Long movieId, Pageable pageable);

영화 상세보기 페이지에서 해당 영화에 대한 리뷰를 조회하는 것이므로 원래는 영화 상세페이지인 detail.jsp에서 리뷰 기능을 구현했다. 그러나 댓글 작성 후 댓글 리스트 조회시에 댓글 리스트 부분만 리로드되는 것이 아니라 페이지 전체가 리로드 되는 문제점을 발견했다. 영화 정보에 대한 부분은 굳이 리로드하지 않아도 되는 데이터이므로 리뷰 부분을 따로 분리하여 AJAX 처리를 하였고, 댓글에 대한 페이징 처리를 하였다.

따라서 detail.jsp 하단에 리뷰 부분을 따로 분리한 jsp를 연결해주고 review.jsp에서 영화 리뷰 관련 작업을 해준다.

<jsp:include page="movie/reviews.jsp"/>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>

<div class="review">
    <table class="reviewList">
        <thead>
            <tr id="reviewTitle">
                <th>영화후기</th>
                <th><textarea cols="120" rows="4" id="reviewContent"> </textarea></th>
                <th>
                    <button onclick="insertReview();">평점 및 리뷰작성</button>
                </th>
            </tr>
        </thead>
        <tbody>
            <c:forEach var="review" items="${reviewList.content}">
                <tr id="reviewItem">
                    <td>${review.reviewWriter}</td>
                    <td>${review.reviewContent}</td>
                    <td>${fn:substringBefore(review.createReviewDate.toString(), 'T')}</td>
                </tr>
            </c:forEach>
        </tbody>
    </table>
    <div class="pagination"></div>
</div>
</section>

<script>
   var movieId = ${movie.movieId};

  $(document).ready(function() {
         updateReviewList(movieId);
     });

 function insertReview() {
     $.ajax({
         url: "reviewInsert",
         data: {
             movieNo: movieId,
             reviewContent: $("#reviewContent").val()
         },
         type: "post",
         success: function (result) {
             updateReviewList(movieId);
             $("#reviewContent").val("");
         },
         error: function () {
             console.log("리뷰 등록 ajax통신 실패");
         }
     });
 }

function updateReviewList(movieId, page) {
    $.ajax({
        url: "reviewList",
        data: { movieNo: movieId,
                page: page
              },
        type: "get",
        dataType: "json",
        success: function (response) {
            var reviewsHtml = '';
            if (response.content && Array.isArray(response.content)) {
                response.content.forEach(function(review) {
                    reviewsHtml += '<tr><td>'
                                + review.reviewWriter + '</td><td>'
                                + review.reviewContent + '</td><td>'
                                + review.createReviewDate.substring(0, 10) + '</td></tr>';
                });

                var paginationHtml = '';
                if (response.totalPages > 1) {
                    if (response.number > 0) {
                        paginationHtml += '<a href="javascript:void(0);" onclick="updateReviewList(' + movieId + ',' + (response.number - 1) + ');">이전</a>';
                    }

                    for (var pageNum = 0; pageNum < response.totalPages; pageNum++) {
                        paginationHtml += '<a href="javascript:void(0);" onclick="updateReviewList(' + movieId + ',' + pageNum + ');" ' + (pageNum === response.number ? 'class="active"' : '') + '>' + (pageNum + 1) + '</a>';
                    }

                    if (response.number + 1 < response.totalPages) { // 현재 페이지가 총 페이지 수보다 작으면 "다음" 링크 생성
                        paginationHtml += '<a href="javascript:void(0);" onclick="updateReviewList(' + movieId + ',' + (response.number + 1) + ');">다음</a>';
                    }
                }
            }
             $('.pagination').html(paginationHtml);
            $('.reviewList tbody').html(reviewsHtml); // 후기 목록 업데이트
        },
        error: function() {
            console.log("리뷰 목록 불러오기 실패");
        }
    });
}
 </script>

<jsp:include page="../layouts/footer.jsp"/>