스프링(Spring), 스프링부트(SpringBoot)/스프링부트(SpringBoot) 기초

스프링 부트 핵심 가이드(장정우 지음) - Spring Data JPA 활용 2(@Query 어노테이션 사용하기, QueryDSL 적용하기)

개발학생 2025. 8. 22. 15:52
반응형

*책 내용과 다르게, 다음과 같은 환경에서 프로젝트 생성  

 

[Java] 차근차근 Java 설치하기 (JDK17, Window 11)

자바 개발 도구 설치 방법에 대해서 알아보겠습니다. Java17은 LTS(Long Term Support : 장기 지원) 릴리즈로 1년 후까지 기술 지원 및 버그를 개선한 서비스를 제공받을 수 있습니다. 업데이트 버전을 꾸

yungenie.tistory.com

 

[Windows] Spring Tool Suite 4(STS 4) 다운로드 및 설치

STS란?Spring Tool Suite(STS)는 스프링 프로젝트를 생성하고, 개발할 수 있게 해주는 도구입니다. STS 설치 과정에 대해 설명드리겠습니다. 설치 파일 다운로드STS 공식 사이트에서 설치 파일을 다운로

priming.tistory.com

 

MySQL :: Download MySQL Community Server

Select Version: 9.3.0 Innovation 8.4.5 LTS 8.0.42 Select Operating System: Select Operating System… Microsoft Windows Ubuntu Linux Debian Linux SUSE Linux Enterprise Server Red Hat Enterprise Linux / Oracle Linux Fedora Linux - Generic Oracle Solaris mac

dev.mysql.com

  • Gradle

**STS에서 Gradle 프로젝트 생성한 과정 

*** 함께 보면 좋은 글

2025.07.10 - [스프링(Spring), 스프링부트(SpringBoot)/스프링부트(SpringBoot) 기초] - 스프링 부트 핵심 가이드(장정우 지음) - 스프링 부트 개요

 

스프링 부트 핵심 가이드(장정우 지음) - 스프링 부트 개요

1. 스프링 프레임워크자바(Java) 기반 애플리케이션 프레임워크로, 엔터프라이즈급(기업 환경 대상 개발) 애플리케이션을 위한 다양한 기능 제공-> 오픈소스 경량급 애플리케이션 프레임워크로

keep-programming-study.tistory.com

2025.07.11 - [스프링(Spring), 스프링부트(SpringBoot)/스프링부트(SpringBoot) 기초] - 스프링 부트 핵심 가이드(장정우 지음) - 개발에 앞서 알면 좋은 기초 지식

 

스프링 부트 핵심 가이드(장정우 지음) - 개발에 앞서 알면 좋은 기초 지식

