목표
JPQL로 작성된 아래 메서드의 쿼리문을 Querydsl로 변환하자.

상황
1. Todo 조회 시 연관된 엔티티인 user를 left join으로 가져옴: N+1 문제 발생 가능

2. Querydsl 관련 종속성 및 QuerydslConfig 없음: 추가 필요
3. 연관 객체로 가져오는 user의 데이터 중에서 일부만 사용하여 dto로 재가공: db에서 조회할 때부터 dto projection 적용
해결 방법
1. build.gradle에 종속성 추가

2. QuerydslConfig.java 파일 추가

3. TodoCustomRepository.java 및 Querydsl 적용할 db 조회 메서드 생성

4. 3의 구현체인 TodoCustomRepositoryImpl 및 Querydsl 쿼리문 작성

5. 기존 TodoRepository에 interface 상속 추가

결과
정상 조회 및. 쿼리도 1번만 실행


개선
현재 Querydsl문은 Projections.constructor()를 이용하여 todo 엔티티로부터 6개의 필드를 타입 순서를 맞춰 생성자 파라미터에 넣어줘야만 한다. 필드가 많으므로 필드 주입 순서가 달라질 경우 런타임에 문제가 발생할 수 있다.
따라서 컴파일 타임에 파라미터 타입과 순서 오류를 잡아주는 @QueryProjection으로 변경함으로써 컴파일 타입 안전성을 높여보자. 물론 이 방법에도 장단점이 있으므로 알고 가는 것이 좋다.
@QueryProjection 장단점 비교
장점: 잘못된 타입/순서를 컴파일 타임에 확인 가능
단점: DTO가 Querydsl에 의존하게 됨
1. UserResponse, TodoResponse DTO 클래스 생성자에 @QueryProjection 어노테이션 적용


2. 문제 발생: QUserResponse 부분에서 컴파일 에러 발생
3. 원인 분석
Q클래스는 빌드 시점에 APT가 자동 생성하는데, QUserResponse를 TodoCustomRepository.findByTodoId() 메서드가 이미 참조하고 있어 컴파일 자체가 실패 -> Q클래스 생성 불가 -> 악순환
3. 해결:
해당 파일 전체를 주석 처리, ./gradlew compileJava 실행: 참조 코드가 없는 상태에서 빌드 성공. APT가 Q클래스 생성. 이후. 주석 복구
문제 원인 상세
1단계: TodoResponse만 Q클래스로 바꾸고 빌드 -> 성공

2단계: UserResponse도 Q클래스로 바꾸려고 보니 컴파일러가 인식을 못함 -> 그런데 컴파일을 해야 생성됨 -> 악순환

[타임라인]
[1단계] Projections.constructor() → QTodoResponse로 교체 후 빌드
→ QTodoResponse 생성 성공 ✅
[2단계] 내부의 UserResponse도 QUserResponse로 교체
→ 이 시점에 QUserResponse는 아직 생성된 적 없음
→ QUserResponse를 참조하는 코드 때문에 컴파일 실패
→ QUserResponse 생성 불가 (악순환) ❌
인사이트
Q클래스는 컴파일 시점에 APT(Annotation Processing Tool)에 의하여 생성됨.
생성되기도 전에 컴파일 에러가 나는 경우가 있음.
그럴 땐 해당 코드 전체를 주석 처리 후 ./gradlew compileJava를 수행하면 일단 생성은 됨.
'Projects > [Spring] Code Refactoring Project' 카테고리의 다른 글
| [Spring Plus] DTO Projection으로 N+1 문제 해결하기 (0) | 2026.03.01 |
|---|---|
| [Spring Plus] JPA Cascade 옵션 지정하기 (0) | 2026.02.28 |
| [Spring Plus] AOP의 동작 흐름 제어하기 (0) | 2026.02.28 |
| [Spring Plus] 컨트롤러 테스트 성공시키기 (0) | 2026.02.28 |
| [Spring Plus] JPQL로 쿼리 옵션 추가하기 (0) | 2026.02.27 |