본문 바로가기
프로젝트/자리 있어요?

[Refactoring] 22.08.26. 마이 페이지 (리뷰 작성, 수정, 삭제, 확인)

by 규글 2022. 8. 26.

마이 페이지 (리뷰 작성, 리뷰 보기)

구성

1. 주문 내역 확인

  • 해당 계정으로 주문한 내역을 확인할 수 있다.
  • 주문한 내역을 취소할 수 있다.

2. 리뷰 작성

  • 해당 주문에 대한 리뷰를 작성할 수 있다.
  • 이미지와 글을 동시에 작성할 수 있다.
  • 리뷰를 작성했다면, 해당 리뷰에서 사용자가 준 별점을 확인할 수 있다.
  • 리뷰를 작성했다면, 해당 버튼은 수정 버튼으로 바뀐다.

3. 리뷰 확인

  • 해당 매장에 대한 리뷰 정보를 한 번에 확인할 수 있다.

 

코드 뜯어보기

MenuController.java

  • addReview - addReview

 작성한 리뷰 정보 저장 요청 처리를 위한 controller의 method이다. Http 의존성을 제거하는 방향으로 logic을 살펴보려고 한다.

 

 우선 HttpServletRequest 객체를 사용하는 부분을 service 바깥으로 옮긴 후, 해당 내용을 전달받는 방식으로 바꾸었다. 필요한 내용은 이미지를 저장할 경로와 접속한 email 정보이다. 또한 넘겨받은 ReviewDto에는 이미 imageFile, storeNum, orderNum, content에 대한 정보가 담겨있으므로 이것의 storeNum data로 DB에서 매장 정보를 가져올 것이다. 그런데 logic에서 보이는 매장의 이름이 굳이 리뷰 table에 있을 필요가 없다. 이는 메뉴 table에 매장의 DB number와 이름이 모두 존재해야할 필요가 없는 것과 동일한 이유이다. 때문에 ReviewDto에 매장 이름 정보를 넘기지 않도록 하며, 리뷰 table에서도 storeName column을 지워주었다.[각주:1]

 

 마지막으로 생각할 것은 평균 별점에 대한 내용이다. 기존에 매장 table에는 평균 별점과 총 리뷰의 개수에 대한 column이 없었고, 리뷰 table에서 해당 매장 번호로 별점을 계산하는 방식을 취하고 있었다. 때문에 리뷰를 추가, 수정, 삭제의 과정에 평균 별점을 계산해서 가져오는 logic이 동일하게 존재한다. 더해서 메인 페이지로의 요청에도 사용되고 있다.

 동일한 과정이 존재하던 리뷰의 List를 가져오는 logic과 마이 페이지에서 주문 정보를 가져오는 logic에서는 매장에 평균 별점 column을 만들어주면서 이미 수정해주었다.[각주:2] [각주:3]평균 별점을 계산해서 return 하는 이유는 즉각적으로 화면에서 별점이 적용되도록 바꾸기 위함이었다. 더해서 따로 매장 table에 각 매장의 평균 별점의 수에 대한 column을 만들지 않았던 이유는, 굳이 만들어 값을 넣어주지 않아도 리뷰 table의 값들을 통해 계산할 수 있기 때문이었다.

 

 하지만 지금처럼 새로 column을 만든 상태라면 별점의 평균값을 계산하는 query문 대신, 매장 table에서 평균 별점을 수정하고 리뷰의 수를 수정하는 query문을 사용하게 된다. 물론 수정하는 query문은 추가했다. 당장 보면 기존의 logic보다도 상당히 더 길어졌다. 기존의 별점 평균을 계산하는 dao method를 그대로 사용한다면 DB에 요청하는 query문은 하나가 추가되는 것이다.

 

 그렇다면 다른 table의 정보를 이용해서 원하는 값을 얻어낼 수 있다면 굳이 column으로 만들어 저장하지 않아도 될까? 지금처럼 매장 table에 avgStar와 reviewCount column을 만들어 주문 정보를 가져올 때 DB에 data를 조회하는 횟수를 하나 줄였지만, 그로 인해 리뷰를 추가, 제거하는 과정에서 조회하는 횟수가 늘었다. 사실상 하나가 늘어난 셈이지 않을까?

 

 그렇지 않을 것이다. 잘 보면 해당 logic에서의 평균 별점을 얻기 위해 조회하는 횟수는 주문 수가 많아질수록 늘어났을 것이다. 예를 들어 리뷰를 추가할 때 DB를 조회하는 횟수가 하나 늘어난 것이지만, 해당 계정으로의 주문 건수가 20개가 넘어간다고 하면 마이 페이지로의 이동 시 20번의 조회 횟수가 줄어든 것이라고 할 수 있다. 새롭게 column을 추가하여 DB를 조회하는 부하(?) 를 줄였다고 생각하면 column을 새로 만든 지금 상태가 더 좋은 것이지 않을까? 좀 더 고민해봐야 할 내용인 것 같다.

 

  • getReviewList - getReviewList

 마이 페이지에서 주문한 매장의 전체 리뷰 목록을 불러오는 요청을 처리하는 controller의 method이다. Ajax 요청을 받아 return 하도록 한 이유는 주문 목록이 많아지고(물론 페이징 처리가 될 테지만), 각 매장에 대한 리뷰도 많아지면 한 번에 로딩했을 때 어려울 것이라고 생각했기 때문이다. 심지어 리뷰의 List를 화면에 띄울 때 modal을 사용했다. 따라서 해당 modal들을 주문한 개수만큼 만들면 안된다고 생각해서 하나의 modal을 활용하는 logic을 나름대로 고안한 것이었다.

 덕분에 다음 이미지와 같이 이 프로젝트에서 가장 마음에 들지 않는 부분이 탄생하게 되었다. 지금도 보면 끔찍하다.

 

 리뷰를 주문 정보 card를 클릭하면 보이는 modal 안에서 보도록 하는 것이 목표였다. 그런데 이 내용들을 페이지 로딩 시 출력하는 것이 아니라 클릭했을 때 구조를 만들려고 하니 방법이 떠오르지 않았다. 당시에도 노력했던 부분이지만 해당 부분을 외부에 다른 file로 html 구조를 만들어서 ajax로 받은 data와 연동해서 출력하는 방식이 존재한다면, 그런 방식을 채택하려고 했다. 시간에 쫓겨 신경쓰지 못한 부분이 고스란히 드러난 부분이라 가장 마음에 들지 않는 부분이다. 심지어 필자가 작성한 부분이라 마음에 들지 않는 것은 두 배..

 

 프론트보다 백에 집중하기로 했으니 일단 진행해본다. 이 부분은 매장 상세 페이지로의 요청 처리를 하는 StoreController의 goStoreDetail method에서 수행되는 logic으로, 당시 평균 별점에 대한 내용을 수정하면서 현재의 모습이 되었다. StoreController에서는 list를 ModelAndView에 넣어 보내고 있었는데, ReviewController의 이곳에서는 Map으로 data를 넣어 보내고 있다. 따라서 Map으로 Service logic을 return 하되, StoreController에서는 List가 아닌 Map으로 받게 되는 것이므로, 그 key를 받도록 수정해주었다.

 

  • getReviewData - getReviewData

 리뷰를 수정하는 modal 창에, 수정하고자 하는 리뷰의 내용을 가져오는 요청을 처리하는 controller의 method이다. 리뷰 List를 가져오는 요청과 마찬가지로, 하나의 modal을 가지고 data를 바꾸기 위한 요청이다. Map 객체를 service 안쪽에서 다루려고 한다.

 

 Map 객체를 service 안에서 다루도록 변경했다. 그것이 전부이다. Dao method의 query문에는 원 댓글인지 댓글의 댓글인지를 구분하는 targetNum column에 대한 data를 요구하게끔 작성되어 있지만, 전달받는 ReviewDto 객체에는 주문 번호인 orderNum 값만 담겨 있고, targetNum data는 존재하지 않아 자연스럽게 0을 넣게 되어있다.

 

  • updateReview - updateReview

 리뷰를 수정 요청에 대한 처리를 하는 controller의 method이다. 이 항목은 마이 페이지에서 유저가 수정할 때와, 매장 리뷰 관리 페이지에서 매장 관리자가 수정할 때 요청된다. 리뷰 table에는 주문 번호와 작성자의 정보가 함께 있으므로, 그 두 가지를 이용해서 구분할 수 있다. Http 의존성을 없애면서 Map 객체를 service 안쪽에서 다루도록 수정할 것이다.

 

 왼쪽 이미지는 기존 logic이고, 오른쪽 이미지는 DB의 내용을 수정하기 위한 바꾼 logic이다. 리뷰를 추가하는 logic에서처럼 DB를 조회하지 않고 평균 별점을 수정하려니 오히려 두 번의 DB 조회를 추가적으로 하게 되었다. 평균 별점을 직접 계산하지 않고, 기존의 dao method를 사용해서 조회했다고 하더라도 별점을 update 하기 위해 기존의 리뷰 수도 알아야하므로 매장 정보를 조회하는 것은 동일하다.

 

 그렇다면 매장 정보를 조회하지 않고도, 작성한 리뷰 정보를 조회하지 않고도 평균 별점을 계산해 DB에 update하는 것이 가능할까? 원래 사용했던 평균 별점 계산하는 dao method를 지우지 않은 상태이므로 이를 이용한다면 평균 별점을 새롭게 얻는 것은 문제가 아니다. 그렇다면 나머지 매장의 평균 별점과 리뷰 수를 update하는 것은 StoreMapper에서 reviewCount에 값이 어떻게 들어있는지 여부로 query 문의 경우의 수를 나누면 될 것 같다.

 

 수정 시에는 StoreDto의 reviewCount에 강제로 -7을 넣고, Mapper에서 경우의 수를 나눴다. 굳이 -7인 것에 이유는 없다. 처음 수정했을 때보다는 DB 조회를 한 번 덜하게 되었다. 이러면 리뷰를 추가하고 수정하는 logic에서 평균 별점에 대한 내용이 일관성이 없어지는 것 같다.

 

  • deleteReview - deleteReview

 마이 페이지에서 작성한 리뷰를 삭제하는 요청에 대한 controller의 method이다. Map을 service 안쪽에서 다뤄줄 것이다.

 

 Service logic이 조금 많이 길어졌다. 평점을 출력하는 과정과 Map 객체를 다루는 것이 아니더라도 StoreDto에 setting 해서 평균 별점과 리뷰의 수를 수정하는 과정이 더해져서 조금 많이 길어졌다.

 그리고 약간의 고민이 더해졌다. 사용하는 Dto는 ReviewDto와 OrderDto로, 둘 다 주문 번호인 orderNum을 필요로 하는 query문을 위한 것이다. 어차피 동일하게 필요하다면, 처음부터 mapping되도록 객체를 전달받으면 좀 더 좋은 것이라는 생각이 들어서 시도해 본 수정이다. 그럼 굳이 따로 setting해줄 필요도 없어진다.

 

 추가적인 고민은 StoreMapper의 query문에 대한 것이다. 무엇이 바뀌었는가 하면, DB에 그대로 reviewCount의 값을 넣고 빼지 않기로 했다. 리뷰가 추가된다면 기존보다 1이 더해질 것이고, 수정한다면 0이, 삭제한다면 -1이 더해지는 셈이다. 이런 방식으로 한다면 굳이 query문으로 기존 data를 받아올 필요도 없어진다.

 

  • (번외) Mapper 변경으로 인한 리뷰 추가, 수정 method 수정

 리뷰 수정 logic에서 바꿔준 내용이다.

 

 리뷰 추가 logic에서 바꿔준 내용이다. 이렇게 된다면 굳이 새로 별점의 평균값을 계산하는 과정이 리뷰를 추가하는 부분에만 있는 것이 이상해지는 것이 맞는 것 같다.

 

 

 여기까지 밑줄 친 내용을 제외한 ReviewService의 모든 내용을 살펴보았다. 하지만 지금 과정에서는 아주 큰 문제가 있다. 바로 리뷰를 추가하고 수정하는 과정에서 매장 관리자가 추가하는 리뷰에 대한 답글을 추가하는 경우를 고려하지 않았다. 단적으로 말하자면, 매장 관리자가 답글을 추가해도 매장의 리뷰 수가 증가하는 상황이다.

 따라서 밑줄 친 내용이 매장 리뷰 관리 페이지에서의 요청 처리를 위한 service logic이므로, 이들을 확인하면서 동시에 매장 관리자가 리뷰에 대한 댓글을 작성했을 때까지 고려해서 service logic을 한 번 더 고쳐볼 것이다.

댓글