현재 쿼리 복잡도만 보면 JPQL로도 충분하지만, 컴파일 타임 타입 안전성과 동적 쿼리 확장성을 고려해 QueryDSL을 사용하였습니다.
코틀린에서 QueryDSL 사용을 위해 kapt를 사용했습니다.
kapt( Kotlin Annotation Processing Too l)
QueryDSL은 컴파일 타임에 @Entity 클래스를 기반으로 Q 클래스를 자동 생성하여 동작합니다.
Java에서는 annotationProcessor로 이를 처리하지만, Kotlin은 컴파일 구조가 달라 annotationProcessor를 직접 사용할 수 없으며, QueryDSL은 APT 기반으로 Q 클래스를 생성하기 때문에 KSP로는 대체할 수 없어, Kotlin에서 QueryDSL을 사용하려면 kapt를 사용해야 합니다.
의존성 코드
plugins {
kotlin("jvm") version "1.9.25"
kotlin("plugin.spring") version "1.9.25"
id("org.springframework.boot") version "3.5.0"
id("io.spring.dependency-management") version "1.1.7"
kotlin("plugin.jpa") version "1.9.25"
// for querydsl
kotlin("kapt") version "1.9.25"
}
...
dependencies {
...
// query dsl
implementation("com.querydsl:querydsl-jpa:5.0.0:jakarta")
kapt("com.querydsl:querydsl-apt:5.0.0:jakarta")
kapt("jakarta.annotation:jakarta.annotation-api")
kapt("jakarta.persistence:jakarta.persistence-api")
...
}
// querydsl QClass 위치
val generated = file("src/main/generated")
// querydsl QClass 파일 생성 위치 지정
tasks.withType<JavaCompile> {
options.generatedSourceOutputDirectory.set(generated)
}
// kotlin source set에 querydsl QClass 위치 추가
sourceSets {
main {
kotlin.srcDirs += generated
}
}
// gradle clean 시에 QClass 디렉토리 삭제
tasks.named("clean") {
doLast {
generated.deleteRecursively()
}
}
...
QueryDSL 설정 클래스
@Configuration
class QueryDslConfig(
val em: EntityManager
) {
@Bean
fun queryFactory(): JPAQueryFactory {
return JPAQueryFactory(em)
}
}
기존의 JPQL 쿼리
@Query("""
SELECT DISTINCT p FROM PerformanceSchedule ps
JOIN FETCH ps.performance p
JOIN FETCH p.performanceScheduleList
WHERE ps.venue.id = :venueId
""")
fun findPerformancesByVenueId(@Param("venueId") venueId: Long): List<Performance>
Query DSL로 변경
fun findPerformancesByVenueId(venueId: Long): List<Performance> {
val performanceSchedule = QPerformanceSchedule.performanceSchedule
return queryFactory
.select(performanceSchedule.performance)
.from(performanceSchedule)
.join(performanceSchedule.performance.performanceScheduleList).fetchJoin()
.where(performanceSchedule.venue.id.eq(venueId))
.distinct()
.fetch()
}