데이터 접근 기술 - Querydsl
- 김영한님의 스프링 DB 2편 강의를 통해 Querydsl의 개념, 설정 방법, 기본 문법, 동적 쿼리 작성법을 정리함
Querydsl 소개와 문제 해결
등장 배경
- 동적 쿼리의 어려움
- 순수 JPA(JPQL)나 스프링 데이터 JPA만으로는 동적 쿼리를 작성하기 까다로움
- 문자열 조합으로 쿼리를 만들면 오타가 발생하기 쉽고 컴파일 시점에 오류를 잡을 수 없음
- Querydsl의 해결책
- 쿼리를 자바 코드로 작성하여 컴파일 시점에 문법 오류를 잡아줌
- IDE의 자동 완성 기능을 활용할 수 있어 개발 생산성이 높아짐
기존 방식과 Querydsl 비교
- 순수 JPA (문자열 조합)
- 조건이 늘어날수록
if문과 문자열 더하기 연산이 복잡해짐 - 띄어쓰기 등 사소한 실수로 런타임 오류가 발생할 수 있음
1 2 3 4 5 6 7 8 9
// 순수 JPA의 동적 쿼리 문제점 public List<Item> findAll(ItemSearchCond cond) { String jpql = "select i from Item i"; // 복잡한 문자열 조합 if (StringUtils.hasText(itemName)) { jpql += " i.itemName like concat('%',:itemName,'%')"; } return em.createQuery(jpql, Item.class).getResultList(); }
- 조건이 늘어날수록
- Querydsl (자바 코드)
- 직관적이고 가독성이 뛰어남
- 메서드 추출을 통해 조건을 재사용할 수 있음
1 2 3 4 5 6 7 8
return query .select(item) .from(item) .where( likeItemName(itemName), maxPrice(maxPrice) ) .fetch();
Querydsl 설정
build.gradle 설정 (Spring Boot 3.x 이상)
-
Querydsl은 설정이 다소 복잡하지만, 한 번 설정해두면 매우 편리하게 사용 가능함
1 2 3 4 5 6 7 8 9 10 11 12 13 14
// build.gradle dependencies { // Querydsl 추가 implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta' annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jakarta" annotationProcessor "jakarta.annotation:jakarta.annotation-api" annotationProcessor "jakarta.persistence:jakarta.persistence-api" } // QClass 생성 위치 및 Clean 설정 def querydslDir = "$buildDir/generated/querydsl" clean { delete file(querydslDir) }
Q 타입 생성
Q 타입이란?
- Querydsl은 컴파일 시점에 엔티티(
@Entity)를 기반으로 Q 클래스를 생성함- ex)
Item엔티티가 있다면QItem이라는 클래스가 생성됨 -
QItem을 사용하여 쿼리를 타입 세이프(Type-Safe)하게 작성할 수 있음1 2 3 4 5
public class QItem extends EntityPathBase<Item> { public static final QItem item = new QItem("item"); public final StringPath itemName = createString("itemName"); // }
- ex)
생성 방법
- Gradle - IntelliJ 사용 시
- Gradle
tasks->build->clean - Gradle
tasks->other->compileJava build/generated/querydsl경로에 Q 파일이 생성되었는지 확인