1. 서버 간 통신마이크로서비스 아키텍처에서 한 서버가 다른 서버에 통신을 요청하는 것을 의미-> 한 대는 서버/다른 한 대는 클라이언트가 됨 가장 많이 사용되는 방식은 HTTP/HTTPS 방식(TCP/IP, SOA

keep-programming-study.tistory.com

2025.08.15 - [스프링(Spring), 스프링부트(SpringBoot)/스프링부트(SpringBoot) 기초] - 스프링 부트 핵심 가이드(장정우 지음) - Spring Data JPA 활용 1(JPQL, 쿼리 메서드 살펴보기, 정렬과 페이징 처리)

 

스프링 부트 핵심 가이드(장정우 지음) - Spring Data JPA 활용 1(JPQL, 쿼리 메서드 살펴보기, 정렬과

*책 내용과 다르게, 다음과 같은 환경에서 프로젝트 생성 Windows11(윈도우 11) 환경자바 JDK 17 버전 설치 https://yungenie.tistory.com/11 [Java] 차근차근 Java 설치하기 (JDK17, Window 11)자바 개발 도구 설치 방법

keep-programming-study.tistory.com

 

1. @Query 어노테이션 사용하기

  • 직접 JPQL을 작성하여, JPA 구현체에서 자동으로 쿼리 문장을 해석하고 실행
  • 어떤 데이터베이스를 선택했든 데이터베이스에 특화된 SQL을 작성할 수 있으며, 주로 튜닝된 쿼리 사용

@Query를 사용하여 ProductRepository.java에 메서드 작성

// Product 엔티티 중에서 name이 특정 값과 일치하는 것들을 찾아서 리스트로 반환
// :name은 JPQL 쿼리에서 사용하는 이름 기반 파라미터
// @Param("name")은 자바 메서드의 파라미터를 :name에 연결
@Query("SELECT FROM Product WHERE Product.name = :name")
List<Product> findByName(@Param("name") String name);

// 위의 findByName 메서드를 호출했을 때 나오는 하이버네이트 로그
Hibernate:
    select
    	product0_.number as number1_0_,
        product0_.created_at as created_2_0_,
        product0_.name as name3_0_,
        product0_.price as price4_0_,
        product0_.stock as stock5_0_,
        product0_.updated_at as updated_6_0_
    from
        product product0_
    where
    	product0_.name=?

// Product 엔티티에서 엔티티 타입이 아닌, 원하는 특정 칼럼들만 추출
@Query("SELECT p.name, p.price, p.stock FROM Product p WHERE p.name = :name")
List<Object[]> findByName2(@Param("name") String name);

// 위의 findByName2 메서드를 호출했을 때 나오는 하이버네이트 로그
Hibernate:
    select
        product0_.name as col_0_0_,
        product0_.price as col_1_0_,
        product0_.stock as col_2_0_,
    from
        product product0_
    where
        product0_.name=?

 

2. QueryDSL 적용하기

1) QueryDSL의 특징

  • 메서드 이름을 기반으로 생성하는 JPQL의 한계는 @Query 어노테이션을 통해 대부분 해소할 수 있지만,
    직접 문자열을 입력하기 때문에 컴파일 시점에 에러를 잡지 못하고 런타임 에러가 발생할 수 있음
    -> 이러한 문제를 해결하기 위해, 문자열이 아니라 코드로 쿼리를 작성하는 QueryDSL 사용
  • 정적 타입을 통해 SQL과 같은 쿼리를 생성할 수 있도록 지원하는 프레임워크
  • 문자열이나 XML 파일 대신, 자체 제공하는 플루언트(Fluent) API를 활용하여 쿼리 생성

QueryDSL의 장점

  • IDE가 제공하는 코드 자동 완성 기능 사용 가능
  • 문법적으로 잘못된 쿼리를 허용하지 않으므로, 정상적으로 작성된 QueryDSL은 문법 오류를 발생시키지 않음
  • 고정된 SQL 쿼리를 작성하지 않으므로 동적으로 쿼리 생성 가능
  • 코드로 작성하므로 가독성 및 생산성 향상
  • 도메인 타입과 프로퍼티를 안전하게 참조할 수 있음

2) QueryDSL을 사용하기 위한 프로젝트 설정

  • Gradle은 annotationProcessor라는 전용 configuration scope를 제공해서
    별도의 APT 플러그인 없이도 annotation processing을 깔끔하게 처리할 수 있음
    -> APT(Annotation Processing Tool)은 어노테이션으로 정의된 코드를 기반으로 새로운 코드를 생성하는 기능
  • Maven과 Gradle에서 QueryDSL 사용 시 설정 차이
항목  Maven  Gradle
Annotation Processor 설정 maven-compiler-plugin의 annotationProcessorPaths 사용 annotationProcessor 의존성으로 설정
QueryDSL APT 설정 querydsl-apt를 annotationProcessorPaths에 명시 annotationProcessor 'com.querydsl:querydsl-apt:...'
생성된 Q 클래스 경로 설정 target/generated-sources/java 등으로 지정 build/generated/querydsl 등으로 지정
소스 디렉토리 추가 <sourceDirectory> 또는 IDE 설정 필요 sourceSets.main.java.srcDirs += ...

(1) build.gradle에 코드 추가

plugins {
    ... 생략 ...
    // QueryDSL (JPA) 플러그인
    id 'com.ewerk.gradle.plugins.querydsl' version '1.0.10'
}

.. 생략 ...
// QueryDSL 설정
def querydslDir = "$buildDir/generated/querydsl"

querydsl {
    jpa = true
    querydslSourcesDir = querydslDir
}

sourceSets {
    main.java.srcDirs += querydslDir
}

compileJava {
    options.annotationProcessorGeneratedSourcesDirectory = file(querydslDir)
}

