무엇이 생성되는가 @Entity class Member { String email; int age; } 를 컴파일하면 QueryDSL 의 애너테이션 프로세서가 QMember 를 만든다. 안에는 StringPath email, NumberPath<Integer> age 처럼 필드마다 타입이 붙은 경로 객체가 들어 있다. Path 는 "이 쿼리에서 어떤 칼럼을 가리키는지" 를 나타내는 표현식이고, 타입이 있으니 그 타입에 맞는 연산만 호출할 수 있다.
왜 타입이 중요한가 문자열 쿼리에서 age = 'abc' 는 컴파일러가 막지 못한다. NumberPath<Integer> 에는 문자열을 넘기는 메서드가 아예 없으므로 같은 실수가 컴파일 단계에서 불가능해진다. 즉 검증 시점을 런타임에서 컴파일 타임으로 끌어당기는 것이 Q 클래스의 존재 이유다.
대가 소스가 생성물이라는 점에서 두 가지 비용이 온다.
- 빌드 설정이 필요하다.
annotationProcessor의존성을 걸어야 하고, 생성된 소스는 보통build/generated아래에 놓여 저장소에 커밋하지 않는다. - 엔티티를 바꾸면 재생성해야 한다. 필드 이름을 바꿨는데 Q 클래스가 옛것이면 컴파일이 깨진다. 클린 빌드가 필요한 순간이 이때다. IDE 가 생성 디렉터리를 소스 경로로 인식하지 못해 "
QMember를 찾을 수 없다" 로 보이는 문제도 대부분 이 경로 설정 문제다.
면접에서 이어지는 지점 Spring Boot 3 이후 Q 클래스 생성이 실패하는 사례는 대개 QueryDSL 항목에 적은 Jakarta 이행 때문이다. 프로세서가 javax.persistence.Entity 를 찾는데 엔티티에는 jakarta.persistence.Entity 가 붙어 있어, 프로세서가 엔티티를 하나도 인식하지 못하고 생성물이 비는 형태로 드러난다.