Spring
QueryDSL 의존성 설정 및 활용
윤승
2025. 5. 13. 14:43
QueryDSL을 사용하기 전에 먼저 QueryDSL이란 무엇인지 알아보자.
QueryDSL이란?
QueryDSL은 도메인 모델을 기반으로 한 타입 안전(type-safe)한 쿼리 작성 라이브러리
JPA를 쓰다 보면 이런 경험 한 번쯤 해봤을 거다.
복잡한 조건을 가진 쿼리문들을 사용하게 되면 JPQL로 하자니 불편하고, 메서드 이름도 길어지고...
이럴 때 사용하는 게 바로 QueryDSL이다.
QueryDSL은 자바 코드로 SQL을 짤 수 있게 해주는 안전한 쿼리 빌더 타입이다!
✅ QueryDSL 관련 의존성 및 설정
QueryDSL을 사용하기 위해서는 다음과 같은 의존성들과 설정이 필요하다.
스프링부트3 버전을 사용 중이다.
build.gradle 파일
plugins {
id 'java'
id 'org.springframework.boot' version '3.3.3'
id 'io.spring.dependency-management' version '1.1.6'
}
group = 'org.example'
version = '0.0.1-SNAPSHOT'
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'com.h2database:h2'
runtimeOnly 'com.mysql:mysql-connector-j'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
// 🔽 QueryDSL 관련 의존성 추가
// QueryDSL JPA 연동 (jakarta API 기반)
implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta'
// Q타입 클래스 생성을 위한 APT 프로세서
annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jakarta"
annotationProcessor "jakarta.annotation:jakarta.annotation-api"
annotationProcessor "jakarta.persistence:jakarta.persistence-api"
// bcrypt
implementation 'at.favre.lib:bcrypt:0.10.2'
// jwt
compileOnly group: 'io.jsonwebtoken', name: 'jjwt-api', version: '0.11.5'
runtimeOnly group: 'io.jsonwebtoken', name: 'jjwt-impl', version: '0.11.5'
runtimeOnly group: 'io.jsonwebtoken', name: 'jjwt-jackson', version: '0.11.5'
}
// QueryDSL의 Q타입 클래스가 생성될 디렉토리 지정
def querydslSrcDir = 'src/main/generated'
// clean 명령 시 생성된 Q타입 디렉토리 삭제
clean {
delete file(querydslSrcDir)
}
tasks.named('test') {
useJUnitPlatform()
}
관련 의존성들과 설정을 해주었다면,
Gradle에 들어가서 compile.java를 실행해 주면 된다.
실행하였다면,
이런 식으로 Q클래스들이 생성되어 있는 것을 알 수 있다.
QueryDSL을 사용하려면, Q 클래스가 정상적으로 생성된 후 JPAQueryFactory를 Bean으로 등록해야 한다.
이를 위해 별도의 @Configuration 파일을 작성해 JPAQueryFactory를 스프링 컨테이너에 등록해 주는 작업이 필요하다.
@Configuration
public class JPAConfiguration {
@PersistenceContext
private EntityManager em;
@Bean
public JPAQueryFactory jpaQueryFactory() {
return new JPAQueryFactory(em);
}
}
- @Configuration: 이 클래스가 스프링 컨테이너에 Bean으로 등록된다.
- @PersistenceContext는 스프링이 해당 필드에 적절한 EntityManager를 주입해 준다.
- EntityManager는 JPA의 핵심 인터페이스로, 쿼리 실행, 엔티티 저장/조회/수정 등을 담당한다.
- JPAQueryFactory는 QueryDSL의 핵심 객체로, 이걸 통해 타입 안전한 쿼리(QueryDSL 문법)를 실행할 수 있다.
모든 설정들이 완료되었다면, 이제 QueryDSL을 사용해 보자.
기존 JPQL(@Query) 방식
@Query("SELECT t FROM Todo t " + // Todo 엔티티를 선택
"LEFT JOIN t.user " +
"WHERE t.id = :todoId") // 조건: Todo의 id가 todoId인 경우
Optional<Todo> findByIdWithUser(@Param("todoId") Long todoId); //NULL방지
- 문자열 기반 JPQL을 @Query 어노테이션 안에 작성
- 조건은 :파라미터명 형식으로 바인딩
- fetch join을 쓰지 않으면 지연 로딩 발생 가능
QueryDSL 방식
@RequiredArgsConstructor // final 필드를 자동 생성자 주입해주는 롬복 어노테이션
public class TodoRepositoryQuerydslImpl implements TodoRepositoryQuerydsl {
private final JPAQueryFactory jpaQueryFactory; // QueryDSL 쿼리를 실행할 수 있는 핵심 객체
@Override
public Optional<Todo> findByIdWithUser(Long todoId) {
return Optional.ofNullable(
jpaQueryFactory.select(QTodo.todo) // Todo 엔티티를 선택
.from(QTodo.todo) // Todo 테이블로부터
.leftJoin(QTodo.todo.user) // Todo와 연결된 User를 left join
.fetchJoin() // 즉시 로딩(fetch join)으로 User 정보까지 한 번에 가져옴
.where(QTodo.todo.id.eq(todoId)) // 조건: id가 todoId인 데이터
.fetchOne() // 단일 결과 조회 (없으면 null 반환)
);
}
}
- 컴파일 타임에 오류 확인 가능 → 타입 안정성 확보
- 가독성이 높고 유지보수에 유리
- 복잡한 조건, 동적 쿼리 구성에 매우 강력
Repository 로직 흐름