Dev./Spring

[Spring] QueryDSL Projection 방식 비교

limitation01 2025. 12. 17. 19:58

개요

QueryDSL로 검색 API를 구현하면서 엔티티가 아닌 검색 전용 DTO로 결과를 조회해야 했다.

DTO 매핑방식으로 두가지 선택지가 있다는 것을 알게되었다

  • Projections.constructor()
  • @QueryProjection

두 방식은 결과는 같아 보이지만,
안전성 · 유지보수성 · 오류 발견 시점에서 큰 차이가 있었다.

 

Projections.constructor 방식

.select(Projections.constructor(
    TodoSearchResponse.class,
    todo.title,
    manager.id.countDistinct(),
    comment.id.countDistinct()
))

특징

  • DTO에 QueryDSL 의존성 X
  • 생성자만 있으면 바로 사용 가능
  • 설정이 간단

단점 

  • 생성자 파라미터 순서가 바뀌어도 컴파일 통과
  • 타입이 애매하면 런타임 에러
  • DTO 생성자 변경 시 IDE가 쿼리 오류를 잡아주지 못함

쿼리 오류를 실행해보기 전까지 알 수 없다

@QueryProjection 방식

DTO

@Getter
public class TodoSearchResponse {

    private final String title;
    private final long managerCount;
    private final long commentCount;

    @QueryProjection
    public TodoSearchResponse(
            String title,
            long managerCount,
            long commentCount
    ) {
        this.title = title;
        this.managerCount = managerCount;
        this.commentCount = commentCount;
    }
}

쿼리 사용

.select(new QTodoSearchResponse(
    todo.title,
    manager.id.countDistinct(),
    comment.id.countDistinct()
))

특징 

  • 생성자 시그니처가 컴파일 타임에 검증
  • 파라미터 개수/순서/타입이 틀리면 즉시 컴파일 에러
  • DTO 변경 시 쿼리 코드에서 바로 오류 발생

쿼리 오류를 컴파일 시점에 잡을 수 있다

 

@QueryProjection을 선택한 이유

이번 검색 API는 다음 조건을 만족해야 했다.

  • QueryDSL 기반 복잡한 검색
  • 집계(countDistinct) 포함
  • DTO 구조 변경 가능성 있음

이 상황에서는
실행 전에 오류를 잡을 수 있는 안정성이 가장 중요하다고 판단했다.