우당탕탕
Spring Boot에서 Querydsl 도입, 직접 해보고 정리한 절차와 팁 본문
저도 처음에 Spring Boot 프로젝트에 Querydsl을 도입할 때 생각보다 삽질이 많았거든요. 쿼리 작성이 훨씬 편해진다는 이야기에 도입을 결심했는데, 설정부터 코드 작성까지 한 번에 깔끔하게 정리된 자료가 별로 없더라고요.
그래서 직접 제가 경험한 걸 바탕으로 Querydsl 도입 절차를 단계별로 정리해봤습니다. 설정부터 빌드, 코드 작성, 그리고 흔히 겪는 문제 해결까지 이 글만 보면 충분할 거예요.
개발 환경 / 버전 정보
제가 사용한 환경은 Java 17, Spring Boot 3.1.0, 그리고 Gradle 8.0였습니다. 이 조합에서 Querydsl 5.0.0 버전을 도입했어요.
1단계: build.gradle에 Querydsl 의존성 추가
사실 이 부분에서 많이들 헷갈려하시는데, 저는 이렇게 했어요. 먼저 build.gradle에 Querydsl 관련 의존성을 추가합니다.
plugins {
id 'java'
id 'org.springframework.boot' version '3.1.0'
id 'io.spring.dependency-management' version '1.1.0'
}
dependencies {
implementation 'com.querydsl:querydsl-jpa:5.0.0'
implementation 'jakarta.persistence:jakarta.persistence-api:3.1.0'
annotationProcessor 'com.querydsl:querydsl-apt:5.0.0:jpa'
annotationProcessor 'jakarta.annotation:jakarta.annotation-api:2.1.1'
// 기타 필요한 의존성
}
특히 annotationProcessor가 빠지면 Q파일 생성을 못해서 계속 막혔어요. 꼭 넣으셔야 합니다.
2단계: Gradle 플러그인 설정 및 소스 폴더 추가
그다음에 Q클래스가 제대로 생성되려면 빌드 시에 annotationProcessor가 실행돼야 하거든요. 그래서 Gradle에 다음 설정을 추가했어요.
java {
sourceSets {
main {
java {
srcDir 'build/generated/querydsl'
}
}
}
}
task generateQueryDSL(type: JavaCompile) {
source = sourceSets.main.java
classpath = configurations.annotationProcessor
options.annotationProcessorPath = configurations.annotationProcessor
destinationDir = file('build/generated/querydsl')
options.compilerArgs = [
'-proc:only',
'-processor', 'com.querydsl.apt.jpa.JPAAnnotationProcessor'
]
}
compileJava.dependsOn generateQueryDSL
이렇게 하면 빌드 시 자동으로 Q파일이 build/generated/querydsl에 생성돼요. 이 경로를 인텔리제이에도 소스 폴더로 등록해줘야 제대로 인식됩니다.
3단계: 인텔리제이에서 Generated Sources 경로 등록하기
생성된 Q파일을 인텔리제이가 인식 못 하면 코드 자동완성도 안 되고, 에러 표시가 계속 떠요. 그래서 build/generated/querydsl 폴더를 다음 순서로 설정했습니다.
- 프로젝트 뷰에서 해당 폴더 우클릭
- Mark Directory as → Generated Sources Root 선택
이렇게 하니 빌드 후 바로 Q클래스가 인텔리제이에서 자동완성되고 오류도 사라졌습니다.
4단계: 실제 Entity에 적용해보기
이제 Q클래스가 제대로 생성되면 실전에서 사용해봐야겠죠? 저는 이렇게 간단한 회원 엔티티를 만들었어요.
@Entity
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
private int age;
// getter, setter 생략
}
그리고 Repository에 Querydsl 지원을 추가하기 위해 QuerydslPredicateExecutor를 상속하거나 직접 커스텀 리포지토리를 만들었는데요, 저는 편리성 때문에 Spring Data JPA의 Querydsl 지원을 사용했습니다.
public interface MemberRepository extends JpaRepository<Member, Long>, QuerydslPredicateExecutor<Member> {
}
이렇게 하면 기본적인 Querydsl 기능을 바로 사용할 수 있어요.
5단계: Q클래스를 활용해 직접 쿼리 작성해보기
이 단계가 사실 가장 재밌더라고요. Q클래스를 활용해서 원하는 조건으로 쿼리를 만드는 과정입니다. 간단한 예시를 보여드릴게요.
@Autowired
private EntityManager em;
public List<Member> findByUsernameAndAge(String username, int age) {
JPAQueryFactory queryFactory = new JPAQueryFactory(em);
QMember m = QMember.member;
return queryFactory.selectFrom(m)
.where(m.username.eq(username)
.and(m.age.gt(age)))
.fetch();
}
이렇게 하면 username이 특정 값이고, 나이가 특정 값 이상인 회원을 쉽게 조회할 수 있죠. 코드가 훨씬 깔끔해졌어요.
여기서 삽질했던 부분들
처음에 빌드해도 Q클래스가 생성 안 되는 문제가 있었어요. 에러 메시지는 없는데 아무런 Q파일도 안 만들어져서 정말 한참 헤맸는데, annotationProcessor 의존성을 안 넣은 게 원인이었더라고요.
annotationProcessor 'com.querydsl:querydsl-apt:5.0.0:jpa'
이걸 안 넣으면 아무리 빌드해도 계속 Q클래스가 생성 안 돼서 딱히 오류도 없이 당황했어요.
또한 인텔리제이에서 generated 소스 폴더를 마킹 안 하면 빌드 성공해도 편집기에서는 오류로 표시돼서 꽤나 헷갈렸습니다.
심화: JPAQueryFactory 빈으로 등록해 재사용하기
처음에는 매번 함수 내에서 JPAQueryFactory를 새로 만들었는데, 이게 생각보다 비효율적이더라고요. 그래서 @Bean으로 등록해서 DI 받는 방법도 써봤습니다.
@Configuration
public class QuerydslConfig {
@PersistenceContext
private EntityManager em;
@Bean
public JPAQueryFactory jpaQueryFactory() {
return new JPAQueryFactory(em);
}
}
이렇게 등록해두니 서비스 클래스에서 매번 새로 만드는 번거로움 없이 깔끔하게 쓸 수 있었어요.
자주 물어보시는 것들
Q. Q클래스가 안 만들어져요, 빌드만 하면 되나요?
A. 빌드하면 보통 생성되긴 하는데, 의존성에 annotationProcessor 'com.querydsl:querydsl-apt:...:jpa'가 있어야 해요. 그리고 IDE에서 생성 경로를 'Generated Sources'로 마킹해야 인식됩니다.
Q. Querydsl과 Spring Data JPA 같이 써도 되나요?
A. 네, Querydsl을 Spring Data JPA Repository에서 확장 가능해서 둘이 아주 잘 어울려요. 필요하면 커스텀 리포지토리를 만들어 복잡한 쿼리도 깔끔하게 구현할 수 있습니다.
Querydsl 도입하면서 정말 편리해진 점은 동적 쿼리를 깔끔하게 만들 수 있다는 거였어요. 자바 코드로 타입 안정성을 보장하면서 쿼리 조립도 쉬워서 코드를 더 깔끔하게 관리할 수 있더라고요.
요즘 Spring Boot 프로젝트에선 필수처럼 느껴지는 기술이라 실제로 써보면서 꼭 한 번 도입해보시길 추천합니다.
'Tech > Spring' 카테고리의 다른 글
| Spring Security 6 권한 설정, 이 부분부터 꼭 체크하세요 (0) | 2026.06.11 |
|---|---|
| Spring Cloud Gateway 도입하면서 겪은 라우팅 문제와 해결 체크리스트 (0) | 2026.06.09 |
| Spring WebFlux 적용하다 헷갈렸던 개념들, 저랑 다르네요 (0) | 2026.06.01 |
| JPA Auditing으로 생성·수정 시간 자동화하다가 겪은 삽질 후기 (0) | 2026.06.01 |
| Spring Boot JWT 로그인 구현하다 겪은 삽질과 해결 과정 공유합니다 (0) | 2026.05.27 |
