catsridingCATSRIDING|OCEANWAVES
Dev

Querydsl 적용하기

jynn@catsriding.com
Nov 20, 2023
Published byJynn
999
Querydsl 적용하기

Integrate Spring Data JPA with Querydsl

Querydsl Framework는 초기 설정이 다소 복잡하지만, ORM 기술을 보다 효과적으로 활용할 수 있게 해줍니다. 그래서 새로운 프로젝트를 시작할 때 항상 Spring Data JPA와 함께 구성합니다.

이번 포스팅에서는 프로젝트에 Querydsl을 적용하고 Spring Data JPA와 통합하는 방법을 살펴보겠습니다.

Prerequisites

Spring Initializr를 통해 Spring Boot 프로젝트를 생성합니다:

build.gradle
dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    compileOnly 'org.projectlombok:lombok'
    runtimeOnly 'com.mysql:mysql-connector-j'
    annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
    annotationProcessor 'org.projectlombok:lombok'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
  • Java 17
  • Gradle
  • MySQL 8
  • Dependencies
    • Spring Boot 3.1.4
    • Spring Data JPA
    • MySQL Driver
    • Lombok

Querydsl이 정상적으로 동작하는지 확인하기 위해 데이터베이스 테이블을 추가합니다.

MySQL
create table ACCOUNTS  
(  
    id       bigint auto_increment primary key,  
    username varchar(255) not null,  
    password varchar(255) not null  
);

󰖷 application.yml 파일에 DataSource 정보를 추가하여 데이터베이스와 연결합니다.

application.yml
spring:  
  datasource:  
    jdbc-url: jdbc:mysql://localhost:3306/querydsl  
    username: catsriding  
    password: catsriding  

그리고 생성한 ACCOUNTS 테이블을 JPA Entity로 매핑합니다.

Account.java
@Entity  
@Builder  
@NoArgsConstructor  
@AllArgsConstructor  
@Table(name = "ACCOUNTS")  
public class Account {  
  
    @Id  
    @GeneratedValue(strategy = GenerationType.IDENTITY)  
    @Column(name = "id", nullable = false)  
    private Long id;  
    
    @Column(name = "username", nullable = false)  
    private String username;  
    
    @Column(name = "password", nullable = false)  
    private String password;  
  
}

애플리케이션을 실행하여 정상적으로 동작하는지 확인한 후 다음 단계를 진행합니다.

Configuring Querydsl

Querydsl을 사용하면 복잡한 SQL 쿼리를 간단하고 안전하게 작성할 수 있습니다. 이를 위해서는 먼저 Querydsl의 의존성을 프로젝트에 추가해야 합니다. 의존성을 추가하면 Querydsl이 제공하는 다양한 기능을 사용할 수 있게 되며, JPA와의 통합도 가능해집니다.

아래와 같이  build.gradle 파일을 열고 필요한 Querydsl 의존성을 추가합니다:

