QueryDSL

전현진·2025년 5월 9일

Spring

목록 보기
14/14

QueryDSL ?

오픈소스 프로젝트로 JPQL을 Java 코드(SQL 형식의 쿼리)로 Type-Safe 하게 작성할 수 있도록 하는 라이브러리다.

@Query("SELECT t FROM Todo t " +
            "LEFT JOIN t.user " +
            "WHERE t.id = :todoId")
    Optional<Todo> findByIdWithUser(@Param("todoId") Long todoId);

해당 위 쿼리는 JPQL로 작성되어 있다. QueryDSL로 만들어 보자

QueryDSL을 사용하는 이유

1. 휴먼 에러 방지 :
기존 JPQL은 쿼리를 문자열로 작성해야한다. 만약 오타가 있거나 잘못 작성한다해도 컴파일 시점에 에러가 발생하지 않고 런타임 시점에 발생하기 때문에 실행시키기 전에는 잘못된 부분을 알 수 없다.
하지만 QueryDSL은 자바 코드로 쿼리를 작성하기 때문에 컴파일 시점에 에러를 잡을 수 있다는 큰 장점이 있다.

2. 중복된 조건식 재활용 가능 :
기존에 같은 조건에 추가로 조건이 생긴다면, 새로운 JPQL을 작성해 작업해야 되어 조건의 중복사용 불가하여 상당히 불편하엿다.
그렇기에 QueryDSL는 Expression과 JPAQueryFactory로 나누어 중복사용이 가능해지며 편리함을 추구하게 되었다.

3. 보기 쉬운 동적쿼리 :
JPQL는 조건이 많으면 많을 수록 읽기 어려워 진다는 단점을 가지고 있었는데, QueryDSL 바뀌면서 조건이 많아도 읽기 수월하고 그만큼 깔끔한 코드를 유지 할 수 있다.

QueryDSL 설정

주의 : 설정은 환경마다 다르며 그렇기에 설정 역시 다를 수 있다.
작성자 작업 환경 : SpringBoot 3.4.0, Java 17

build.gradle

    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"

위 처럼 작성 하면 Entity의 필드를 Q클래스로 자동으로 생성 해 준다.

Q Class

Querydsl을 사용하면 기본적으로 QClass라는 자식이 생성된다. 이는 엔티티 클래스의 메타 데이터를 가지고 있는 클래스이다.

  • 컴파일 단계에서 엔티티를 기반으로 생성되며, JPA_APT(JPAAnnotationProcessTool)가 @Entity와 같은 특정 어노테이션을 찾고 해당 클래스를 분석해서 만든다.
  • 엔티티 클래스의 메타 정보를 담고 있는 클래스로, Querydsl은 이를 이용하여 타입 안정성(Type safe)를 보장하며 쿼리를 작성할 수 있게 된다.
  • 사용하여 쿼리를 작성하면 엔티티 속성을 직접 참조하고 조합하여 쿼리를 구성할 수 있다.
  • 사용하면 컴파일 시점에 오류를 확인할 수 있다.
  • 엔티티 속성의 타입을 정확하게 표현하므로, 타입에 맞지 않는 연산이나 비교를 시도하면 컴파일러가 오류를 감지할 수 있다.
  • IDE의 자동완성 기능을 활용하여 속성 이름을 직접 기억하지 않고 쿼리 작성을 보다 편리하게 할 수 있다.

QueryDSL 코드

1. JpaQueryFactory 빈(Bean) 등록

@Configuration
public class QueryDslConfig {
    @PersistenceContext
    private EntityManager entityManager;

    @Bean
    public JPAQueryFactory jpaQueryFactory() {
        return new JPAQueryFactory(entityManager);
    }
}
  • 빈으로 등록된 jpaQueryFactory를 이용하여 Q클래스를 사용 할 수 있도록 한다.



2. JPA 레포지터리 선언

public interface TodoRepository extends JpaRepository<Todo, Long>, TodoRepositoryQueryDsl {
}
  • TodoRepositoryQueryDsl 상속시켜 레파지토리에서 사용 가능 하게 만든다.

  • 이에 JPQL과 QueryDSL는 동시에 사용가능 하여 간단한 쿼리는 JPQL, 복잡한 쿼리는 QueryDSL로 작성하기에 용이하다.



3. TodoRepositoryQueryDsl 인터페이스 생성

public interface TodoRepositoryQueryDsl {
    Optional<Todo> findByIdWithUser(Long todoId);
}
  • Spring Data JPA가 커스텀 구현체를 자동으로 인식 할 수 있음

  • 코드를 역할별로 나눌 수 있고, 유지보수에 유리함

  • 확장성이 높으며 단위 테스트에 좋다



4. TodoRepositoryQueryDslImpl 클래스 생성

@RequiredArgsConstructor
public class TodoRepositoryQueryDslImpl implements TodoRepositoryQueryDsl {

    private final JPAQueryFactory jpaQueryFactory;

    QTodo qTodo = QTodo.todo;

    @Override
    public Optional<Todo> findByIdWithUser(Long todoId) {

        return Optional.ofNullable(jpaQueryFactory
            .select(qTodo)
            .from(qTodo)
            .join(qTodo.user)
            .fetchJoin()
            .where(qTodo.id.eq(todoId))
            .fetchOne());
    }
}
  • 문자열로 표현하던 JPQL를 대체하여 QueryDSL를 사용해서 쿼리스럽게 작성 할 수 있다.

  • 가독성이 좋으며 휴먼 에러를 컴파일러에서 잡아 줄 수 있다.



정리

쿼리를 보면 레포지토리에 상속하기에 JPQL과 QueryDSL를 같이 쓸 수 있다.
그렇기에 간단한 조건이라면 JPQL 구현하면 빠르게 작성 할 수 있고, 조건이 많거나 중복되는 조건이 있다면 QueryDSL로 작성해서 처리한다면 상당한 도움이 될 것이다.




참고

https://velog.io/@evan523/JPA-QueryDSL
https://www.youtube.com/watch?v=Dz-46mPfkGo&t=269s
https://turtle-codingstudy.tistory.com/54
챗GPT

profile
안녕하세요

0개의 댓글