dependencies {
    ... 생략 ...
    // QueryDSL 관련 의존성
    implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta'
    annotationProcessor 'com.querydsl:querydsl-apt:5.0.0:jakarta'
    annotationProcessor 'jakarta.persistence:jakarta.persistence-api:3.1.0'
}

(2) build.gradle 우클릭 후 Gradle -> Refresh Gradle Project 클릭

 

3) 기본적인 QueryDSL 사용

(1) STS에서 Q클래스 인식이 안되는 문제

  • STS는 build/generated/querydsl 디렉토리를 자동으로 소스 디렉토리로 인식하지 않으므로 수동으로 추가
  1. STS에서 프로젝트 우클릭 → Properties 클릭
  2. Properties 창에서 Java Build Path → Source 탭으로 이동
  3. Add Folder 클릭
  4. build/generated/querydsl 선택
  5. Apply and Close
  • 그래도 안 되면?
    • STS 재시작
    • Project > Clean 실행
    • Gradle > Refresh Gradle Project 실행

(2) 테스트 코드 새로 작성 중 QProduct가 import가 안되는 문제... cmd에서 gradlew clean build 하는것조차안됨 
-> gradlew clean build 후에 gradlew compileQuerydsl가 되어야함... 

    • compileQuerydsl 태스크가 Spring, Jakarta, Lombok 등 거의 모든 의존성을 못 찾고 있음
      -> 즉, compileQuerydsl이 실행될 때 필요한 클래스 경로(classpath)가 제대로 설정되지 않은 상태.. 해결을 위해 여러가지 시도 
      • QueryDSL 추가 시 gradlew clean build가 안되는문제 해결해야함!! 

C:\sts\workspace\study>gradlew clean build

> Task :compileQuerydsl FAILED

...

