JPA에서 개발자가 원하는 엔티티를 얻으려면, JPQL을 작성하고 EntityManager에 전달하여 실행하면 된다.
여기서 한 가지가 문제가 있는데, JPQL이 문자열이라는 점이다. JPQL이 문자열이기에 타입안정성 체크도 어렵고 동적쿼리 작성도 직관적이지 못하다. 이런 문제를 해결하기 위해, QueryDSL 프레임워크를 사용한다.
개발자가 직접 JPQL을 작성하지 않고 QueryDSL에게 요청하는 방식이다. 개발자는 QueryDSL이 제공하는 스펙에 따라 JPQL 데이터를 세팅하면 된다. QueryDSL의 프로세스는 개발자가 세팅한 데이터를 토대로 JPQL을 생성하고 EntityManager로 쿼리를 실행하여 결과를 반환한다. 이로써 개발자는 데이터 세팅에만 관여하고, JPQL 생성은 QueryDSL이 자동으로 하니, 타입 안정성 체크가 가능해지고 직관적인 동적쿼리생성이 가능해진다.
그럼 QueryDSL이 어떤 방식으로 위 과정을 구현하는지 구체적으로 파헤쳐 보자.
빌더 패턴 ( Builder Pattern )
QueryDSL은 빌더 패턴 구조로 이루어져 있다.
빌더 패턴은 객체의 데이터 세팅에 사용되는 디자인 패턴이다. 생성자 매개변수나 setter함수를 이용한 데이터 세팅은 유연성이 떨어진다. QueryDSL은 다양한 형태의 JPQL을 만들어야 한다. 여러 형태의 JPQL을 만들기 위해, 클라이언트로부터 다양한 데이터를 세팅 받는다. 특정 데이터는 세팅되고 특정 데이터는 세팅되지 않을 수 있다. 빌더 패턴은 이러한 다양한 경우에 유연하게 대처할 수 있는 디자인 패턴이다.
이러한 이유로 빌더 패턴은 QueryDSL에 핵심 구조를 이룬다. 빌더 패턴에 대한 지식이 부족하면 이해하기 어려울 수 있으니 선수학습을 추천한다.
- QueryDSL 작성예시
public List<Member> searchMember(MemberSearchCondition condition){
QMember qMember = QMember.member; //Q타입 클래스 객체 생성
return queryFactory
.select(qMember) // select 메소드
.from(qMember) // from 메소드
.leftJoin(member.team, team) // leftJoin메소드
.where( // where 메소드
usernameEq(condition.getUsername()),
teamNameEq(condition.getTeamName()),
ageGoe(condition.getAgeGoe()),
ageLoe(condition.getAgeLoe())
)
.fetch(); //fetch메소드
}
위 코드에서 두 가지를 특징을 알아야 한다.
1. Q타입클래스
2. 메소드 체이닝 방식
- Q타입클래스
클라이언트는 JPQL을 실행하여 원하는 데이터가 담긴 엔티티 객체( 혹은 일반객체 )를 반환 받으려 한다. 원하는 엔티티 정보를 QueryDSL에 넘겨야 하는데, 이때 사용되는 클래스가 Q타입클래스이다. Q타입클래스는 컴파일 과정에서 이미 생성되어 있는 엔티티를 토대로 자동으로 만들어 진다.
QMember qMember = QMember.member;
그래서 클라이언트는 Q타입클래스 객체를 위와같이 쉽게 생성할 수 있다. QueryDSL은 JPA와 분리되어 있는 프레임워크이다. 엔티티를 직접 사용하면 JPA에 종속되므로 Q타입클래스 같은 QueryDSL 전용 클래스를 만들어 사용한다.
- 메소드 체이닝 방식
메소드 체이닝은 빌더 패턴의 특징이다. 위 코드를 보면 select 메소드, from 메소드, leftJoin 메소드, where메소드가 '.' 연산자로 체인처럼 연결되어 있음을 확인할 수 있다. 클라이언트는 JPQL 생성에 필요한 데이터를 세팅해야 한다. 빌더 패턴은 데이터 세팅을 select, from, leftjoin, where 순으로 할 수 있도록 제공한다. 이것이 QueryDSL이 가독성이 높은 이유다. 데이터 세팅을 단순히 생성자의 매개변수나 setter함수로 한다면 이러한 직관적인 구조가 나올 수 없다.
여기까지 QueryDSL 동작원리를 간단히 알아보았다.
정리하면, QueryDSL은 JPQL을 대신 생성하여, 타입안정성을 올리고 가독성 높은 동적쿼리생성을 가능하게 만든다. 클라이언트는 그저 JPQL 생성에 필요한 데이터만 세팅하면 된다. JPQL은 굉장히 다양한 형태로 존재하기에, 다양한 경우를 유연하게 지원할 수있는 구조가 필요하다. QueryDSL은 빌더 패턴 구조를 활용하여 이를 지원한다.
다음 포스팅에서는 QueryDSL 내부구조를 파헤쳐보겠다.
'JPA > QueryDSL' 카테고리의 다른 글
[QueryDSL] Expression( 표현 ) (0) | 2023.08.04 |
---|---|
[QueryDSL] QueryDSL 동작원리(3) - fetch (0) | 2023.08.02 |
[QueryDSL] QueryDSL 동작원리(2) - 메타데이터 설정하기 (0) | 2023.08.01 |
[QueryDSL] QueryDSL 설정하기 ( SpringBoot 2.6이상, SpringBoot 3.x ) (3) | 2023.07.26 |
[QueryDSL] QueryDSL이란? (0) | 2023.07.21 |