
개요
요즘 QueryDSL을 찾다 보면 OpenFeign QueryDSL이라는 표현을 자주 보게 됩니다.
정확히 말하면, 기존 QueryDSL 프로젝트의 유지보수 체감이 한동안 약했고, 그 흐름 속에서 OpenFeign 조직 아래의 querydsl 포크가 더 활발하게 릴리즈를 이어가고 있는 상태입니다.
즉:
OpenFeign이 QueryDSL을 공식 승계했다고 단정하기보다는실무에서는 OpenFeign 포크를 적극 유지보수 중인 QueryDSL 배포판처럼 사용한다
고 이해하는 편이 더 정확합니다.
먼저 결론
Spring Boot + JPA 기준으로 보면 보통 아래처럼 정리할 수 있습니다.
| 환경 | 추천 방향 |
|---|---|
Spring Boot 2.x / javax |
구 QueryDSL 설정과 더 가까움 |
Spring Boot 3.x / jakarta |
io.github.openfeign.querydsl + jakarta classifier |
| Hibernate 6.x | OpenFeign QueryDSL 6.x |
| Hibernate 7.x / JPA 3.2 | QueryDSL 7.x 검토 |
지금 실무에서 가장 많이 쓰는 조합은 대체로 아래입니다.
- Spring Boot 3.x
- JDK 17
- Hibernate 6.x
- OpenFeign QueryDSL 6.x
핵심 차이
실무에서 말하는 “OpenFeign QueryDSL”은 보통 아래를 의미합니다.
- GitHub 저장소:
OpenFeign/querydsl - Maven 좌표:
io.github.openfeign.querydsl:*
기존 좌표와 차이는 이것입니다.
- 예전:
com.querydsl - 포크:
io.github.openfeign.querydsl
즉, 업그레이드할 때는 버전만이 아니라 groupId도 같이 확인해야 합니다.
Spring Boot 3에서 사용하는 방법
여기서는 Spring Boot 3 + JPA + Hibernate 6 + Jakarta 기준으로 정리합니다.
Gradle
dependencies { |
Maven
<dependencies> |
여기서 핵심은 두 가지입니다.
querydsl-jpa에jakartaclassifier 사용querydsl-apt에도jakartaclassifier 사용
Spring Boot 3는 javax.persistence가 아니라 jakarta.persistence 기반이므로, classifier를 잘못 고르면 QClass 생성이나 import가 꼬이기 쉽습니다.
기본 설정
엔티티가 아래처럼 있다고 가정해보겠습니다.
|
빌드가 정상적으로 끝나면 QMember가 생성됩니다.
QMember member = QMember.member; |
Spring Boot에서는 보통 JPAQueryFactory를 Bean으로 등록해서 사용합니다.
|
실무 예제
1. 동적 검색
가장 기본적인 사용 패턴입니다.
import static com.example.domain.QMember.member; |
이 패턴의 장점은 아래와 같습니다.
- 문자열 기반 JPQL보다 안전함
- null 조건 조합이 자연스러움
- 정렬, 페이징, join으로 확장하기 쉬움
2. @QueryProjection 기반 DTO 조회
DTO 생성자를 QueryDSL projection 대상으로 쓰고 싶다면 @QueryProjection을 사용할 수 있습니다.
import com.querydsl.core.annotations.QueryProjection; |
빌드 후에는 QMemberDto가 생성됩니다.
import static com.example.domain.QMember.member; |
장점은 생성자 파라미터 타입이 컴파일 타임에 검증된다는 점입니다. 반면 DTO가 QueryDSL에 의존하게 된다는 단점도 있습니다.
3. Pageable + projection
목록 API에서는 content query와 count query를 분리하는 방식이 가장 실용적입니다.
public class MemberPageDto { |
public class MemberSearchCondition { |
import com.querydsl.core.types.Projections; |
이 패턴이 많이 쓰이는 이유는 분명합니다.
- 조회용 projection과 count query를 분리해 성능 제어가 쉬움
- 복잡한 join이 들어가도 content query와 count query를 따로 최적화 가능
- Spring Data
Pageable과 자연스럽게 연결 가능
5.0.0 이후 변경점 정리
5.0.0
2021-07-22 공개된 QueryDSL 5.0.0은 큰 전환점이었습니다.
- 최소 Java 8 기준으로 정리
joda-time,guava,jsr305필수 런타임 의존성 제거Fetchable#stream()추가- Java 8 date/time API 사용 기본화
jakarta.*지원 추가- Kotlin code generation 지원
- Java Record 지원
즉, 5.0.0은 현대 Java/Jakarta 환경에서 다시 쓰기 좋게 정리된 메이저 릴리즈라고 볼 수 있습니다.
6.0
2024-01-25 공개된 OpenFeign QueryDSL 6.0의 핵심은 아래입니다.
- Hibernate 6.4 fully integrated
- Querydsl JPA <-> Spring Data repository integration
- CGLIB 제거, ByteBuddy 사용
- Java record projection 생성 개선
- native query 처리 정리
즉, Spring Boot 3 + Hibernate 6 사용자들이 OpenFeign 포크를 더 강하게 보게 된 이유가 여기서 분명해졌습니다.
6.9
2024-11-22 공개된 6.9에서는 Kotlin 진영에 의미 있는 변화가 들어왔습니다.
- KSP module 추가
- KSP example 추가
- incremental processing support 추가
즉, QueryDSL code generation을 KAPT 대신 KSP 기반으로 가져갈 수 있는 길이 열렸습니다.
6.12
2025-06-09 공개된 6.12에서는 실무 체감이 큰 기능 보강이 들어갔습니다.
@QueryProjection의 builder-based Q class generation 지원querydsl-ksp-codegen에서@JdbcTypeCode지원@Converter로 basic type에 매핑된 JPA collection의contains()처리 개선- Querydsl Aggregations에
TypeWrapperFactoryExpression추가
여기서 중요한 건 표현입니다.
QClass Builder가 새로 생겼다라고 넓게 쓰기보다는@QueryProjection의 builder 기반 Q class generation 지원이 추가됐다
라고 쓰는 편이 더 정확합니다.
추가로 릴리즈 노트에 있는 아래 두 항목은 이름만 보면 조금 추상적으로 느껴질 수 있습니다.
TypeWrapperFactoryExpressionlikeToRegex / regexToLike처리 보정
TypeWrapperFactoryExpression은 무슨 의미인가
이 항목은 Querydsl Aggregations에서 집계 결과를 커스텀 숫자 타입으로 더 타입 안전하게 감싸기 위한 기능으로 이해하면 됩니다.
예를 들어 집계 결과는 보통 아래처럼 기본 숫자 타입으로 많이 나옵니다.
LongIntegerBigDecimal
하지만 도메인에서는 아래처럼 래퍼 타입으로 다루고 싶을 수 있습니다.
MoneyScoreQuantity
즉, 예전에는 집계 결과를 먼저 기본 숫자 타입으로 받은 뒤, 애플리케이션 코드에서 다시 값 객체로 감싸는 코드가 필요했다면, 이 영역을 더 타입 안전하게 다루기 위한 기반 기능이 추가된 것으로 보면 됩니다.
실무적으로는 아래 같은 경우에 의미가 있습니다.
- 통계/리포트 조회 결과를 도메인 값 객체로 감싸고 싶은 경우
- 집계 결과의 타입 변환을 DTO 생성 시점에 더 명확하게 가져가고 싶은 경우
- 숫자 타입 변환 실수를 줄이고 싶은 경우
예시
예를 들어 집계 결과를 단순 Long으로 받는 대신, Score라는 값 객체로 감싸고 싶다고 가정해보겠습니다.
public class Score { |
집계 결과를 그냥 받으면 보통 아래처럼 처리하게 됩니다.
Long totalScore = queryFactory |
이런 종류의 코드는 단순해 보이지만, 집계 결과가 많아질수록 매번 기본 숫자 타입을 받아서 다시 값 객체로 감싸는 코드가 반복됩니다.
즉, TypeWrapperFactoryExpression이 의미 있는 지점은 아래처럼 볼 수 있습니다.
- 집계 결과를 단순 숫자가 아니라 도메인 타입으로 다루고 싶을 때
- 숫자 타입 변환을 더 타입 안전하게 가져가고 싶을 때
- Aggregation 결과 projection을 더 깔끔하게 만들고 싶을 때
likeToRegex / regexToLike 처리 보정은 무슨 의미인가
이 항목은 새 기능이라기보다 문자열 패턴 변환의 정확도를 높이는 수정에 가깝습니다.
이름 그대로 아래 변환과 관련 있습니다.
- SQL
LIKE패턴 -> 정규식 변환 - 정규식 패턴 ->
LIKE패턴 변환
문제가 생기기 쉬운 부분은 보통 아래입니다.
%,_같은 LIKE 와일드카드\escape 문자^,$같은 regex anchor
이 처리가 잘못되면 아래 같은 일이 생길 수 있습니다.
- 의도보다 더 많은 문자열이 매칭됨
- 반대로 매칭돼야 할 문자열이 빠짐
- escape가 깨져서 검색 결과가 달라짐
즉, 이 변경은 QueryDSL 내부의 패턴 변환 정확도를 보정한 것으로 보면 됩니다.
예시
예를 들어 사용자가 검색어 패턴을 입력한다고 가정해보겠습니다.
String keyword = "admin_%"; |
이 값을 LIKE 검색 조건에 그대로 넣으면 _와 %가 와일드카드로 해석될 수 있습니다.
queryFactory |
이 경우 의도는 "admin_%"라는 문자열 자체를 찾는 것인데, 실제로는 아래처럼 동작이 달라질 수 있습니다.
%는 임의 길이 문자열_는 한 글자 와일드카드
반대로 내부적으로 LIKE 패턴을 regex로 바꾸거나, regex 성격의 패턴을 LIKE로 바꾸는 과정에서 escape와 anchor 처리가 잘못되면 아래 같은 문제가 생깁니다.
"admin_1"이 매칭돼야 하는데 안 됨"adminXYZ"가 의도치 않게 매칭됨- 시작/끝 anchor가 잘못 적용되어 범위가 달라짐
즉, likeToRegex / regexToLike 보정은 이런 문자열 패턴 변환의 미세한 오류를 줄여주는 변경이라고 보면 됩니다.
6.12 관련 예제
6.12에서 많이 이야기되는 부분은 아래 3가지입니다.
@QueryProjection의 builder-based Q class generation- KSP codegen 개선
- JPA collection
contains()처리 개선
1. builder 기반 projection 예시
아래처럼 builder 패턴을 쓰는 DTO가 있다고 가정해보겠습니다.
public class MemberSummaryDto { |
실무에서는 여전히 아래처럼 Projections.constructor()를 많이 사용합니다.
public List<MemberSummaryDto> findMemberSummaries() { |
또는 조회 후 builder로 직접 변환할 수도 있습니다.
public List<MemberSummaryDto> findMemberSummaries() { |
즉, 6.12의 의미는 “builder 스타일 DTO와 QueryProjection/code generation 쪽 결합이 더 좋아졌다”는 데 있습니다.
2. KSP codegen 예시
Kotlin 프로젝트라면 6.9부터 KSP 기반 codegen을 사용할 수 있고, 6.12에서는 KSP 쪽 기능이 더 보강되었습니다.
예를 들어 Kotlin + JPA 엔티티가 아래처럼 있다고 가정해보겠습니다.
|
KSP 설정은 보통 이런 식으로 가져갑니다.
dependencies { |
즉, Java 프로젝트가 annotationProcessor를 쓴다면 Kotlin 프로젝트는 ksp(...)로 code generation을 붙이는 흐름으로 이해하면 됩니다.
3. JPA collection contains() 예시
6.12 릴리즈 노트에는 @Converter로 basic type에 매핑된 JPA collection의 contains() 처리 개선도 포함되어 있습니다.
예를 들어 태그 리스트를 converter로 저장하는 엔티티가 있다고 가정해보겠습니다.
|
이런 경우 QueryDSL에서 아래처럼 조건을 구성할 수 있습니다.
import static com.example.domain.QArticle.article; |
이 부분은 JPA provider와 매핑 방식에 따라 차이가 있을 수 있는데, 6.12에서는 이런 contains() 처리 쪽이 개선됐다는 점이 실무적으로 의미가 있습니다.
5.6.1 / 6.10.1
2024-12-15 공개된 5.6.1과 6.10.1에서는 CVE-2024-49203 수정이 들어갔습니다.
OpenFeign 보안 공지 기준 영향 범위는 아래였습니다.
- affected:
<= 6.8.0 - patched:
5.6.1,6.10.1,7.0
즉, 오래된 5.x / 6.x를 계속 쓰고 있다면 최소한 이 패치 버전 이상으로는 올리는 것이 안전합니다.
7.0
2025-06-09 공개된 7.0은 또 하나의 메이저 전환점입니다.
- JPA 3.2.0
- Hibernate 7.0
- EclipseLink 5.0
- Java 17 중심 정리
mysema.langdependency 제거- obsolete code / dropped feature 정리
즉, 7.0은 Hibernate 7 + Java 17 시대를 겨냥한 메이저 릴리즈입니다.
선택 가이드
실무적으로는 아래만 기억해도 충분합니다.
- Spring Boot 3라면
jakartaclassifier를 쓴다 - 좌표는
io.github.openfeign.querydsl로 본다 - Hibernate 6이면 QueryDSL 6.x를 우선 고려한다
- 보안 때문에라도 5.6.1 / 6.10.1 / 7.0 미만의 오래된 버전은 피한다
- QueryDSL을 올릴 때는 Hibernate / Spring Boot / JPA 버전을 같이 본다
한 줄 요약
OpenFeign QueryDSL은 기존 QueryDSL의 적극 유지보수 포크로 보는 것이 맞고, Spring Boot 3 환경에서는 보통 io.github.openfeign.querydsl + jakarta classifier + 6.x 계열 조합이 가장 현실적인 선택입니다.
참고
- OpenFeign QueryDSL README: https://github.com/OpenFeign/querydsl
- OpenFeign QueryDSL 6.0 Release: https://github.com/OpenFeign/querydsl/releases/tag/6.0
- OpenFeign QueryDSL 6.9 Release: https://github.com/OpenFeign/querydsl/releases/tag/6.9
- OpenFeign QueryDSL 6.10.1 Release: https://github.com/OpenFeign/querydsl/releases/tag/6.10.1
- OpenFeign QueryDSL 6.12 Release: https://github.com/OpenFeign/querydsl/releases/tag/6.12
- OpenFeign QueryDSL 5.6.1 Release: https://github.com/OpenFeign/querydsl/releases/tag/5.6.1
- OpenFeign QueryDSL 7.0 Release: https://github.com/OpenFeign/querydsl/releases/tag/7.0
- QueryDSL 5.0 Release History: https://querydsl.com/releases.html
- QueryDSL Security Advisory CVE-2024-49203: https://github.com/OpenFeign/querydsl/security/advisories/GHSA-6q3q-6v5j-h6vg