[Spring][쇼핑몰 프로젝트][50] 상품 테이블 평균 평점 반영
프로젝트 Github : https://github.com/sjinjin7/Blog_Project
프로젝트 포스팅 색인(index) : https://kimvampa.tistory.com/188
목표
상품 테이블 평점 적용
vam_book테이블에 ratingAvg컬럼을 추가하고 댓글 '등록', '수정', '삭제'할 때 해당 상품의 평균 평점 값이 최신화되도록 기능을 구현할 것입니다.
순서
1. 개요
2. 컬럼추가
3. DTO
4. Mapper
5. Service
6. 테스트
7. 보완
1. 개요
vam_book 테이블에 ratingAvg컬럼을 추가해 줄 것입니다. 이 컬럼의 목적은 해당 상품 평점의 평균 값이 저장되도록 하는 것이 목적입니다. 이번 포스팅에선 이 컬럼의 값이 댓글 등록, 수정, 삭제될 때마다 평점의 평균값을 구해서 값이 최신화 되도록 하는 것이 목표입니다.
구현을 위해서 Service 단계에서 댓글의 등록, 수정, 삭제 될때마다 상품의 평점 평균을 구하고 그 값을 해당 상품 테이블(vam_book)의 ratingAvg컬럼 값으로 최신화하도록 만들 것입니다. 먼저 평점의 평균을 구하는 Mapper 메서드, 구한 평균값을 vam_book 테이블에 반영하는 Mapper 메서드를 만들겠습니다.
※ 등록, 수정, 삭제 순간마다 평균 평점을 구하고, DB에 반영 하는 것은 매우 많은 요청이 있을 시 서버의 자원을 많이 잡아먹는 요인이 될 수 있다고 생각됩니다. 따라서 배치 프로그램을 사용해서 특정 시간마다 평점을 최신화하는 코드가 동작하도록 하는 방법도 있을 거 같습니다.
2. 컬럼추가
DB에서 아래의 쿼리 명령어를 실행해서 rating 컬럼을 추가합니다.
Oracle
alter table vam_book add ratingAvg number(2,1);
MySQL
alter table vam_book add ratingAvg decimal(2,1);
3. DTO
평점 평균값을 vam_book 테이블의 ratingAvg에 반영할때 실행될 쿼리에는 '반영 할 평점 평균 값'과 어떠한 상품 평균 값을 구할지에 대한 조건인 '상품 번호(bookId)'가 필요로 합니다. 따라서 평균값 반영 쿼리를 실행 할 Mapper 메서드에 두개의 데이터를 한번에 전달 할 수 있도록 DTO클래스를 새로 작성 해주고자 합니다.
com.vam.model 패키지에 UpdateReplyDTO 클래스를 생성합니다.
상품의 평균값을 저장 할 수 있는 double 타입의 변수와 상품번호 값을 저장 할 수 있는 int타입의 변수를 선언합니다.
private int bookId;
private double ratingAvg;
선언 한 변수에 대한 getter/setter/toString 메서드를 작성해줍니다.
4. Mapper 메서드
ReplyMapper 인터페이스
com.vam.mapper 패키지의 Mapper 메서드에 특정 상품의 평점 평균 값을 구하는 메서드와 구한 평점 평균 값을 vam_book 테이블의 rating 컬럼에 반영하는 메서들 작성합니다.
* getReplyAverage
- 어떠한 상품의 평점 평균값인지 조건을 주기 위해 int 타입의 bookId 변수를 파라미터로 선언했습니다.
- 평점 평균값을 반환받을 수 있도록 Double 타입을 반환 타입으로 지정 했습니다. (아직 상품 평점이 없어서 null을 반환 할 수도 있다고 생각되어 double 기본 타입이 아닌 null또한 값으로 가질 수 있는 Wrapper 클래스인 Double을 반환타입으로 지정했습니다.)
* updateRating
- '상품번호', '평균 평점 값'을 전달받을 수 있도록 파라미터로 UpdateReplyDTO 타입의 변수를 선언했습니다.
/* 평점 평균 구하기 */
public Double getRatingAverage(int bookId);
/* 평점 평균 반영하기 */
public int updateRating(UpdateReplyDTO dto);
ReplyMapper.xml
src/main/resources/com/vam/mapper 경로에 있는 ReplyMapper.xml 파일에 인터페이스에서 선언한 메서드가 수행할 쿼리문을 작성합니다.
<select id="getRatingAverage" resultType="double">
select avg(rating)
from vam_reply
where bookId = #{bookId}
</select>
<update id="updateRating">
update vam_book
set ratingAvg = #{ratingAvg}
where bookId = #{bookId}
</update>
5. Service
댓글 등록, 수정, 삭제 Service 메서드에 평균 평점을 반영해주는 코드를 추가해주어야 하는데 3 가지 모두 동일한 코드를 사용할 것이기 때문에 단순히 메서드를 호출만 할 수 있도록, 메서드를 만들어주겠습니다.
com.vam.service 패키지의 ReplyServiceImpl클래스에 setRating 메서드를 선언합니다.
- 반환 타입은 없습니다.
- 상품 번호 값이 필요로 하기 때문에 int 타입의 bookId를 파라미터 변수로 선언했습니다.
public void setRating(int bookId) {
}
선언한 메서드의 구현부에 먼저 상품의 평점 평균값을 구하는 Mapper 메서드를 호출하여 반환받은 값을 새로 선언한 변수에 대입하는 코드를 작성합니다.
public void setRating(int bookId) {
Double ratingAvg = replyMapper.getRatingAverage(bookId);
}
반환 받은 값이 null 일 경우 변수에 0이 저장되도록 if문을 사용해서 작성했습니다.
public void setRating(int bookId) {
Double ratingAvg = replyMapper.getRatingAverage(bookId);
if(ratingAvg == null) {
ratingAvg = 0.0;
}
}
UpdateReplyDTO 객체를 인스턴스화 하여 bookId, ratingAvg 값을 객체의 변수에 저장합니다.
public void setRating(int bookId) {
Double ratingAvg = replyMapper.getRatingAverage(bookId);
if(ratingAvg == null) {
ratingAvg = 0.0;
}
UpdateReplyDTO urd = new UpdateReplyDTO();
urd.setBookId(bookId);
urd.setRatingAvg(ratingAvg);
}
값이 세팅된 UpdateReplyDTO 객체를 인자로 하는 updateRating Mapper 메서드(테이블에 평균 평점 반영)를 호출합니다.
public void setRating(int bookId) {
Double ratingAvg = replyMapper.getRatingAverage(bookId);
if(ratingAvg == null) {
ratingAvg = 0.0;
}
UpdateReplyDTO urd = new UpdateReplyDTO();
urd.setBookId(bookId);
urd.setRatingAvg(ratingAvg);
replyMapper.updateRating(urd);
}
이로써 상품의 평균값을 구하고 vam_book테이블 ratingAvg 컬럼에 값을 반영하는 메서드를 완성했습니다. 작성한 메서드를 등록, 수정, 삭제 Service 메서드 구현부에서 호출해줍니다.
등록
수정
삭제
6. 테스트
상품을 등록, 수정, 삭제했을때 해당 상품의 평균 평점 값이 변경되었는지 확인해봅니다. 테스트를 위해서 vam_reply테이블에 한 상품에 대한 행을 여러 개 추가했습니다.
댓글 등록, 수정, 삭제 후 vam_book의 ratingAvg에 값이 반영이 되었는지 확인합니다.
값이 들어왔는데 소수점 아래 너무 많은 값이 들어와 있는 걸 확인했습니다. 이를 소수점 첫째 자리까지 표시가 되도록 보완해보겠습니다.
7. 보완
평균값의 소수점 첫째 자리까지 표시하기 위해서 DB단계에서 가공을 하거나 Java에서 가공을 할 수 있습니다. 전 Java에서 Math 클래스의 round 함수를 사용하여 가공을 하겠습니다.
ReplyServiceImpl에 작성했던 setRating 구현부에서 if문 다음 공간에 아래의 코드를 추가해줍니다.
ratingAvg = (double) (Math.round(ratingAvg*10));
ratingAvg = ratingAvg / 10
서버를 구동하여 댓글 등록, 수정, 삭제 중 하나를 수행하여 값이 제대로 들어오는지 확인합니다.
REFERENCE
DATE
- 2020.01.20