Kotlin JDSL 구축 - 1

Mayfly·2024년 12월 30일

간단한 데모버전 API 를 만들어야 할 일이 생겼다.

비교적 간단한 DB 구조여서, Kotlin JDSL 을 이 기회에 이용하여 구성해보기로 마음 먹었다.

기본적인 설정은 다음의 Kotlin JDSL 공식문서 를 확인하자.

interface ClientRepositoryInterface : JpaRepository<Client, Int>, KotlinJdslJpqlExecutor

단 한 라인 만으로 JpaRepository 에서 귀찮게 구현해내던 것들을 지원해주며, 쿼리문을 조금 더 이해하기 쉽게 사용할 수 있다.

findAllByIds, exists, findPage 등은 상당히 고마웠다.

그런데 사용하기에 앞서 바로 문제가 발생했다…


오류 상황

먼저 현재 환경을 살펴보자.

build.gradle.kts


implementation("org.springframework.boot:spring-boot-starter-actuator")
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
implementation("org.springframework.boot:spring-boot-starter-web")implementation("com.linecorp.kotlin-jdsl:jpql-dsl:3.3.1")
implementation("com.linecorp.kotlin-jdsl:jpql-render:3.3.1")
implementation("com.linecorp.kotlin-jdsl:spring-data-jpa-support:3.3.1")
...

interface

import com.linecorp.kotlinjdsl.support.spring.data.jpa.repository.KotlinJdslJpqlExecutor
import org.springframework.data.jpa.repository.JpaRepository
import org.demo.api.domain.client.entity.Client

interface ClientRepository : JpaRepository<Client, Int>, KotlinJdslJpqlExecutor

entity


class Client(
    id: Int,
    name: String
) {
    @Id
    @GeneratedValue
    val id = id

    @Column(nullable = false)
    val name: String = name
}

test

@Test
fun `client 정보 요청`() {
	mockMvc.get("/client/${client.id}").andExpect {
		status { isOk() }
		json {
			with(secClient) {
				"$.data.id" shouldBe id
				"$.data.ownerId" shouldBe ownerId
			}
		}
	}
}

간단하게 테스트를 돌려보자.

곧장 에러가 발생한다.

No property 'findSlice' found for type 'Client'

이슈들을 먼저 검색해본 결과, 상당히 비슷한 이슈를 찾아낼 수 있었다.

Kotlin JDSL 자체의 문제로, 아직 미해결 이슈인듯 하니 추후 확인 후 수정해야 하겠다.

  • 해당 이슈 https://github.com/line/kotlin-jdsl/issues/668
    • 해당 이슈도 핫픽스로 해결되었다. 버전을 3.3.2 이상으로 업그레이드 시키자. (역시 라인 선생님들은 대응이 엄청나게 빠르다… 불철주야 고생이 많으신 분들 감사합니다.)
    • 결론부터 말하자면, 전혀 상관없는 이슈 였다…

위에서 해결하지 못했던 이슈가 핫픽스로 수정되어, 신나서 곧장 적용하였다.

build.gradle.kts


implementation("org.springframework.boot:spring-boot-starter-actuator")
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
implementation("org.springframework.boot:spring-boot-starter-web")implementation("com.linecorp.kotlin-jdsl:jpql-dsl:3.4.2")
implementation("com.linecorp.kotlin-jdsl:jpql-render:3.4.2")
implementation("com.linecorp.kotlin-jdsl:spring-data-jpa-support:3.4.2")
...

그러나 어림도 없었다.

더 자세히 살펴보니 에러도 비슷할 뿐 같지 않고, 상황도 다르다.

완벽히 같은 상황 혹은 에러를 찾아보고, 에러 로그를 더 파고들어 보자.


오류 발견

No property 'findSlice' found for type 'Client'

정확한 에러는 다음과 같다.

애초에 Client 에는 ‘findSlice’ 라는 property 가 존재하지 않는다.

그런데 자꾸 JPA 에서 이 property 를 찾는 이유가 뭘까.

우선 ‘findSlice’ 가 어디에서 튀어나온 녀석인가 했더니,

KotlinJdslJpqlExecutor 에서 JPA Spring Supports 로 지원하는 메소드이다.

곧장 KotlinJdslJpqlExecutor 인터페이스 상속을 지우고 테스트하자 아무 문제없이 작동하였다.

그럼 에러 발생 장소는 여기일 것이다.

interface ClientRepositoryInterface : JpaRepository<Client, Int>, KotlinJdslJpqlExecutor 

또한 KotlinJdslJpqlExecutor 에는 ‘findSlice’ 가 존재하는데 없다고 나오니, KotlinJdslJpqlExecutor 가 매핑이 안되고 있는 것이라 판단하였다.

매핑은 KotlinJDSLAutoConfiguration 을 통해서 하고 있으니, 곧장 확인해 보자.

@SpringBootTest
class Snsb3DemoApiApplicationTests {

    @Test
    fun contextLoads() {
    }

}

일단 기능 구현 전 단계에서 부터 문제가 발생하는지에 대한 체크이다.

아니나 다를까, 같은 에러가 계속 발생한다.

일단 내 예상이 맞는지 디버깅 모드로 테스트를 돌려서, KotlinJDSLAutoConfiguration 이 제대로 매핑되는지 확인하였다.

Negative matches:
-----------------
...
KotlinJdslAutoConfiguration:
      Did not match:
         - @ConditionalOnClass did not find required class 'jakarta.persistence.EntityManager' (OnClassCondition)
...

KotlinJdslAutoConfiguration 에 EntityManager 가 매칭이 안되고 있다!

클래스패쓰에 EntityManager 가 없다는 어처구니 없는 에러 메시지가 나온다.

분명 build.gradle.kts 에는,

implementation("org.springframework.boot:spring-boot-starter-data-jpa")

분명하게 jakarta.persistence 를 포함하는 의존성을 선언해두었다.

뭔가 싶어 의존성들을 확인해 보았다.

./gradlew dependencies --configuration compileClasspath

있다!

그럼에도 클래스패스에 없다고 뜨는 것은 다른 문제인 것으로 생각되나 감조차 잡히지 않는다.

조금 더 시간이 걸릴 듯 하니 다음 글에 이어적도록 하자…

profile
Devops 에 관심이 많은 Backend 개발자

0개의 댓글