- Gradle
Querydsl 기본 사용법
JPAQueryFactory 설정
- Querydsl을 사용하려면
JPAQueryFactory가 필요하며EntityManager를 주입받아 생성함 -
스프링 빈으로 등록하여 공용으로 사용하거나 리포지토리에서 직접 생성하여 사용함
1 2 3 4 5 6 7 8 9 10 11 12
@Repository @Transactional public class JpaItemRepositoryV3 implements ItemRepository { private final EntityManager em; private final JPAQueryFactory query; public JpaItemRepositoryV3(EntityManager em) { this.em = em; this.query = new JPAQueryFactory(em); // QueryFactory 생성 } }

기본 쿼리 작성
-
Q 타입을
static import하면 코드가 더 간결해짐1 2 3 4 5 6 7 8 9 10
public List<Item> findAll(ItemSearchCond cond) { String itemName = cond.getItemName(); Integer maxPrice = cond.getMaxPrice(); return query .select(item) .from(item) .where(likeItemName(itemName), maxPrice(maxPrice)) .fetch(); }
-
주요 메서드
fetch()- 리스트 조회, 데이터 없으면 빈 리스트 반환
fetchOne()- 단건 조회, 결과 없으면 null, 둘 이상이면 예외 발생
fetchFirst()limit(1).fetchOne()과 동일
동적 쿼리 구현
BooleanBuilder 사용
- 초기값 없이 생성하거나 초기 조건을 넣어 생성 가능
-
조건에 따라
and(),or()메서드를 체이닝하여 사용1 2 3 4 5 6 7 8 9 10 11 12 13 14
// BooleanBuilder 사용 BooleanBuilder builder = new BooleanBuilder(); if (StringUtils.hasText(itemName)) { builder.and(item.itemName.like("%" + itemName + "%")); } if (maxPrice != null) { builder.and(item.price.loe(maxPrice)); } List<Item> result = query .select(item) .from(item) .where(builder) .fetch();
BooleanExpression 사용 (권장)
where절에null이 들어오면 해당 조건은 무시되는 특성을 이용함-
메서드를 분리하여 코드 가독성을 높이고 조건을 재사용할 수 있음
1 2 3 4 5 6 7 8 9 10 11 12 13
// BooleanExpression 사용 (권장) List<Item> result = query .select(item) .from(item) .where(likeItemName(itemName), maxPrice(maxPrice)) .fetch(); private BooleanExpression likeItemName(String itemName) { if (StringUtils.hasText(itemName)) { return item.itemName.like("%" + itemName + "%"); } return null; // null은 조건 무시 }
- 전체 코드 보기
연습 문제
-
String 기반 쿼리(JPQL 문자열 직접 작성 등)의 가장 큰 문제점은 무엇일까요?
a. 컴파일 시 오류 발견이 어렵다 (런타임 오류 발생)
- String 기반 쿼리는 단순 문자열이므로 오타가 있어도 컴파일 시점에는 알 수 없음
- 실제 쿼리가 실행되는 런타임에 에러가 발생하여 디버깅이 어렵고 위험함
-
Querydsl에서 타입 안전성(Type Safety)을 제공하는 핵심 요소는 무엇일까요?
a. Q 타입(Q 파일) 코드 자동 생성
- Querydsl은 엔티티를 분석하여
Q 타입클래스를 자동으로 생성함 - 이를 통해 필드명 오타나 타입 불일치를 컴파일 시점에 바로 잡아낼 수 있음
- Querydsl은 엔티티를 분석하여
-
Querydsl로 동적 쿼리 작성을 쉽게 하는 기능은 무엇일까요?
a. BooleanBuilder 활용
- 상황에 따라 조건이 달라지는 동적 쿼리를
BooleanBuilder를 사용해 쉽게 조립할 수 있음 - 또는
BooleanExpression을 반환하는 메서드 방식을 사용하여Where절에서null을 무시하는 방식으로도 구현 가능함
- 상황에 따라 조건이 달라지는 동적 쿼리를
-
JPA 환경에서 Querydsl 실행 시 JPQL 생성에 필요한 주요 객체는 무엇일까요?
a. JPAQueryFactory
JPAQueryFactory는EntityManager를 기반으로 동작하며, Querydsl 문법으로 작성된 코드를 JPQL로 변환하여 실행함
-
Querydsl의 가장 큰 장점 중 하나는 무엇일까요?
a. 컴파일 시점에 오류 발견 가능
- 자바 코드로 쿼리를 작성하므로 컴파일러가 문법 오류를 체크해 줌
- 이는 개발 생산성을 높이고 애플리케이션의 안정성을 크게 향상시킴
요약 정리
- Querydsl은 자바 코드로 쿼리를 작성하게 해주어 컴파일 시점에 문법 오류를 잡을 수 있는 강력한 도구임
- Q 타입을 통해 엔티티를 객체처럼 다루며 타입 세이프하게 쿼리를 작성할 수 있음
- 동적 쿼리 처리에 매우 탁월하며,
BooleanBuilder보다는 BooleanExpression을 활용한Where다중 파라미터 방식을 권장함 (가독성 및 재사용성 우수) - JPA를 보완하는 기술이므로 스프링 데이터 JPA와 함께 사용하면 단순 조회와 복잡한 쿼리 모두를 효율적으로 처리할 수 있음