* What went wrong:
Execution failed for task ':compileQuerydsl'.
> Compilation failed; see the compiler output below.
  C:\sts\workspace\study\src\main\java\com\example\demo\service\impl\ProductServiceImpl.java:22: error: cannot find symbol
        private final Logger LOGGER = LoggerFactory.getLogger(ProductServiceImpl.class);
        
    symbol:   class Logger
    location: class ProductServiceImpl
  C:\sts\workspace\study\src\main\java\com\example\demo\jpa\data\dao\impl\ProductDAOImpl.java:28: error: cannot find symbol
      @Autowired
       ^
    symbol:   class Autowired
    location: class ProductDAOImpl
  C:\sts\workspace\study\src\main\java\com\example\demo\jpa\data\entity\Product.java:40: error: cannot find symbol
      @Id
       ^
    symbol:   class Id
    location: class Product
  C:\sts\workspace\study\src\main\java\com\example\demo\jpa\data\entity\Product.java:45: error: cannot find symbol
      @GeneratedValue(strategy = GenerationType.IDENTITY)
       ^
    symbol:   class GeneratedValue
    location: class Product
  C:\sts\workspace\study\src\main\java\com\example\demo\jpa\data\entity\Product.java:45: error: cannot find symbol
      @GeneratedValue(strategy = GenerationType.IDENTITY)
                                 ^
    symbol:   variable GenerationType
    location: class Product
  C:\sts\workspace\study\src\main\java\com\example\demo\jpa\data\entity\Product.java:50: error: cannot find symbol
      @Column(nullable = false)
       ^
    symbol:   class Column
    location: class Product
  C:\sts\workspace\study\src\main\java\com\example\demo\jpa\data\entity\Product.java:54: error: cannot find symbol
      @Column(nullable = false)
       ^
    symbol:   class Column
    location: class Product
  C:\sts\workspace\study\src\main\java\com\example\demo\jpa\data\entity\Product.java:58: error: cannot find symbol
      @Column(nullable = false)
       ^
    symbol:   class Column
    location: class Product
  C:\sts\workspace\study\src\main\java\com\example\demo\jpa\data\dto\ProductDTO.java:8: error: cannot find symbol
        @JsonProperty("name")
         ^
    symbol:   class JsonProperty
    location: class ProductDTO
  C:\sts\workspace\study\src\main\java\com\example\demo\jpa\data\dto\ProductDTO.java:11: error: cannot find symbol
      @JsonProperty("price")
       ^
    symbol:   class JsonProperty
    location: class ProductDTO
  C:\sts\workspace\study\src\main\java\com\example\demo\jpa\data\dto\ProductDTO.java:14: error: cannot find symbol
      @JsonProperty("stock")
       ^
    symbol:   class JsonProperty
    location: class ProductDTO
  C:\sts\workspace\study\src\main\java\com\example\demo\service\impl\ProductServiceImpl.java:29: error: cannot find symbol
      @Autowired
       ^
    symbol:   class Autowired
    location: class ProductServiceImpl
  C:\sts\workspace\study\src\main\java\com\example\demo\jpa\data\dao\impl\ProductDAOImpl.java:19: error: cannot find symbol
  @Component // Spring이 이 클래스를 Bean으로 등록할 수 있도록 지정
   ^
    symbol: class Component
  C:\sts\workspace\study\src\main\java\com\example\demo\jpa\data\repository\ProductRepository.java:7: error: cannot find symbol
  public interface ProductRepository extends JpaRepository<Product, Long> {
                                             ^
    symbol: class JpaRepository
  C:\sts\workspace\study\src\main\java\com\example\demo\jpa\data\entity\Product.java:21: error: cannot find symbol
  @Getter
   ^
    symbol: class Getter
  C:\sts\workspace\study\src\main\java\com\example\demo\jpa\data\entity\Product.java:23: error: cannot find symbol
  @Setter
   ^
    symbol: class Setter
  C:\sts\workspace\study\src\main\java\com\example\demo\jpa\data\entity\Product.java:25: error: cannot find symbol
  @NoArgsConstructor
   ^
    symbol: class NoArgsConstructor
  C:\sts\workspace\study\src\main\java\com\example\demo\jpa\data\entity\Product.java:27: error: cannot find symbol
  @AllArgsConstructor
   ^
    symbol: class AllArgsConstructor
  C:\sts\workspace\study\src\main\java\com\example\demo\jpa\data\entity\Product.java:29: error: cannot find symbol
  @Entity
   ^
    symbol: class Entity
  C:\sts\workspace\study\src\main\java\com\example\demo\jpa\data\entity\Product.java:31: error: cannot find symbol
  @Builder
   ^
    symbol: class Builder
  C:\sts\workspace\study\src\main\java\com\example\demo\jpa\data\entity\Product.java:33: error: cannot find symbol
  @EqualsAndHashCode
   ^
    symbol: class EqualsAndHashCode
  C:\sts\workspace\study\src\main\java\com\example\demo\jpa\data\entity\Product.java:35: error: cannot find symbol
  @ToString(exclude="name")
   ^
    symbol: class ToString
  C:\sts\workspace\study\src\main\java\com\example\demo\jpa\data\entity\Product.java:37: error: cannot find symbol
  @Table(name="product")
   ^
    symbol: class Table
  C:\sts\workspace\study\src\main\java\com\example\demo\service\impl\ProductServiceImpl.java:19: error: cannot find symbol
  @Service
   ^
    symbol: class Service
  C:\sts\workspace\study\src\main\java\com\example\demo\StudyApplication.java:6: error: cannot find symbol
  @SpringBootApplication
   ^
    symbol: class SpringBootApplication
  C:\sts\workspace\study\src\main\java\com\example\demo\jpa\data\dao\impl\ProductDAOImpl.java:7: error: package org.springframework.beans.factory.annotation does not exist
  import org.springframework.beans.factory.annotation.Autowired;
                                                     ^
  C:\sts\workspace\study\src\main\java\com\example\demo\jpa\data\dao\impl\ProductDAOImpl.java:8: error: package org.springframework.stereotype does not exist
  import org.springframework.stereotype.Component;
                                       ^
  C:\sts\workspace\study\src\main\java\com\example\demo\jpa\data\repository\ProductRepository.java:3: error: package org.springframework.data.jpa.repository does not exist
  import org.springframework.data.jpa.repository.JpaRepository;
                                                ^
  C:\sts\workspace\study\src\main\java\com\example\demo\jpa\data\entity\Product.java:6: error: package jakarta.persistence does not exist
  import jakarta.persistence.Column;
                            ^
  C:\sts\workspace\study\src\main\java\com\example\demo\jpa\data\entity\Product.java:7: error: package jakarta.persistence does not exist
  import jakarta.persistence.Entity;
                            ^
  C:\sts\workspace\study\src\main\java\com\example\demo\jpa\data\entity\Product.java:8: error: package jakarta.persistence does not exist
  import jakarta.persistence.GeneratedValue;
                            ^
  C:\sts\workspace\study\src\main\java\com\example\demo\jpa\data\entity\Product.java:9: error: package jakarta.persistence does not exist
  import jakarta.persistence.GenerationType;
                            ^
  C:\sts\workspace\study\src\main\java\com\example\demo\jpa\data\entity\Product.java:10: error: package jakarta.persistence does not exist
  import jakarta.persistence.Id;
                            ^
  C:\sts\workspace\study\src\main\java\com\example\demo\jpa\data\entity\Product.java:11: error: package jakarta.persistence does not exist
  import jakarta.persistence.Table;
                            ^
  C:\sts\workspace\study\src\main\java\com\example\demo\jpa\data\entity\Product.java:12: error: package lombok does not exist
  import lombok.AllArgsConstructor;
               ^
  C:\sts\workspace\study\src\main\java\com\example\demo\jpa\data\entity\Product.java:13: error: package lombok does not exist
  import lombok.Builder;
               ^
  C:\sts\workspace\study\src\main\java\com\example\demo\jpa\data\entity\Product.java:14: error: package lombok does not exist
  import lombok.EqualsAndHashCode;
               ^
  C:\sts\workspace\study\src\main\java\com\example\demo\jpa\data\entity\Product.java:15: error: package lombok does not exist
  import lombok.Getter;
               ^
  C:\sts\workspace\study\src\main\java\com\example\demo\jpa\data\entity\Product.java:16: error: package lombok does not exist
  import lombok.NoArgsConstructor;
               ^
  C:\sts\workspace\study\src\main\java\com\example\demo\jpa\data\entity\Product.java:17: error: package lombok does not exist
  import lombok.Setter;
               ^
  C:\sts\workspace\study\src\main\java\com\example\demo\jpa\data\entity\Product.java:18: error: package lombok does not exist
  import lombok.ToString;
               ^
  C:\sts\workspace\study\src\main\java\com\example\demo\jpa\data\dto\ProductDTO.java:4: error: package com.fasterxml.jackson.annotation does not exist
  import com.fasterxml.jackson.annotation.JsonProperty;
                                         ^
  C:\sts\workspace\study\src\main\java\com\example\demo\service\impl\ProductServiceImpl.java:4: error: package org.slf4j does not exist
  import org.slf4j.Logger;
                  ^
  C:\sts\workspace\study\src\main\java\com\example\demo\service\impl\ProductServiceImpl.java:5: error: package org.slf4j does not exist
  import org.slf4j.LoggerFactory;
                  ^
  C:\sts\workspace\study\src\main\java\com\example\demo\service\impl\ProductServiceImpl.java:6: error: package org.springframework.beans.factory.annotation does not exist
  import org.springframework.beans.factory.annotation.Autowired;
                                                     ^
  C:\sts\workspace\study\src\main\java\com\example\demo\service\impl\ProductServiceImpl.java:7: error: package org.springframework.stereotype does not exist
  import org.springframework.stereotype.Service;
                                       ^
  C:\sts\workspace\study\src\main\java\com\example\demo\StudyApplication.java:3: error: package org.springframework.boot does not exist
  import org.springframework.boot.SpringApplication;
                                 ^
  C:\sts\workspace\study\src\main\java\com\example\demo\StudyApplication.java:4: error: package org.springframework.boot.autoconfigure does not exist
  import org.springframework.boot.autoconfigure.SpringBootApplication;
                                               ^
  48 errors
  
  
  ...
  • GetController.java, ProductController.java 주석 처리

  • gradlew --version으로 gradle 버전 확인: 8.5 이상이 필요하다고 하는데 8.14버전이었음... 
    -> 찾아보니 8.14버전이 8.5버전보다 높은 게 맞음... 

  • gradlew --refresh-dependencies clean build --info 시도: 캐시 초기화 후 모든 의존성을 다시 다운로드한 후 로그출력 
    -> 실패 이유가 안 나옴 

  • build.gradle 코드 전체 리팩토링
    -> 또다른 문제 발생... 
plugins {
    id 'java'
    id 'org.springframework.boot' version '3.5.3'
    id 'io.spring.dependency-management' version '1.1.7'
    id 'jacoco'
    id 'com.ewerk.gradle.plugins.querydsl' version '1.0.10'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(17)
    }
}

