티스토리 뷰

자바에서 entity 를 정의할 때는 id 를 어떻게 선언할 것인지에 대한 고민을 크게 할 필요 없었다. 보통 인스턴스 필드 선언부의 가장 최상단에 정의했을 것이다.

@Entity
@Table(name = "person")
public class Person {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "person_id")
    private Long id;

    @Column(name = "person_name")
    private String name;
}

하지만 코틀린에서 정의할 때는 이런저런 고민을 하게된다. 아마도 코틀린이 자바보다 다양한 문법을 지원하고 있다보니 괜시리 더 고민이 되게 되는 것 같다.

@Entity
@Table(name = "person")
class Person(
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "person_id")
    val id: Long?,

    @Column(name = "person_name")
    val name: String
)

 

가장 보편적인 방법이 이 방법인 것 같다. 코틀린의 기본 생성자 문법을 사용하면서 자바에서 그랬듯 id 프로퍼티를 최상단에 선언한다.

 

# 프로퍼티의 위치

기본 생성자 내에서 id 프로퍼티의 위치도 고민거리인데 자바에서 id 선언 위치는 생성자와는 상관없는 위치였지만 코틀린에서 기본 생성자 방식으로 선언하게되면 그게 곧 생성자 파라미터 위치인지라 최상단에 선언할 경우 생성자의 첫번째 파라미터도 id 가 되게된다.

val person = Person(null, "LichKing")

생성자를 호출하여 객체를 생성할 경우 위의 코드처럼 null 을 명시적으로 넣어줘야하는데 영속화되기 전 엔티티를 만들때는 항상 id 가 null 일 것이므로 반복적으로 null 을 넣어줘야한다.

 

# default parameter 사용

@Id                                                 
@GeneratedValue(strategy = GenerationType.IDENTITY) 
@Column(name = "person_id")                         
val id: Long? = null,

코틀린의 장점 중 하나인 default parameter 문법을 쓰면 해소할 수 있지만, 생성자가 가장 위에 있는 상태에서 default parameter 를 주게되면 생성자를 호출하는 쪽에서는 named argument 문법이 강제된다.

val person = Person(name = "LichKing") // null 을 넣는 코드는 사라졌지만 named argument 가 강제된다.

필요에 의해 named argument 를 사용하는건 적절하지만 null 초기화 피하려고 무조건적으로 named argument 를 사용하게되면 코드가 번잡해지는 경향이 있어 썩 이쁘게 보이지 않는다.

 

# class body 에 선언

@Entity
@Table(name = "person")
class Person(
    @Column(name = "person_name")
    val name: String
) {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "person_id")
    val id: Long? = null
}

이런식으로 id 를 생성자에서 빼는 방식도 있다. 이렇게 선언하면 명시적으로 null 을 전달하지 않으면서도 생성자를 호출할때 named argument 가 강제되지도 않는다. 다만 이 방식도 문제가 없지 않은데, 영속화되지 않으면서 id 가 필요할때 외부에서 id 를 넣어줄 방법이 없다. 영속화되지 않으면서 id 가 필요한 경우가 언제일까? 바로 테스트코드다. 엔티티를 대상으로 단위 테스트를 작성할때 id 를 이용하는 코드가 있을 수 있는데 위 방식을 사용하게되면 id 를 넣어줄 방법이 없기 때문에 문제가 된다. Mockk 같은 라이브러리를 이용하는 수밖에 없다. 나는 Mockk 없이 정상적인 상태의 객체를 생성할 수 없다면 그걸 POJO(POKO 가 더 맞는거같다)라고 부를 수 있을지 의문이다.

 

# 생성자 최하단에 선언

@Entity
@Table(name = "person")
class Person(
    @Column(name = "person_name")
    val name: String,

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "person_id")
    val id: Long? = null
)

현 시점에서 내가 가장 선호하는 방식이다. id 에 default parameter 를 넣어놨기에 필요하지 않을땐 굳이 null 을 전달할 필요도 없으며 호출하는 쪽에서 named argument 방식이 강제되지도 않는다. 그리고 테스트 코드에서 id 주입이 필요하면 그땐 명시적으로 임의의 id 를 전달할 수도 있다.

 

어떻게보면 정말 별거 아닐 수도 있는 고민이지만 id 프로퍼티를 어떻게 선언하는게 가장 좋을지 이리저리 고민을 많이 했었다. 내가 얻은 결론은 기본 생성자의 최하단 파라미터로 선언하는게 가장 낫다는 것이다. 다음 포스팅에선 Long 과 Long? 타입중 어떤걸 선택할지에 대해 좀 더 고민해본 내용을 적어보겠다.

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함