별점 + 코멘트 기능

부추·2022년 4월 13일
0

왓챠피디아를 이용해봤다면 알 수 있을 것이다. 코멘트만 남길 수도, 별점만 남길 수도, 코멘트도 별점도 남길 수도 있다는 것을.. 다양한 경우의 수가 존재한다.

처음에 테이블 설계 때 막연하게 별점 테이블의 PK를 코멘트 테이블에서 FK로 받아서 둘을 연결시켜주면 되겠다고 생각만 했다.
프로젝트 초반에 설계했던 ERD

공교롭게 별점, 코멘트 기능이 내가 담당하던 기능이 아니었어서 신경을 못썼었다. 일단 각자 insert는 되고 있었기에 잘 되고 있구나 생각하다가 내가 코멘트 목록 출력 부분을 구현하게 되면서 이상한 점을 깨닫게 됐다.

보이는 바와 특정 컨텐츠에 대한 각 유저마다의 코멘트 내용과 출력이 같이 출력되어야 하는데, 이 때

SELECT a.*, s.star FROM (
SELECT d.name, d.photo_name, c.* FROM dmember_detail d JOIN dcomment c ON d.mem_num=c.mem_num ) a 
LEFT OUTER JOIN dcontents_star s ON a.star_num = s.star_num 
WHERE a.contents_type=#{contents_type} and a.contents_num=#{contents_num} 
ORDER BY a.comment_num DESC

위의 sql문을 사용해서 리스트를 불러오는데 분명 별점이 존재하는데도 불구하고 별점이 불러와지지 않는 것이다.
sql문을 다시 살펴보고 sql developer에서 실행해보고 결과 값도 확인한 뒤에야 알 수 있었다.

기존에 설계했던 대로 코멘트 테이블과 별점 테이블을 FK로 엮어줘야 하는데 코멘트 테이블의 star_num 컬럼은 그대로 계속 null인 상태로 새로운 row가 생성되고 별점 테이블에서는 또 별도의 row가 생성되고 있으니 둘이 연결이 될 수 있을리가..
너무나도 당연한 이야기긴한데 그때 당시에는 눈에 잘 안보였다 ㅎ;

  1. 별점만 주는 경우
  2. 코멘트만 작성하는 경우
  3. 별점을 먼저 준 뒤 코멘트를 작성하는 경우
  4. 코멘트를 먼저 작성한 뒤 별점을 주는 경우

1,2 는 문제가 없고 결국 3,4가 문제인 건데.. 조건체크와 insert 문장을 어떻게 작성해야 하나 고민 꽤나 했었다.
star_num의 존재 여부를 먼저 확인한 뒤, 없으면 새롭게 ①star_num을 생성. 그리고 나중에 생성되는 row에 기존에 생성된 ①의 star_num을 입력해주는 걸로 해결했다. 텍스트로만 보면 감이 안오니까 같이 봐보도록 하자.

1. 먼저 컨트롤러에서 컨텐츠 디테일 페이지로 데이터를 뿌릴 때, 해당 컨텐츠에 유저가 평가한 별점 정보(star_num 등)를 같이 뿌려준다.

해당 jsp에서는 hidden타입으로 어딘가에 저장해놨었다.

2. 그리고 코멘트 등록 이벤트 발생 시, ajax로 코멘트 컨트롤러로 star_num을 전달한다.

이 때 만약 star_num이 존재하지 않는다면 임의로 star_num 값을 0으로 명시해준다. (단순한 조건체크 용 값 때문에 어떤 값이든 상관없음)

3. 코멘트 컨트롤러로 이동해서, 넘겨받은 데이터들을 매퍼로 또 전달한다.

xml 타입의 mapper를 사용할 것이고 이것저것 데이터를 넘길거라 map 객체에 넘겨받은 데이터를 저장해서 mapper로 전달했다.
약간 여기서부터 조원들에게 설명하기를 포기했다.

4. insert문을 실행시켜보자.

<insert id="insertComment" parameterType="map" >
		INSERT INTO dcomment 
				(
					comment_num,
					contents_num,
					contents_type,
					content,
					mem_num,
					reg_date,
					star_num)
		VALUES 
				(
					dcomment_seq.nextval,
					#{commentVO.contents_num},
					#{commentVO.contents_type},
					#{commentVO.content},
					#{commentVO.mem_num},
					SYSDATE,
				<if test="star_num == 0 "> <!-- 별점이 없으면 일단 dcomment 테이블에서 임의의 star_num을 생성 → 나중에 별점을 추가할 경우 이 star_num과 연결할 용도 -->
					dcontents_star_seq.nextval
				</if>
				<if test="star_num > 0 "> <!-- 이미 등록된 별점이 있으면 dcontents_star 테이블에서 가져온 star_num을 입력 -->
					#{star_num}
				</if>
				)
	</insert>

일반적인 insert문과 크게 다를 건 없고 중간에 if로 조건문이 추가적으로 있을 뿐이다. 앞서 ajax에서 설정했듯이 star_num이 0이라면 (기존에 평가한 별점이 없다면) 그동안은 별점 테이블에서만 사용되었던 별점 시퀀스를 이용해 (일단)새롭게 star_num을 생성해서 insert를 한다. 반면 star_num 값이 존재한다면 (기존에 평가한 별점이 있다면) 별점 테이블에 존재하는, 3의 과정에서 뷰단에 뿌려졌던 star_num값을 가져와서 똑같이 입력한 뒤 insert해주면 된다.

즉, 간단히 말해서
1. star_num이 존재하든 안하든 일단 contents controller에서 contents detail page로 값을 뿌려준다.
2. ajax를 통해 contents detail page에 뿌려졌던 star_num을 comment controller로 전달
3. controller에서 다시 mapper로 전달
4. if문을 통해 insert되는 value를 다르게 설정
의 과정을 반복하는 것.

별점이 없었을 경우 : 코멘트 테이블에새로운 star_num이 생성됨 (나중에 별점을 줄 경우에 사용되어야 하기 때문에 임의로 일단 생성해준다)
별점이 있었을 경우 : 별점 테이블에 존재하는 star_num을 끌고와서 코멘트 테이블에 저장

또한 제약조건도 변경해야했다. 코멘트를 먼저 작성하는 경우 star_num을 FK로 한다면 FK 무결성 제약조건에 위반돼서 어쩔 수 없이 FK 제약 조건을 삭제했다. 별개의 테이블이 되어버린 둘..

코멘트 작성의 케이스로 설명을 했는데 반대의 경우도 똑같이 진행하면 된다. 분명 좀더 깔끔한 코드가 있을 것 같은데.. 내 머리로는 이게 한계..

profile
부추가 좋아요

0개의 댓글