Kotlin으로 JPA 사용시 주의점(with Access Strategy)

dojinyou·2023년 5월 21일
0

Kotlin과 Spring을 이용해 개발하다보면 자연스럽게 Spring Data JPA를 이용하는 경우가 많습니다. JPA를 사용하면서 가장 먼저 마주하게 되는 것이 엔티티 클래스를 정의하는 것입니다. 엔티티 클래스를 정의함에 있어서 Kotlin의 특성과 JPA를 정확하게 알지 못해 경험했던 문제를 공유하고 이에 대한 정확한 지식과 저의 생각을 공유하고자 합니다.

우선 문제를 이해하기 위해 kotlin propertiesAccess Strategy에 대해 간단히 알아보겠습니다.

Kotlin Properties

코틀린 공식 문서의 properties 에는 다음과 같이 되어 있습니다.

Properties in Kotlin classes can be declared either as mutable, using the var keyword, or as read-only, using the val keyword.

간단하게 변경 가능한 프로퍼티는 var 키워드를, 읽기 전용(불변) 프로퍼티는 val 키워드를 이용해 선언할 수 있다는 것입니다.

The full syntax of a read-only property declaration differs from a mutable one in two ways: it starts with val instead of var and does not allow a setter

또한 읽기 전용(불변) 프로퍼티는 var 대신 val로 시작(선언)한다는 것과 setter가 허용되지 않는 다는 것이 차이점이라고 합니다. 여기서 핵심적인 내용은 val 키워드는 setter를 허용하지 않는 다는 점입니다.

Access Strategy

Spring Data JPA의 구현체로 주로 사용되는 Hibernate의 User Guide의 Access strateegies를 보면 2가지 전략을 소개하고 있습니다.

As a Jakarta Persistence provider, Hibernate can introspect both the entity attributes (instance fields) or the accessors (instance properties). By default, the placement of the @Id annotation gives the default access strategy.

When using property-based access, Hibernate uses the accessors for both reading and writing the entity state.

When using field-based access, adding other entity-level methods is much more flexible because Hibernate won’t consider those part of the persistence state.

프로퍼티 기반의 접근 방식은 엔티티의 상태에 대한 읽기와 쓰기에서 모두 접근자(메서드)를 이용한다고 되어 있습니다. 한편 필드 기반의 접근 방식은 특별한 접근에 대한 설명이 되어 있지 않습니다. 또한 Id 어노테이션에 의해서 기본 접근 전략이 결정된다고 합니다.

문제 상황

@Entity
class Sample(
    @Id
    val id: String = UUID.randomUUID().toString(),

    @get:Column(length = 32, nullable = false, updatable = false)
    val immutableField: String,
)

이 엔티티 클래스의 문제점은 Access Strategy의 혼용하고 있다는 것입니다. @Id에 의해서 현재는 필드 기반 접근 방식을 기반 접근 방식으로 설정하였습니다. 하지만 immutableField에 대해서 프로퍼티 기반의 접근 방식을 사용하고 있습니다. 이럴 경우 실제로 @Column에 대한 정보는 전혀 이용하지 않게 됩니다. 실제로 jpa의 ddl-auto 옵션을 통해 table 생성 ddl를 자동 생성하도록 하면 아래와 같이 기본 값으로 처리된 것을 확인할 수 있습니다.

// hibernate의 auto-ddl
Hibernate:
	create table sample (
		id varchar(255) not null,
	    immutable_field varchar(255),
	    primary key (id)
	) engine=InnoDB

이 문제는 일반적으로 기본 접근 전략과 동일하도록 @get:을 지워주워 기본 전략과 동일하도록 맞춰주거나 @get:Access(AccessType.PROPERTY)을 이용하여 기본 접근 전략을 Overriding하여 해결할 수 있습니다.

하지만 예시의 코드에서는 기본 전략을 Overriding을 할 수 없습니다. 그 이유는 바로 val로 선언된 불변 필드에 대해서는 Property-based access 사용할 수 없기 때문입니다. 프로퍼티 베이스 접근방법은 게터 와 세터 메서드를 이용해 필드에 접근하게 되는 데 val로 선언된 불변 프로퍼티는 별도의 세터가 생성되지 않기 때문입니다. 실제로 아래와 같이 코드를 수정하게 되면 다음 에러 메세지를 보시게 됩니다.

@Entity
class Sample(
    @Id
    val id: String = UUID.randomUUID().toString(),

    @get:Access(AccessType.PROPERTY)
    @get:Column(length = 32, nullable = false, updatable = false)
    val immutableField: String,
)

// org.hibernate.PropertyNotFoundException: Could not locate setter method for property

이 문제는 val 키워드가 아닌 var 키워드로 변경하거나 val 키워드의 경우 접근 전략을 필드 접근 전략으로 설정해주어야 합니다.

개인적인 생각

개인적으로 엔티티 클래스를 자유롭게 정의하기 위해서는 필드 접근 전략이 좋다고 생각합니다. 적절하게 불변 객체의 변경을 제어할 수 있는 val 키워드를 사용할 수 있기 때문입니다.

하지만 코틀린에서는 언어 차원에서 필드가 아닌 프로퍼티로 정의하고 사용하도록 하고 있어 필드에 대한 접근 전략이 언어 레벨에서 문제가 없는 지 조금 더 확인이 필요할 것 같습니다.

profile
더 좋은 세상을 만드는 데 기술로 기여하고 싶습니다

0개의 댓글