현재 진행중인 프로젝트에서 나는 댓글의 좋아요와 싫어요 기능을 맡게 되었다.
기존에 설계했던 테이블은 아래와 같이 구성되어 있었다.
좋아요와 싫어요는 모두 COMMENT(댓글) 테이블에 속성으로 포함시켰다. 댓글에 좋아요를 누르면, 단순하게 likes가 1씩 증가할 것이라 생각했기 때문이다.
하지만 이렇게 댓글 테이블의 컬럼으로 놓게 되었을 때, 동일한 회원이 동일한 댓글에 좋아요를 여러번 누를 수 있게 된다.
이를 막기 위해서는 회원이 어떤 댓글에 좋아요를 눌렀는지 알아야 한다. 즉, 좋아요라는 친구는 회원과 댓글에 대한 정보를 모두 알아야 한다.
그래서 COMMENT 테이블에 있는 LIKES 속성을 밖으로 빼내어 다음과 같이 테이블로 새롭게 추가했다.
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@Builder
@Getter
public class CommentLikes {
@Id @GeneratedValue
@Column(name = "comment_likes_id")
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "comment_id")
private Comment comment;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "member_id")
private Member member;
}
public interface CommentLikesRepository extends JpaRepository<CommentLikes, Long> {
@Query("select cl from CommentLikes cl where cl.member = :member and cl.comment = :comment")
Optional<CommentLikes> findByMemberAndComment(Member member, Comment comment);
}
파라미터로 넘어온 Member, Comment와 연결된 CommentLikes 객체를 조회하는 메서드를 작성했다.
아래는 회원이 댓글에 좋아요를 누르는 로직을 작성한 코드다.
public void likeComment(Long commentId, HttpServletRequest request) {
String memberEmail = extractEmailFromToken(extractToken(request));
Member member = memberRepository.findByEmail(memberEmail)
.orElseThrow();
Comment comment = commentRepository.findById(commentId)
.orElseThrow();
Optional<CommentLikes> byMember = commentLikesRepository.findByMemberAndComment(member, comment);
if (byMember.isPresent()) {
return;
}
CommentLikes commentLikes = CommentLikes.builder()
.member(member)
.comment(comment)
.build();
commentLikesRepository.save(commentLikes);
}
우선 회원과 댓글을 조회하고나서, 각각을 저장한 CommentLikes 객체를 만들어 저장한다.
이때 가져온 Member와 Comment에 연결된 CommentLikes가 존재한다면, 즉 회원이 이미 해당 댓글에 좋아요를 눌렀다면 메서드를 빠져나가게 한다. 이 부분은 나중에 예외로 처리하는 편이 좋을 것같다.
다음은 하나의 댓글에 있는 좋아요 수를 조회하는 로직이다.
public long getLikesCount(Long commentId) {
return commentRepository.findById(commentId)
.orElseThrow()
.getLikes()
.size();
}
파라미터로 넘어온 댓글 ID에 해당하는 댓글을 가져온 후, 그 댓글이 가진 좋아요 목록을 가져온다. 그리고 좋아요 목록의 개수를 반환하면 끝이다.
이번에 구현한 기능은 동일한 회원이 중복으로 누르는 것을 어떻게 방지할지 깨닫는 순간부터 별로 어렵지는 않았다.