TIL(Today I Learned)

TIL - Entity의 Setter 사용을 지양하기

Happy._. 2024. 6. 19. 21:58

Todo 프로젝트에서 Entity 클래스의 프로퍼티들을 다음과 같이 설정하였다.

  • 수정이 가능한 프로퍼티에 대해서는 var로 선언
  • 수정이 될 일이 없는 프로퍼티들은 val로 선언

Todo Entity에서 title, content, writer, completed는 수정 요청 시 변경되는 값이다.

@Entity
@Table(name = "todo")
class Todo private constructor(
    var title: String,
    var content: String,
    var writer: String,
    
    // ...
    
) : BaseEntity() {

    // ...

    @Enumerated(EnumType.STRING)
    var completed: TodoCompleted = TodoCompleted.FALSE
}

 

그리고 해당 Entity의 값을 Service에서 직접 Setter를 통해 값을 변경했었다.

@Transactional
override fun updateTodo(todoId: Long, request: UpdateTodoRequest): TodoResponse {
    
    // ...

    todo.title = request.title
    todo.content = request.content
    todo.writer = request.writer   
}

 

직접 Setter를 통해 값을 변경하는 것이 문제가 되는 이유

  • 변경사항이 생겼을 때 어느 부분에서 변경했는지 찾기 어려움
  • 변경한 의도를 파악하기 어려움

Setter를 지양하기 위해 private set 설정을 하려고 했다.

그런데 Kotlin에서 Entity는 open 클래스이기 때문에 private를 사용할 수 없다.

대신 protected set으로 설정해 setter에 대한 접근을 차단할 수 있다.

protected: 상속받는 인터페이스, 클래스 또는 자식 클래스에서만 접근 가능

 

protected set을 설정하고 setter 대신 update 함수를 적용한 코드는 다음과 같다.

@Entity
@Table(name = "todo")
class Todo private constructor(
    title: String,
    content: String,
    writer: String,

    // ....
    
) : BaseEntity() {

    // ..

    var title: String = title
        protected set
    var content: String = content
        protected set
    var writer: String = writer
        protected set

    @Enumerated(EnumType.STRING)
    var completed: TodoCompleted = TodoCompleted.FALSE
        protected set

    // ...

    fun update(title: String?, content: String?, writer: String?, completed: TodoCompleted?) {
        title?.let { this.title = it }
        content?.let { this.content = it }
        writer?.let { this.writer = it }
        completed?.let { this.completed = it }
    }
}

 

이제 Service에서는 Setter 사용할 수 없고 update 함수를 통해 Entity에서 값을 업데이트해야 한다.

@Transactional
override fun updateTodo(todoId: Long, userId: Long, request: UpdateTodoRequest): TodoResponse {

    // ...

    todo.update(
        title = request.title,
        content = request.content,
        writer = request.writer,
        completed = request.completed
    )

    return todo.toResponse()
}

 

처음에는 protected set 설정을 하지 않고 Setter를 사용하지 않으면 되지 않을까? 하는 생각도 했었지만 사람 마음이란 게 코드를 작성하다 보면 더 편한 방법을 찾게 되고 그러다가 "구현이 먼저다"라는 합리화를 통해 Setter를 사용하게 될 확률이 있기 때문에 이런 부분을 예방하고자 protected set 설정을 하는 것이 좋다고 생각한다.