repositories {
    mavenCentral()
}

configurations {
    compileOnly {
        extendsFrom annotationProcessor
    }
}

dependencies {
    // Spring Boot
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'

    // DB
    runtimeOnly 'com.mysql:mysql-connector-j'
    testImplementation 'com.h2database:h2'

    // JSON
    implementation 'com.google.code.gson:gson:2.10.1'

    // Swagger / OpenAPI
    implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.9'
    implementation 'io.swagger.core.v3:swagger-annotations:2.2.20'

    // Lombok
    compileOnly 'org.projectlombok:lombok'
    annotationProcessor 'org.projectlombok:lombok'

    // Spring Configuration Processor
    annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'

    // QueryDSL
    implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta'
    annotationProcessor 'com.querydsl:querydsl-apt:5.0.0:jakarta'
    annotationProcessor 'jakarta.persistence:jakarta.persistence-api:3.1.0'

    // Test
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    testImplementation 'org.assertj:assertj-core:3.24.2'
    testRuntimeOnly 'org.junit.platform:junit-platform-launcher'

    // JaCoCo API (optional)
    implementation 'org.jacoco:org.jacoco.core:0.8.10'
    
    // SLF4J
    implementation 'org.slf4j:slf4j-api:2.0.12'
    implementation 'org.slf4j:slf4j-simple:2.0.12' 
}

