[Spring Boot] Lv2-8 QueryDSL

김현정·2025년 5월 14일
0

JPQL → QueryDSL 리팩토링 중 N+1 문제 해결

문제 상황

기존 TodoService#getTodo(Long todoId) 메서드는 일정과 작성자 정보를 함께 조회하기 위해 JPQL 기반의 findByIdWithUser() 메서드를 사용하고 있었다.

@Query("SELECT t FROM Todo t JOIN FETCH t.user WHERE t.id = :id")
Optional<Todo> findByIdWithUser(@Param("id") Long id);

문제 원인

QueryDSL로 리팩토링할 때 단순히 selectFrom(todo).where(todo.id.eq(id))만 사용할 경우, 연관된 user 정보가 Lazy 로딩으로 인해 나중에 조회되면서 N+1 문제가 발생할 수 있음.

해결 방법

Todo todo = jpaQueryFactory
    .selectFrom(QTodo.todo)
    .where(QTodo.todo.id.eq(todoId))
    .fetchOne();

→ 이후 todo.getUser()를 호출하면 쿼리가 한 번 더 나가며 N+1 문제 발생

JOIN FETCH를 QueryDSL로 대체하기 위해 fetchJoin()을 사용하여 아래와 같이 리팩토링함.

QTodo todo = QTodo.todo;
QUser user = QUser.user;

Todo result = jpaQueryFactory
    .selectFrom(todo)
    .join(todo.user, user).fetchJoin() // N+1 방지
    .where(todo.id.eq(todoId))
    .fetchOne();

배운 점

QueryDSL에서 연관 관계의 지연 로딩 문제(N+1)를 해결하려면 반드시 fetchJoin()을 사용해야 한다.

JPQL의 JOIN FETCH는 QueryDSL에서는 join(...).fetchJoin()으로 대체할 수 있음.

단순 조회라고 해도 Entity 간 연관관계를 적극적으로 사용하는 경우에는 N+1 문제가 생길 수 있으므로 항상 주의해야 한다.

0개의 댓글