Spring

리뷰 좋아요 조회 쿼리 N + 1 문제 해결하기

heyh0 2024. 4. 5. 20:43

# Event

    @Override
    public PageResponse<ReviewContentGetAllResponse> findAllReviewContentOrderBy(
            Long userId, RatingOrderType orderType, int size, int offset) {
        List<ReviewContentGetAllResponse> reviewContents = queryFactory
                .select(new QReviewContentGetAllResponse(
                        review.id, user.profileImage, user.name, review.rating, review.content, book.isbn, book.title, book.imageUrl
                        JPAExpressions.select(reviewLike.user.id) // 문제가 되는 쿼리
                                .from(reviewLike)
                                .where(reviewLike.review.eq(review))
                                .fetch()
                ))
                .from(review)
                .leftJoin(book).on(review.book.eq(book))
                .leftJoin(book).on(review.user.eq(user))
                .where(review.user.id.eq(userId))
                .orderBy(createSpecifier(orderType), review.id.asc())
                .offset(offset)
                .limit(size)
                .fetch();

 

기능 요구사항은 사용자가 리뷰 목록을 조회하면 내가 좋아요한 리뷰에 대해서 좋아요 버튼이 빨간 하트로 보여져야 한다.

그래서 현재 사용자의 pk가 리뷰 좋아요 목록에 있는지 프론트단에서 검사하여 보여진다.

(유저-리뷰 좋아요는 다대다 관계로 중간 연결 테이블을 만들었다, 구성 필드는 리뷰 좋아요 pk, 리뷰 fk, 유저 fk다.)

 

즉 위 쿼리는 리뷰를 조회할 때 필요한 리뷰 목록을 가져오는 쿼리다.

각 리뷰마다 리뷰 좋아요 목록이 존재하기에 현재 쿼리는 리뷰를 가져올 때마다 그에 따른 리뷰 좋아요 쿼리도 날린다.

즉, 리뷰 목록 조회 쿼리를 1번 보냈지만, 리뷰 내용이 10개라면 일치하는 10개의 리뷰 좋아요 조회 쿼리가 날아간다.

바로 N + 1 문제가 발생한 것이다.

데이터가 많아질수록 해당 쿼리의 수행 시간도 오래 걸릴 것이라고 판단했다.

# Solution

서비스 로직

 

리뷰 좋아요 없이 리뷰 목록만 가져온다. 쿼리 1회

가져온 리뷰 목록에서 리뷰 id만 추출한다.

리뷰 id 목록과 현재 유저 id로 빨간 하트를 보여줘야 하는 리뷰 id 목록을 가져온다. 쿼리 1회

 

현재 사용자가 좋아요한 리뷰 id 목록을 가져오는 쿼리

 

 

쿼리를 잘 분리했고 해결했다 !

그리고 기존에는 리뷰 레포에서 리뷰 좋아요 레포를 접근하는 역할과 책임이 불명확한 문제가 있었는데, 서비스 로직에서 합쳐서 역할과 책임을 확실히 설정할 수 있었다 !