tasks.withType(JavaCompile) {
    options.compilerArgs << "-parameters"
}

tasks.named('test') {
    useJUnitPlatform()
}

jacocoTestReport {
    reports {
        html.required = true
    }
}

// QueryDSL 설정
def querydslDir = "$buildDir/generated/querydsl"

tasks.named('compileQuerydsl', JavaCompile) {
    source = sourceSets.main.java
    classpath = sourceSets.main.compileClasspath
    destinationDirectory.set(file(querydslDir))
    options.annotationProcessorGeneratedSourcesDirectory = file(querydslDir)
    options.compilerArgs += ["-proc:only"]
    options.annotationProcessorPath = files(configurations.annotationProcessor)
}

sourceSets {
    main.java.srcDirs += querydslDir
}

  • 네이버 블로그 코드를 보고 build.gradle 전체 리팩토링.. 드디어성공함 ㅠㅠㅠㅠㅠㅠ 

https://blog.naver.com/2001200190/223934440513

 

JPA,QueryDSL5 실행 예제로(인텔리제이와 STS에서 모두 가능한 설정) 다른 블로그에서 잘안되시는 분

많은 블로그 글에서 실행보니 버전문제등으로 잘안되는 부분이 많아서 실제로 해보면서 정리해봤습니다. 사...

blog.naver.com

 

buildscript { //
	ext {
		queryDslVersion = "5.0.0"
	}
	 dependencies { // 여기가 없으면 STS에서는 gradle tasks option 항목이 없어 queryDSL 컴파일이 안됨

		 classpath("gradle.plugin.com.ewerk.gradle.plugins:querydsl-plugin:1.0.10")

	 }
}

plugins {
    id 'java'
    id 'org.springframework.boot' version '3.5.3'
    id 'io.spring.dependency-management' version '1.1.7'
    id 'jacoco'
    id 'com.ewerk.gradle.plugins.querydsl' version '1.0.10'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '17'

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(17)
    }
}

repositories {
    mavenCentral()
}

configurations {
    compileOnly {
        extendsFrom annotationProcessor
    }
}

dependencies {
    // Spring Boot
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'

    // DB
    runtimeOnly 'com.mysql:mysql-connector-j'
    testImplementation 'com.h2database:h2'

    // JSON
    implementation 'com.google.code.gson:gson:2.10.1'

    // Swagger / OpenAPI
    implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.9'
    implementation 'io.swagger.core.v3:swagger-annotations:2.2.20'

    // Lombok
    compileOnly 'org.projectlombok:lombok'
    annotationProcessor 'org.projectlombok:lombok'

    // Spring Configuration Processor
    annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'

    // QueryDSL
    implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta'
    annotationProcessor 'com.querydsl:querydsl-apt:5.0.0:jakarta'
    annotationProcessor 'jakarta.persistence:jakarta.persistence-api:3.1.0'

    // Test
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    testImplementation 'org.assertj:assertj-core:3.24.2'
    testRuntimeOnly 'org.junit.platform:junit-platform-launcher'

    // JaCoCo API (optional)
    implementation 'org.jacoco:org.jacoco.core:0.8.10'
    
    // SLF4J
    implementation 'org.slf4j:slf4j-api:2.0.12'
	implementation 'org.slf4j:slf4j-simple:2.0.12'
    
    // H2 데이터베이스 (테스트용 인메모리 DB)
    runtimeOnly 'com.h2database:h2'
}

