간단한 데모버전 API 를 만들어야 할 일이 생겼다.
비교적 간단한 DB 구조여서, Kotlin JDSL 을 이 기회에 이용하여 구성해보기로 마음 먹었다.
기본적인 설정은 다음의 Kotlin JDSL 공식문서 를 확인하자.
interface ClientRepositoryInterface : JpaRepository<Client, Int>, KotlinJdslJpqlExecutor
단 한 라인 만으로 JpaRepository 에서 귀찮게 구현해내던 것들을 지원해주며, 쿼리문을 조금 더 이해하기 쉽게 사용할 수 있다.
findAllByIds, exists, findPage 등은 상당히 고마웠다.
그런데 사용하기에 앞서 바로 문제가 발생했다…
먼저 현재 환경을 살펴보자.
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")
...
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
class Client(
id: Int,
name: String
) {
@Id
@GeneratedValue
val id = id
@Column(nullable = false)
val name: String = name
}
@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 자체의 문제로, 아직 미해결 이슈인듯 하니 추후 확인 후 수정해야 하겠다.
전혀 상관없는 이슈 였다…위에서 해결하지 못했던 이슈가 핫픽스로 수정되어, 신나서 곧장 적용하였다.
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

있다!
그럼에도 클래스패스에 없다고 뜨는 것은 다른 문제인 것으로 생각되나 감조차 잡히지 않는다.
조금 더 시간이 걸릴 듯 하니 다음 글에 이어적도록 하자…