관람평 등록 시 실제 예매 여부, 작성 리뷰 여부를 체크해서 exception을 발생시키도록 처리하는 코드를 다시 복습해 보려고 한다. 로직 플로우는 아래와 같다.

Untitled Diagram drawio (1)

첫번째로, 해당 고객의 예매 여부를 확인하는 쿼리문 및 서비스 로직이다. erd는 아래와 같다. 리뷰를 작성하고자 하는 영화의 번호와 고객번호가 필요하기 때문에 티켓 테이블과 상영시간표 테이블을 조인하였다.

image

sql

같은 영화를 여러 번 예매했을 수도 있기 때문에 list로 받아왔다.

    <!-- List<String> getQualification(@Param("customerNo") int customerNo, @Param("movieNo") int movieNo); -->
	<select id="getQualification" resultType="string">
		select 
   			t.ticket_cancelled ticketCancelled
		from 
		    show_schedule s, ticket t
		where 
		    s.show_schedule_no = t.show_schedule_no
			and t.customer_no = #{customerNo}
			and s.movie_no = #{movieNo}
			and t.ticket_cancelled = 'N'
	</select>

Service

서비스 로직은 isReserved, isWrited 두 가지로 구현하였다. isReserved는 반환하는 리스트가 비어있지 않으면 true를, isWrited는 작성된 관람평이 없다면 true를 반환하는 메소드이다.

	/**
	 * 사용자 번호로 해당 영화에 대한 예매 여부를 확인한다.
	 * @param customerNo
	 * @param movieNo
	 * @return
	 */
	public boolean isReserved(int customerNo, int movieNo) {
		List<String> isReserved = reviewMapper.getQualification(customerNo, movieNo);
		return !isReserved.isEmpty();
	}
	
	/**
	 * 사용자 번호와 영화 번호로 해당 영화에 작성한 리뷰가 있는지 확인한다.
	 * @param customerNo
	 * @param movieNo
	 * @return
	 */
	public boolean isWrited(int customerNo, int movieNo) {
		Review review = reviewMapper.getMyReviewByMovieNo(customerNo, movieNo);
		return review == null;
	}

Controller

다음의 코드는 exception 핸들러이다. 컨트롤러 측에서 ReviewErrorException 예외를 발생시킬 때 바로 아래의 exception 핸들러 코드가 실행된다. 예매를 하지 않았거나, 이미 작성된 관람평이 있다면 exception을 발생시킨다.

예매를 하지 않고 관람평 쓰기 버튼을 클릭했을 때 받아오는 응답은 다음 사진과 같다. status는 false, error에는 exception 시 띄울 메세지, items는 null 값이 전달된다. 반대로 예매를 이미 했을 경우에는 items에 message: true가 반환이 되게끔 구현하였다.

image

    @ExceptionHandler(ReviewErrorException.class)
	public @ResponseBody ResponseDto<?> ReviewExceptionHandler(ReviewErrorException e) {
		ResponseDto<?> response = new ResponseDto<>();
		
		response.setStatus(false);
		response.setError(e.getMessage());
		
		return response;
	}

다음은 /rest/check url 요청이 왔을 때 실행되는 컨트롤러 코드이다.

    @RestController
    @RequestMapping("/rest")
    public class ReviewRestController {
        @GetMapping("/check")
        public ResponseDto<Map<String, Object>> checkQualification(@LoginedUser Customer customer, int movieNo) {
            
            ResponseDto<Map<String, Object>> response = new ResponseDto<>();
            HashMap<String, Object> items = new HashMap<>();
            
            boolean isReserved = reviewService.isReserved(customer.getNo(), movieNo);
            boolean isWrited = reviewService.isWrited(customer.getNo(), movieNo);
            
            if (isReserved) {
                items.put("message", true);
            } else {
                throw new ReviewErrorException("관람평은 실관람 이후 작성 가능합니다.");
            }
            
            if (isWrited) {
                items.put("message", true);
            } else {
                throw new ReviewErrorException("이미 작성한 관람평이 있습니다.");
            }
            
            response.setStatus(true);
            response.setItems(items);
            
            return response;
        }
    }

JSP

jsp에서는 아래와 같이 작업하였는데, 관람평 쓰기 버튼을 클릭했을 때 다음과 같은 ajax 요청이 오도록 구현을 했고 응답으로 받은 items가 존재한다면 관람평 모달을 띄우고, items이 존재하지 않다면 exception 시의 메세지를 오류 모달에 띄우도록 하였다.

<script>
    $(".review-btn").click(function() {
        $.ajax({
            type: "get",
            url: "/rest/check",
            data: {movieNo: movieId},
            dataType: 'json',
            async: false,
            success: function (response) {
                if (response.items) {
                    reviewModal.show();
                } else {
                    showError(response.error);
                    return; 
                }
            }
        })
    })
</script>

사실 위의 코드도 여러 번 수정한 코드이고, 구현은 정상적으로 되었지만 조금 더 좋은 방식으로 코드를 작성할 수 없었나 싶은 고민을 하고 있다 🤔🤔