๊น์ํ๋ ๊ฐ์๋ฅผ ๋ฃ๊ณ ๋์ ๊ฐ์์ ์๋ Querydsl ์ธํ ๊ณผ ํ์ฌ ๋ด๊ฐ ์ฐ๊ณ ์๋ ์คํ๋ง 3.x ๋ฒ์ ์ ์ธํ ์ด ์กฐ๊ธ ๋ค๋ฅด๋จ๊ฑธ ์์๋ค. ๊ทธ๋์ ํด๋น ๋ด์ฉ์ ์ ๋ฆฌํ๋๊ฒ๊ณผ ๋์์ ๊ฐ๊ฐ์ ๊ธฐ๋ฅ์ ๋ํด์ ์กฐ๊ธ ์์๋ณด๋ ์๊ฐ์ ๊ฐ์ง๋๋ก ํ๊ฒ ๋ค.
QueryDSL
- JPA์์ ์ ๊ณตํ๋ ๊ฐ์ฒด์งํฅ์ฟผ๋ฆฌ์ธ JPQL(Java Persistence Query Language)์ ํตํด ๋์ ์ฟผ๋ฆฌ๋ฅผ ๊ตฌ์ฑํ๋ฉด ์ฝ๋๊ฐ ๊ต์ฅํ ๋์กํด์ง๋ค๋ ๊ฒ์ ๋๋ ์ ์๋ค.
- JPQL์ ๋ฌธ์์ด์ ์ฌ์ฉํ๋ค. ๋ฌธ์์ด์ ์กฐ๊ฑด์ ๋ฐ๋ผ ์ด์ด๋ถ์ด๋ ํ์์ผ๋ก ๊ตฌ์ฑํ๊ธฐ ๋๋ฌธ์ ์๊ธฐ๋ ๋ฌธ์ ๊ฐ ์๋ค. ๋ฌธ์์ด์ด๊ธฐ์ ์คํ๊ฐ ๋ฐ์ํด๋ ์ปดํ์ผ ๋จ๊ณ์์ ์๋ฌ๋ฅผ ์ก์์ฃผ์ง ๋ชปํ๋ค.(๋ค๋ง, NamedQuery๋ฅผ ์ฌ์ฉํ๋ฉด ๊ฐ๋ฅํ๋ค) ๋ํ ๋์ ์ฟผ๋ฆฌ๋ฅผ ๊ตฌ์ฑํ ๋, ์ค๊ฐ์ค๊ฐ if๋ฌธ์ ์ํด ๋ฌธ์์ด์ด ์ถ๊ฐ๋๊ธฐ ๋๋ฌธ์ ๊ฐ๋ ์ฑ์ด ๋จ์ด์ง๋ค. ๋ฐ๋ผ์ ์ฟผ๋ฆฌ๋ฅผ ์ฒด๊ณ์ ์ผ๋ก ๊ด๋ฆฌํ๊ธฐ ์ด๋ ต๋ค.
- QueryDSL์ ์์ ๊ฐ์ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ๋ง๋ค์ด์ก๋ค. Type-Safeํ ์ฟผ๋ฆฌ๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด ์ํฐํฐ์ ๋งคํ๋๋ ์ ์ ํ์ QClass๋ฅผ ์์ฑํด ์ฟผ๋ฆฌ๋ฅผ ์์ฑํ ์ ์๊ฒ ๋ง๋ค์๋ค. ์ปดํ์ผ ๋จ๊ณ์์ ์ค๋ฅ๋ฅผ ์ก์๋ผ ์ ์๊ณ ๋ฉ์๋ ์ฒด์ด๋์ ํตํด ์กฐ๊ฑด์ ๋ณด๋ค ์ฝ๊ฒ ์ถ๊ฐํ ์ ์๋ค. ์ฆ, ๋์ ์ฟผ๋ฆฌ๋ฅผ ์์ฑํ ๋ ์ฉ์ดํด์ง๋ค๋ ๋ง์ด๋ค.
- QueryDSL๋ ๋ด๋ถ์ ์ผ๋ก๋ JPQL๋ฅผ ๊ตฌ์ฑํด ์ฟผ๋ฆฌ๋ฅผ ๋ง๋ค์ด๋ธ๋ค. ๋ค๋ง ์ฌ์ฉ์๋ค์ด ํธํ๊ฒ ์ฌ์ฉํ ์ ์๊ฒ ์ถ์ํํด๋์ JPQL ๋น๋๋ผ๊ณ ์๊ฐํ๋ฉด ๋๋ค.
Gradle ์ค์
- Spring Boot 3.x ๋ฒ์ ๊ธฐ์ค
- QueryDSL Implementation ๋ถ๋ถ๊ณผ QueryDSL Build Options ๋ถ๋ถ์ ์ถ๊ฐ
build.gradle (groovy)
plugins {
id 'java'
id 'org.springframework.boot' version '3.1.2'
id 'io.spring.dependency-management' version '1.1.2'
}
group = 'com.pythonstrup'
version = '0.0.1-SNAPSHOT'
java {
sourceCompatibility = '17'
}
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'com.h2database:h2'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
// QueryDSL Implementation
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"
}
test {
useJUnitPlatform()
}
/**
* QueryDSL Build Options
*/
def querydslDir = "src/main/generated"
sourceSets {
main.java.srcDirs += [ querydslDir ]
}
tasks.withType(JavaCompile) {
options.getGeneratedSourceOutputDirectory().set(file(querydslDir))
}
clean.doLast {
file(querydslDir).deleteDir()
}
build.gradle.kts (kotlin)
- ์ฝํ๋ฆฐ์ ๊ฒฝ์ฐ, QeuryDSL Version Setting ์ฃผ์ ๋ถ๋ถ๋ ์ถ๊ฐ
plugins {
id 'java'
id 'org.springframework.boot' version '3.1.5'
id 'io.spring.dependency-management' version '1.1.3'
// querydsl๊ด๋ จ ๋ช
๋ น์ด๋ฅผ gradleํญ์ ์์ฑํด์ค๋ค. (๊ถ์ฅ์ฌํญ)
// id "com.ewerk.gradle.plugins.querydsl" version "1.0.10"
}
group = 'study'
version = '0.0.1-SNAPSHOT'
java {
sourceCompatibility = '17'
}
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'com.h2database:h2'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
implementation 'com.github.gavlyukovskiy:p6spy-spring-boot-starter:1.9.0'
// QueryDSL Implementation
annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jakarta"
annotationProcessor "jakarta.annotation:jakarta.annotation-api"
annotationProcessor "jakarta.persistence:jakarta.persistence-api"
// === QueryDsl ๋ ===}
tasks.named('test') {
useJUnitPlatform()
}
//QueryDsl ๋น๋ ์ต์
(์ ํ) ===
def querydslDir = 'src/main/generated'
sourceSets {
main.java.srcDirs += [ querydslDir ]
}
tasks.withType(JavaCompile) {
options.generatedSourceOutputDirectory = file(querydslDir)
}
clean.doLast {
file(querydslDir).deleteDir()
}
Gradle ๋์ ์๋ฆฌ
์์กด์ฑ ์ค์
- Querydsl JPA Support ์์กด์ฑ์ ์ถ๊ฐํด์ค๋ค.
- maven repository: https://mvnrepository.com/artifact/com.querydsl/querydsl-jpa
Annotation Processor
- ๋จผ์ , Annotation Processor๋ ์ปดํ์ผ ๋จ๊ณ์์ Annotation์ ์ ์๋ ์ผ๋ ฌ์ ํ๋ก์ธ์ค๋ฅผ ๋์ํ๊ฒ ํ๋ ๊ฒ์ด๋ค. ์ปดํ์ผ ๋จ๊ณ์์ ์คํ๋๊ธฐ ๋๋ฌธ์ ๋น๋ ๋จ๊ณ์์ ์๋ฌ๋ฅผ ์ถ๋ ฅํ๊ฒ ํ๊ฑฐ๋ ์์ค์ฝ๋ ๋ฐ ๋ฐ์ดํธ ์ฝ๋๋ฅผ ์์ฑํ ์ ์๋ค.
- querydsl-apt๋ ์์ค ์ฝ๋ ๋ ๋ฒจ์์ Querydsl ๊ด๋ จ ์ด๋ ธํ ์ด์ ๋ค์ ์ฒ๋ฆฌํ๊ณ ์ฟผ๋ฆฌ ํ์ ํด๋์ค๋ค์ ์์ฑํด์ฃผ๋ ์ญํ ์ ํ๋ค. ์ด๋ฅผ ํตํด ์ฟผ๋ฆฌ๋ฅผ ์ง์ ์์ฑํ๋ ๋์ Java ์ฝ๋๋ฅผ ํ์ฉํ์ฌ ์ปดํ์ผ๋ฌ๊ฐ ๊ฒ์ฌํ๋ ์์ ํ ๋ฐฉ๋ฒ์ผ๋ก ์ฟผ๋ฆฌ๋ฅผ ์์ฑํ ์ ์๋ค. sourceSets์ QClass๋ฅผ ์ ์ฅํ ๊ฒฝ๋ก๋ฅผ ์ค์ ํด์ฃผ๋ฉด ํด๋น ๊ฒฝ๋ก์ ์ฟผ๋ฆฌ ํ์ ํด๋์ค๋ฅผ ์์ฑํด์ค๋ค.
- annotation-api๋ฅผ ์ถ๊ฐํ์ง ์์ผ๋ฉด ์๋์ ๊ฐ์ ์๋ฌ๊ฐ ๋ฐ์ํ๋ค. ํด๋น api์๋ @Generated, @Deprecated, @SuppressWarnings, @Override์ ๊ฐ์ ์๋ฐ ํ์ค ์ด๋ ธํ ์ด์ ์ด ํด๋น API์ ํฌํจ๋์ด ์๋ค.
Unable to load class 'jakarta.annotation.Generated'
- persistence-api๋ฅผ ์ถ๊ฐํ์ง ์์ผ๋ฉด ์๋์ ๊ฐ์ ์๋ฌ๊ฐ ๋ฐ์ํ๋ค. persistence-api๋ @Entity, @Id, @GeneratedValue, @Column ๋ฑ์ ์ด๋ ธํ ์ด์ ์ ์ง์ํ๋ค.
Unable to load class 'jakarta.persistence.Entity'.
๋น๋์ต์
querydslDir
- querydslDir์ ๋ณ์๋ค. QClass๋ฅผ ์ ์ฅํ๊ธธ ์ํ๋ ๊ฒฝ๋ก๋ฅผ ์ค์ ํด์คฌ๋ค. ๋์ฒด๋ก "src/main/generated" ๊ฒฝ๋ก๋ก ์ค์ ํ๋ค.
sourceSets
- Gradle ๋น๋ ์คํฌ๋ฆฝํธ์์ ์ ์ํ ์์ค์ฝ๋์ ๋ฆฌ์์ค ๋๋ ํ ๋ฆฌ๋ฅผ ๊ตฌ์ฑํ๊ณ ๊ด๋ฆฌํ ๋ ์ฌ์ฉํ๋ค.
- java source set์ querydsl QClass ์์น๋ฅผ ์ถ๊ฐํ๋ค.
JavaCompile ๋ช ๋ น์ด
- JavaCompile ์์ ์ด ์ผ์ด๋ ๋, querydsl QClass ํ์ผ ์์ฑ ์์น๋ฅผ ์ง์ ํ ๋ ์ฌ์ฉํ๋ค.
- Groovy => tasks.withType(JavaCompile) {} ๋ก ์ถ๊ฐ
Clean ๋ช ๋ น์ด
- gradle clean ๋ช ๋ น์ด๋ฅผ ์คํํ ๋, ๋์์ํฌ ์์ ์ ์ถ๊ฐํ ์ ์๋ค.
- clean ๋ช ๋ น์ด๊ฐ ์คํ๋๋ฉด QClass Directory ์ญ์ ํ๋๋ก ์ค์ ํ๋ค.
์คํ ๋ฐฉ๋ฒ
Qclass ์์ฑ ๋ฐ ์ญ์
์์ฑ
- Gradle - Task - other - compileJava๋ฅผ ์คํํ๋ฉด ๋๋ค.
- ๋จ์ SpringBootApplication ์ ์คํํด๋ ์์ฑ๋๋ค.
- ์๋์ ๊ฐ์ด src/main/generated ๊ฒฝ๋ก์ QClass๊ฐ ์์ฑ๋๋ค.
์ญ์
- Gradle - Task - build - clean ์ ์คํํ๋ฉด ๋๋ค.
- src/main/generated ๋๋ ํ ๋ฆฌ์ ํ์ผ์ด ์ ๋ถ ์ญ์ ๋๋ค.
์ฃผ์์ฌํญ
- src/main/generated ๋๋ ํ ๋ฆฌ๋ ๊ฐ๋ฐํ ๋ ํธ์๋ฅผ ์ํด ์ฌ์ฉํ๋ ๊ฒ์ด๋ค. ์ค์ ๋ฐฐํฌ๋ฅผ ์ํด build๋ฅผ ๋ช ๋ น์ด๋ฅผ ์คํํ ๋, ์ฝ๋ ์คํ์ ์ํด ํ์ํ QClass๋ค์ด build ๋๋ ํ ๋ฆฌ์ entity์ ๊ฐ์ ๊ฒฝ๋ก์ ์ ๋ถ ํฌํจ๋๊ธฐ ๋๋ฌธ์ Docker์ ๊ฐ์ ์ปจํ ์ด๋์ generated ๋๋ ํ ๋ฆฌ๋ฅผ COPYํด์ ๋ฃ์ ์ด์ ๊ฐ ์๋ค.
- Git๊ณผ ๊ฐ์ ๋ฒ์ ๊ด๋ฆฌ์์คํ ์ ์ฌ์ฉํ๊ณ ์๋ค๋ฉด, generated๋ฅผ ๊ตณ์ด ์ ์ฅํ ํ์๊ฐ ์๊ธฐ ๋๋ฌธ์ ignore ํ์ผ์ src/main/generated ๋๋ ํ ๋ฆฌ๋ฅผ ์ถ๊ฐํ๋๋ก ํ์.
์ฐธ๊ณ ์๋ฃ
- QueryDSL: https://velog.io/@juhyeon1114/Spring-QueryDsl-gradle-%EC%84%A4%EC%A0%95-Spring-boot-3.0-%EC%9D%B4%EC%83%81
- sourceSets: https://kkang-joo.tistory.com/3
- AnnotationProcessor์ QueryDSL: http://honeymon.io/tech/2020/07/09/gradle-annotation-processor-with-querydsl.html