코틀린에서 QueryDSL 사용

박태현·2025년 6월 19일

예약 프로젝트

목록 보기
10/10

현재 쿼리 복잡도만 보면 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()
  }
profile
꾸준하게

0개의 댓글