스프링 부트/Kotlin

코틀린에서 JPA 적용하기

주인 완 2024. 12. 28. 18:50

 

 

최근 들어 실무 진영에서는 코틀린에 대한 선호도가 높아지고 있는 상황으로 보인다. 당장 올해 여름에 진행했던 당근 테크 인턴십의 서버 직군만 보더라도, 코틀린과 Spring 프레임워크를 활용하는 개발자들을 원했다. 또한 상당수의 빅테크 기업이 여러 서비스를 점차 자바가 아닌 코틀린을 활용해서 개발하거나, 기존의 서비스를 코틀린으로 마이그레이션 하는 것을 볼 수 있었다.

 

더 이상 코틀린은 선택이 아닌 필수라고 생각했다. 그래서 최근부터 코프링(Kotlin + Spring Framework)을 공부하고 있는데, 해당 과정에서 JPA를 적용하면서 겪은 과정을 풀어내보려고 한다.

 

코틀린과 궁합이 좋지 않은 JPA

JPA는 자바 진영에서 ORM 기술 표준으로 사용되는 인터페이스의 집합체이고, 이를 구현한 것이 Hibernate 라이브러리이다. 여기서 ORM(Object-Relational Mapping)은 애플리케이션(Spring)의 엔티티 클래스와 RDB 내의 테이블을 매핑해 주는 뜻을 지니고 있으며, 궁극적으로 애플리케이션의 객체를 테이블에 자동으로 영속화해주는 책임을 지니고 있다.

 

이름에서도 알 수 있듯이, JPA(Java persistence API)는 자바를 기준으로 나온 표준이다. 하지만 코틀린이 추구하는 방향은 자바 진영과 사뭇 결이 다르기에, JPA를 활용해 코프링을 사용하는 데 있어서 사소한 불편함이 존재한다.

 

Hibernate의 요구사항

JPA의 가장 대표적인 구현체인 Hibernate는 변경 감지(Dirty Checking), 지연 로딩(Lazy Loading)등 런타임 환경에서의 다양한 기능을 위해 프록시 객체를 사용하고는 한다. 그리고 그 과정에서 다음과 같은 요구사항을 규정하고 있다.

  • 엔티티 클래스에는 public 또는 protected 범위의 매개변수가 없는(no-arg) 생성자가 포함되어야 함.
  • 엔티티 클래스는 final이어서는 안됨.

기본 생성자 관련하여 오류가 나는 모습

 

하지만 코틀린은 모든 클래스가 기본적으로 불변(final)하기에, open 접두사를 통해 이를 해결해주어야 한다. 또한, 코틀린에서 기본 생성자를 각 클래스마다 구현해주는 것은 여간 번거로운 작업이 아니다. 그래서 이를 코틀린에서 제공해주는 플러그인을 통해 해결해줄 수 있다.

 

JPA 플러그인으로 기본 생성자 해결하기

https://kotlinlang.org/docs/no-arg-plugin.html#-arq22q_43

 

No-arg compiler plugin | Kotlin

 

kotlinlang.org

 

해당 플러그인은 코틀린에서 기본으로 제공해주는 no-arg 플러그인을 감싸고 있는 플러그인이다. no-arg 플러그인은 특정 어노테이션을 통해 매개변수 없는 기본 생성자를 만들어주는 플러그인이며, 이를 통해 JPA에서 기본 생성자가 없는 클래스도 인스턴스화 할 수 있게 만들어준다. 그리고 jpa 플러그인은 JPA를 통해 엔티티를 구현하는 데 있어서 가장 필수적으로 쓰이는 @Entity, @Embeddable, @MappedSuperclass 어노테이션을 no-arg 플러그인에 자동으로 선언해준다.

 

plugins {
    kotlin("plugin.jpa") version "2.1.0"
}

 

그리고 build.gradle.kts에 다음과 같이 추가해주면 손쉽게 적용이 된다.

 

kotlin-spring 플러그인으로 final 해결하기

https://kotlinlang.org/docs/all-open-plugin.html#spring-support

 

All-open compiler plugin | Kotlin

 

kotlinlang.org

 

모든 엔티티 클래스에 open 접두사를 선언해주는 것은 고역이다. 또한, Spring AOP와 같은 프레임워크 및 라이브러리를 사용하려면 마찬가지로 모든 클래스에 open 접두사를 마찬가지로 선언해주어야 한다.

 

정말 고맙게도, 이를 all-open 플러그인을 감싼 플러그인인 kotlin-spring 플러그인을 통해 해결해줄 수 있다. 예를 들어, @Configuration이나 @Service와 같은 특정 빈은 open되어야 하는데, all-open 플러그인을 사용하면 이러한 어노테이션을 지정할 수 있다. 그리고 kotlin-spring 플러그인은 스프링 프레임워크에서 필수적으로 open되어야 하는 다음 어노테이션을 자동으로 처리해준다.

  • @Component, @Configuration, @Controller, @RestController, @Service
  • @Async, @Transactional, @Cacheable, @SpringBootTest

 

plugins {
    id("org.jetbrains.kotlin.plugin.spring") version "2.1.0"
}

 

마찬가지로 build.gradle.kts에 다음과 같이 추가해주면 손쉽게 적용이 된다. 하지만 @Entity 어노테이션은 해당 플러그인의 적용 대상이 아니기에, 수동으로 어노테이션을 명시해주어야 한다. 따라서 다음과 같이 build.gradle.kts를 수정해준다.

 

plugins {
    kotlin("plugin.jpa") version "2.1.0"
}

allOpen {
    annotation("jakarta.persistence.Entity")
    annotation("jakarta.persistence.MappedSuperclass")
    annotation("javax.persistence.Embeddable")
}

 

 

 

추가적으로, 해당 플러그인은 IntelliJ에서 Spring Initializr을 통해 코프링 프로젝트를 구성하면 자동으로 추가되는 어노테이션이기에, 프로젝트 구성 방법에 따라 기본적으로 추가되어 있을 수도 있다.

 

결론

jpa, kotlin-spring 두 개의 플러그인을 통해 코틀린에서 JPA를 사용할 수 있게 되었다.

  • jpa: 기본 생성자 해결하기
  • kotlin-spring: 엔티티 클래스 open하기

 

참조

 

[Kotlin/JPA] 코틀린과 JPA를 함께 사용할 때 추가적으로 설정해야 하는 것들과 Data class

목차 개요 JPA는 자바 진영에서 DB와 연동하기 위해 사용하는 기술 중 하나이다. 자바와 100% 호환되는 코틀린의 특성상 JPA 역시 사용할 수 있긴 하지만 기본적으로 자바에 맞춰진 기술이다 보니

colabear754.tistory.com

 

Getting Started | Building web applications with Spring Boot and Kotlin

Instead of using util classes with abstract methods like in Java, it is usual in Kotlin to provide such functionalities via Kotlin extensions. Here we are going to add a format() function to the existing LocalDateTime type in order to generate text with th

spring.io

 

All-open compiler plugin | Kotlin

 

kotlinlang.org

 

No-arg compiler plugin | Kotlin

 

kotlinlang.org