Entity와 매개변수가 없는 생성자

신연우·2025년 3월 1일

WIL

목록 보기
20/22

문제 상황

우리 팀에서 관리하고 있는 서버에서 갑자기 에러 로그가 발생하기 시작했습니다.
(참고 : 에러 메시지를 포함해 코드는 당시 상황과 유사하게 작성된 예시입니다)

Internal Failure.
java.lang.NullPointerException: Parameter specified as non-null is null: method kr.example.com.Example<init>, parameter originalName

로그에 남은 스택 트레이스를 통해 문제가 되는 코드를 찾아봤습니다.

data class Example(
    val id: Long? = null,
    val name: String,
    val originalName: String = name,
    val priority: Int,
)

생성자에서 명시적으로 String 타입으로 정의하고 있기 때문에 일반적인 상황이라면 컴파일 에러가 발생했을텐데 그러지 않은 것이 굉장히 의아했습니다. 그래서 생성자를 호출하는 곳이 어디에 있는지 살펴봤습니다.

@Entity
@TableName(name = "example")
class ExampleEntity(
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY) val id: Long? = null,
    val name: String,
    val originalName: String = name,
    val priority: Int,
) {
    fun toDomain(): Example {
        return Example(
            id = id,
            name = name,
            originalName = originalName,
            priority = priority,
        )
    }
}

생성자를 호출하는 건 Entity 객체의 toDomain 메서드 뿐이었습니다. 하지만 코드 상으로는 NPE가 발생하는 것이 불가능해보였습니다.

+-------------+------------+----+---+--------------------+-----------------+
|Field        |Type        |Null|Key|Default             |Extra            |
+-------------+------------+----+---+--------------------+-----------------+
|id           |bigint      |NO  |PRI|null                |auto_increment   |
|name         |varchar(255)|NO  |   |null                |                 |
|original_name|varchar(255)|YES |   |null                |                 |
|priority     |int         |NO  |   |null                |                 |
|created_at   |datetime(6) |NO  |   |CURRENT_TIMESTAMP(6)|DEFAULT_GENERATED|
+-------------+------------+----+---+--------------------+-----------------+

하지만 DB에 정의된 스키마를 살펴보니 original_name 컬럼이 nullable했으며 해당 컬럼이 NULL로 들어가 있는 row도 있다는 것을 확인했습니다.

Hibernate는 매개변수가 없는 생성자를 필요로 한다

The entity class must have a public or protected no-argument constructor. It may define additional constructors as well.
Hibernate User Guide

Hibernate에서는 Entity 클래스가 반드시 매개변수가 없는 public 혹은 proceted 생성자가 정의되어 있어야 합니다. 왜냐하면 DB에서 가져온 정보를 reflection을 통해 Entity 클래스의 인스턴스를 만들기 때문입니다.

따라서, 이번에 문제가 발생한 케이스 역시 매개변수가 없는 생성자 + reflection을 통해 Entity 클래스의 인스턴스를 만들었습니다. 애플리케이션에서 정의한 생성자에서는 originalName의 기본값을 name으로 주고 있었지만 해당 생성자를 이용해 인스턴스를 만들지 않았으니 NULL로 초기화가 되어버린 것입니다.

이 과정에서 toDomain 메서드를 호출하면 String으로 정의된 필드에 NULL을 넣으려고 하면서 NullPointerException이 발생하게 되는 것입니다.

번외) 그런데 예시 코드에는 매개변수가 없는 생성자가 없는데요?

예시로 작성한 코드에는 매개변수가 없는 생성자가 존재하지 않는데요. Kotlin과 Spring boot를 사용할 때 다음과 같은 플러그인을 설정하면 자동으로 Entity 클래스에 매개변수가 없는 생성자를 추가해줍니다.

plugins {
    kotlin("plugin.jpa")
}
profile
남들과 함께하기 위해서는 혼자 나아갈 수 있는 힘이 있어야 한다.

0개의 댓글