tasks.withType(JavaCompile) {
    options.compilerArgs << "-parameters"
}

tasks.named('test') {
    useJUnitPlatform()
}

jacocoTestReport {
    reports {
        html.required = true
    }
}

// QueryDSL 설정
def querydslDir = "$buildDir/generated/querydsl"

tasks.withType(JavaCompile) {
	options.generatedSourceOutputDirectory = file(querydslDir)
}

querydsl { 
	jpa = true
	querydslSourcesDir = querydslDir
}

sourceSets { //
	main.java.srcDir querydslDir
}

configurations { 
	compileOnly {
		extendsFrom annotationProcessor
	}
	querydsl.extendsFrom compileClasspath
}

compileQuerydsl { 
	options.annotationProcessorPath = configurations.querydsl
}

clean {
	delete file(querydslDir)
}

STS4에서도 QueryDSL을 사용가능함!!
QProduct.java도 잘 생성됨

 

(3) 테스트 코드 새로 작성 (ProductRepositoryTest.java)

// src/test/java/com.example.demo/ProductRepositoryTest.java
package com.example.demo;

//Java의 List 클래스 import
import java.util.List;

//JUnit 5의 테스트 어노테이션
import org.junit.jupiter.api.Test;

//Spring Boot의 JPA 테스트용 어노테이션
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;

//QueryDSL의 JPAQuery 클래스 import
import com.querydsl.jpa.impl.JPAQuery;

//테스트 대상 엔티티 및 리포지토리 import
import com.example.demo.jpa.data.entity.Product;
import com.example.demo.jpa.data.entity.QProduct;

//JPA의 EntityManager 관련 클래스 import
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;

//JPA 관련 테스트를 위한 설정. 실제 DB 대신 테스트용 인메모리 DB(H2 등)를 사용함
@DataJpaTest
public class ProductRepositoryTest {

	// EntityManager를 주입받아 QueryDSL에서 사용
	@PersistenceContext
	EntityManager entityManager;
	 
	// QueryDSL을 활용한 테스트 메서드
	@Test
	void queryDslTest() {
		// QueryDSL의 JPA 기반 쿼리 객체 생성
	    JPAQuery<Product> query = new JPAQuery<>(entityManager);
	     
	    // QProduct는 QueryDSL이 자동 생성해주는 클래스 (Product 엔티티에 대한 메타 정보 포함)
	    QProduct qProduct = QProduct.product;
	     
	    // QueryDSL을 사용하여 name이 "펜"인 Product를 가격 오름차순으로 조회
	    List<Product> productList = query
	    		.from(qProduct)                      // 조회 대상 테이블 지정
	            .where(qProduct.name.eq("펜"))       // 조건: name이 "펜"인 경우
	            .orderBy(qProduct.price.asc())       // 가격 오름차순 정렬
	            .fetch();                            // 결과 리스트로 반환
	     
	    // 조회된 결과 출력
	    for (Product product : productList) {
	        System.out.println("--------------");
	        System.out.println();
	        System.out.println("Product Number: " + product.getNumber()); // 제품 번호
	        System.out.println("Product Name: " + product.getName());     // 제품 이름
	        System.out.println("Product Price: " + product.getPrice());   // 제품 가격
	        System.out.println("Product Stock: " + product.getStock());   // 제품 재고
	        System.out.println();
	        System.out.println("--------------");
	    }
	}
}

(4) src/test/resources에 application.properties 새로 생성(테스트 전용 설정으로 분리)

# 테스트용 인메모리 H2 DB 설정
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=

# JPA 설정
spring.jpa.hibernate.ddl-auto=create
spring.jpa.show-sql=true

# H2 콘솔 (선택)
spring.h2.console.enabled=true
spring.h2.console.path=/h2-console

  • 또다시 엄청난 문제들이 발생....(해결은 다음 글에 이어서)

반응형