PROJECT-LOG/likelion.university

[이슈] 게시글별 댓글 전체 조회시 대댓글 개수만큼 부모 댓글만 중복 조회됨

HwangJerry 2023. 10. 13. 15:35

문제 쿼리는 다음과 같다.

public List<CommentDetailResponseDto> findAll(Long postId) {
        return queryFactory
                .select(commentDetailResponseDto())
                .from(comment)
                .join(comment.author, user)
                .leftJoin(comment.parentComment, comment)
                .leftJoin(comment.commentLikes, commentLike)
                .where(comment.post.id.eq(postId))
                .orderBy(comment.createdDate.asc())
                .fetch();
}

 

parentComment를 join하여 조회하기 때문에 발생한 문제로 보았다. 즉, 내가 쿼리에 대한 이해가 부족하여 아예 잘못 짠 것이다.

 

댓글이든 대댓글이든 comment라는 엔티티이다. 근데 parentComment 기준으로 join을 하게 되면 parent만 나오게 되는 건 어쩌면 당연했을 것이다. (이러한 실수를 하지 않도록 좀 더 컴퓨팅적 사고력을 길러야겠다는 생각이 든다.)

 

유저를 join한 이유는 dto를 생성할 때 author 정보가 직접적으로 사용되기 때문이다. 나머지 중에서 기존에 join을 걸었던 commentLike의 경우에는 생성되는 쿼리를 보니 nest query가 생성되므로 join 효과가 이미 발생하니 이를 또 쿼리로 넣어주는게 불필요하다 보았다.

 

또한 게시글에 대한 모든 댓글을 클라이언트에 던져주면 parent 여부에 따라 댓글을 정렬하는것은 client의 역할이라 느꼈고, 일단 해당 게시글에 대한 모든 comment를 조회하여 던져주면 클라이언트가 알아서 visualize하겠구나 싶다. (필요한 게 있다면 다시 지원해줘야겠다 하였다.)

 

수정된 query 코드는 다음과 같다.

public List<CommentDetailResponseDto> findAll(Long postId) {
        return queryFactory
                .select(commentDetailResponseDto())
                .from(comment)
                .join(comment.author, user)
                .where(comment.post.id.eq(postId))
                .orderBy(comment.createdDate.asc())
                .fetch();
}

@NotNull
private static QCommentDetailResponseDto commentDetailResponseDto() {
        return new QCommentDetailResponseDto(
                comment.id,
                comment.author.id,
                comment.author.profile.name,
                comment.parentComment.id,
                comment.body,
                comment.commentLikes.size(),
                comment.isDeleted);
}

 

위 코드로 수행되는 쿼리를 확인해보니 아래와 같이 간단하게 잘 생성되고, 그 결과 또한 원하는대로 단일 게시글에 대한 댓글들이 조회되는 것을 볼 수 있었다.

 

(아래 쿼리 중 nested 된 부분은 count를 활용해야 하는 부분이라 성능에 큰 지장도 없으며 불가피한 부분이고, 나머지는 평범하게 잘 생성된 것 같다.)

select
        comment0_.comment_id as col_0_0_,
        comment0_.user_id as col_1_0_,
        user1_.name as col_2_0_,
        comment0_.parent_comment_id as col_3_0_,
        comment0_.body as col_4_0_,
        (select
            count(commentlik3_.comment_id) 
        from
            comment_like commentlik3_ 
        where
            comment0_.comment_id = commentlik3_.comment_id) as col_5_0_,
        comment0_.is_deleted as col_6_0_ 
    from
        comments comment0_ 
    inner join
        users user1_ 
            on comment0_.user_id=user1_.user_id 
    where
        comment0_.post_id=? 
    order by
        comment0_.created_date asc

 

결론

queryDsl과 dataJpa와 같은 ORM으로 처음 DB의 세계를 먼저 배우다보니, 기본이 없어서 이렇게 조금만 쿼리가 복잡해져도 예상과는 다른 결과를 만나게 된다.

 

이러한 약점을 보완하기 위해 이번에 데이터베이스 과목을 수강중인데, 생각보다도 더더욱 몰랐던 기본적인 내용이 많고 더 제대로 DB와 SQL에 대해 이해할 수 있게 되어서 다행이라고 느낀다.

 

이번 이슈를 해결하면서도 다시금 기본의 중요성에 대해 느끼게 되었다. 이번 DB 공부할 때 정리를 확실히 해둬야겠다.