-
Notifications
You must be signed in to change notification settings - Fork 0
왜 var로 선언된 property는 private set이 안 될까?
코틀린에서는 var
로 선언된 property(프로퍼티)
에 대해 setter
를 막는 private set
기능을 제공하지 않습니다.
스프링 + 코틀린 환경에서 개발하다보면 var
로 선언된 프로퍼티의 setter
를 막아야 하는 경우가 존재합니다.
하지만 왜 코틀린에서는 프로퍼티의 private set
기능을 제공하지 않을까요?
이에 대한 예제는 JPA
를 사용하기 위한 Entity
를 구성하는데 있어서 발생합니다.
처음 코틀린으로 엔티티를 작성하면 보통 다음과 같이 작성하게 됩니다.
@Entity
@Table(name = "person")
class Person(
@Column(name = "name")
val name: String,
@Column(name = "age")
val age: Int,
) {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
var id: Long = 0
}
근데 이렇게 엔티티로 생성한 Person
객체의 상태를 변경하려면 어떻게 해야 할까요?
name
과 age
프로퍼티는 val
로 선언했으니 setter
없이 getter
만 존재하게 됩니다.
그래서 Person
객체의 상태를 변경할 수가 없습니다.
그렇다고 var
로 선언하자니 아무 의미 없는 setter
가 생겨 JPA
의 규칙에 어긋납니다.
그럼 var
에 private set
을 해야 하는데 다음 코드는 컴파일 에러가 발생합니다.
@Entity
@Table(name = "person")
class Person(
@Column(name = "name")
var name: String
private set, // compile error
@Column(name = "age")
var age: Int
private set, // compile error
) {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
var id: Long = 0
}
제목에서 말했듯이 코틀린에서는 프로퍼티에 대해 private set
기능을 제공하지 않습니다.
그러면 실제로 개발할 때는 어떻게 우회하여 해결할까요?
@Entity
@Table(name = "person")
class Person(
name: String,
age: Int,
) {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
var id: Long = 0
@Column(name = "name")
var name = name
private set
@Column(name = "age")
var age = age
private set
fun eatRiceCakeSoup() {
age++
}
}
본 해결책은 제가 개발할 때 사용하는 방식으로 가장 합리적으로 우회했다고 생각하는 방식입니다.
하지만 생성자에도 변수를 작성해야하고, 클래스의 바디에도 작성해야 하기 때문에
단순한 엔티티가 길어지는 문제점을 가지고 있습니다.
@Entity
@Table(name = "person")
class Person(
@Column(name = "name")
val name: String,
@Column(name = "age")
val age: Int,
) {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
var id: Long = 0
fun eatRiceCakeSoup(): Person {
val person = Person(
name = name,
age = age + 1,
)
person.id = id
return person
}
}
이렇게 엔티티를 변경할 때마다 새로운 엔티티를 생성하게 되면 불변성 유지도 되고
setter
를 막을 수도 있지만, JPA
의 더티 체킹 기능을 사용할 수 없다는 최대의 문제점이 있습니다.
더티 체킹을 이용하지 않으면 엔티티가 변경될 때 UPDATE
문을 날리기 위해서
엔티티의 변경마다 save()
메소드를 호출해야 합니다.
이는 로직을 더럽히는데 주축을 담당하게 될 수도 있습니다.