build.gradle
dependencies {  
+   implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta'  // 1️⃣
+   annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jakarta"  // 2️⃣
+   annotationProcessor "jakarta.annotation:jakarta.annotation-api"  // 3️⃣
+   annotationProcessor "jakarta.persistence:jakarta.persistence-api"  // 4️⃣

    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    compileOnly 'org.projectlombok:lombok'
    runtimeOnly 'com.mysql:mysql-connector-j'
    annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
    annotationProcessor 'org.projectlombok:lombok'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
  • 1️⃣ Querydsl의 JPA 모듈을 프로젝트에 추가합니다. 이는 Querydsl의 핵심 도구로, 타입-세이프한 쿼리를 작성하는 데 사용되며, JPA와 호환됩니다.
  • 2️⃣ Querydsl의 APT(Annotation Processing Tool) 모듈을 추가합니다. 이 도구는 Querydsl 실행에 필요한 메타모델을 생성합니다.
  • 3️⃣ Jakarta Annotation API를 추가합니다. 이는 Jakarta EE 플랫폼에서 사용되는 공통 어노테이션을 정의하며, Querydsl이 어노테이션을 처리할 때 필요합니다.
  • 4️⃣ Jakarta Persistence API를 추가합니다. 이 API는 JPA(Java Persistence API)를 표준화하였으며, Querydsl이 JPA와 함께 작동하는 데 필요합니다.

Querydsl은 JPA의 EntityManager를 사용하여 Java 코드를 SQL 쿼리로 변환하는 역할을 합니다. 이를 위해 EntityManager를 주입받아 사용하는 JPAQueryFactory 클래스를 활용합니다. 이 JPAQueryFactory를 리포지토리 구현체에서 직접 인스턴스화하는 대신 Spring Bean으로 등록하면, 리포지토리에서 편리하게 주입받아 사용할 수 있습니다:

QuerydslConfig.java
@Configuration  
public class QuerydslConfig {  
  
    @PersistenceContext  
    private EntityManager entityManager;  
  
    @Bean  
    public JPAQueryFactory jpaQueryFactory() {  
        return new JPAQueryFactory(entityManager);  
    }  
}
  • @PersistenceContext: JPA의 EntityManager를 주입받기 위한 어노테이션입니다.
  • JPAQueryFactory: Querydsl을 사용하여 타입-세이프한 쿼리를 작성하기 위한 클래스입니다.

다음은 Querydsl에서 SQL을 작성할때 필요한 Q-Type 클래스를 생성하기 위해서, IntelliJ IDEA나 Gradle 명령어를 통해 프로젝트를 빌드합니다:

Terminal
$ gradle build

프로젝트 빌드가 성공하면  build 디렉토리 하위에 Querydsl의 Q-Type 파일이 생성됩니다. Q는 Query의 줄임말이며 QAccount와 같이 Entity 클래스명 앞에 접두사로 붙습니다.

Console
$ exa -alhFT
drwxr-xr-x@    - catsriding  9 Dec 01:26  ./
drwxr-xr-x@    - catsriding  9 Dec 01:26  ├── build/
drwxr-xr-x@    - catsriding  9 Dec 01:26  │  ├── generated/
drwxr-xr-x@    - catsriding  9 Dec 01:26  │  │  └── sources/
drwxr-xr-x@    - catsriding  9 Dec 01:26  │  │     ├── annotationProcessor/
drwxr-xr-x     - catsriding  9 Dec 01:31  │  │     │  └── java/
drwxr-xr-x     - catsriding  9 Dec 01:31  │  │     │     └── main/
drwxr-xr-x     - catsriding  9 Dec 01:31  │  │     │        └── app/
drwxr-xr-x     - catsriding  9 Dec 01:31  │  │     │           └── catsriding/
drwxr-xr-x     - catsriding  9 Dec 01:31  │  │     │              └── querydsl/
drwxr-xr-x     - catsriding  9 Dec 01:31  │  │     │                 └── entity/
.rw-r--r--   967 catsriding  9 Dec 01:31  │  │     │                    └── QAccount.java

IntelliJ IDEA는  build 디렉토리를 기본적으로 프로젝트 소스에서 제외합니다. IntelliJ에서 Q-Type을 import 할 수 없다면 Q-Type이 위치한 디렉토리가 제외(Excluded) 되었는지 확인하고, 해당 디렉토리를 Generated Sources Root로 변경합니다.

integrate-spring-data-jpa-with-querydsl-0.png

Integrating JpaRepository

Spring Data JPA를 사용할 때 일반적으로 아래와 같이 JpaRepository<T, ID> 인터페이스를 확장한 리포지토리를 구성하게 됩니다.

AccountRepository.java
public interface AccountRepository extends JpaRepository<Account, Long> {  
  
}

Querydsl을 통합하여 사용하기 위해서는 JpaRepository 인터페이스를 확장한 Querydsl 전용 인터페이스를 추가하고, 이를 구현하는 클래스를 만들어야 합니다. 이렇게 하는 이유는 Spring Data JPA에서 제공하는 CRUD 기능과 Querydsl의 기능을 모두 활용할 수 있게 하기 위해서입니다.

먼저, Querydsl 전용 인터페이스를 추가합니다. 인터페이스 이름은 보통 Support, Helper, Extension 등의 접미사를 사용합니다. 저는 개인적으로 Extension 접미사를 주로 사용하는 편입니다:

AccountRepositoryExtension.java
public interface AccountRepositoryExtension {

}

그 다음, 실제 SQL 쿼리를 작성하게 될 인터페이스 구현 객체를 생성합니다. 이 구현 객체의 이름은 반드시 마지막에 Impl 접미사를 붙여야 합니다. 이는 Spring Data JPA가 해당 클래스를 자동으로 인식하게 합니다. 그리고 위에서 Spring Bean으로 등록한 JPAQueryFactory를 주입받습니다.

AccountRepositoryImpl.java
@RequiredArgsConstructor  
public class AccountRepositoryImpl implements AccountRepositoryExtension {  
  
    private final JPAQueryFactory queryFactory;  

}

마지막으로, AccountRepository로 돌아가서 Querydsl 리포지토리 인터페이스를 상속 목록에 추가합니다:

AccountRepository.java
public interface AccountRepository extends JpaRepository<Account, Long>, AccountRepositoryExtension {  

}

이렇게 하면 Querydsl을 프로젝트에 적용하고 Spring Data JPA와 통합하는 작업이 완료됩니다. Querydsl을 활용하여 간단한 조회 쿼리를 작성해보도록 하겠습니다.

먼저 Querydsl 전용 인터페이스에 메서드를 정의합니다:

AccountRepositoryExtension.java
public interface AccountRepositoryExtension {

+   List<Account> fetchAll();

}

그리고 구현 객체에서 이 메서드를 오버라이드하여 SQL 쿼리를 Java 코드로 작성합니다:

AccountRepository.java
import static app.catsriding.querydsl.entity.account.QAccount.account;

@RequiredArgsConstructor
public class AccountRepositoryImpl implements AccountRepositoryExtension {

    private final JPAQueryFactory queryFactory;

+   @Override
+   public List<Account> fetchAll() {
+       return queryFactory
+               .select(account)
+               .from(account)
+               .orderBy(account.id.desc())
+               .fetch();
+   }

}

이와 같이 Querydsl을 활용하면 Java 언어를 사용하기 때문에 IntelliJ IDEA의 자동완성 기능을 적극적으로 지원받을 수 있으며, 타입 세이프하게 SQL 쿼리를 작성할 수 있어 마치 애플리케이션 로직을 작성하는 듯한 진정한 ORM 경험을 제공합니다. Querydsl의 가장 강력한 기능 중 하나는 동적 쿼리 작성 능력인데, 이는 Querydsl 동적 쿼리 작성하기에서 자세히 다루어보겠습니다. 🐬


  • Spring
  • JPA
  • Querydsl