우테코
값 객체(VO) : 일반 클래스 vs record
mint*
2024. 10. 26. 18:36
728x90
값 객체(VO) : 일반 클래스 vs record
우테코 프리코스에서 코드 리뷰를 하는 과정에서, 값 객체를 구현할 때 일반 클래스와 record 중 어느 것으로 구현하는 것이 적절한지 이야기가 나왔다.
record가 보일러플레이트 코드를 줄여주고 불변 객체를 생성해주는 장점이 있어 값 객체에 적합해보이지만, 과연 도메인 객체로 사용하는 것이 적절할까?
record 간단 설명
- java 16에서 정식 도입되었다.
- 데이터 전달을 위한 불변 객체를 편리하게 생성할 수 있다.
- 필드가 모두
private final
이다. 각 필드의 이름으로getter
가 생성된다. - 컴파일러가 생성자와
equals()
,hashCode()
,toString()
을 자동으로 생성한다.
- 필드가 모두
final
클래스로 선언되므로 상속이 불가능하다.
예시
일반 클래스
public final class Delimiter {
private final String value;
public Delimiter(String value) {
this.value = value;
}
public String value() {
return this.value;
}
}
record
public record Delimiter(String value) { }
record 사용하는 곳
- DTO
- 불변 객체
- 단순 값 타입
- config
값 객체(VO) 간단 설명
- 도메인 개념을 추상화하여 값으로 취급한 객체
- 불변성, 동등성, 유효성 검증 등을 보장해야 한다.
- 불변성 :
final
필드,setter
금지 - 동등성 : 식별자가 아닌 값으로 동등성을 판단한다. -
equals()
,hashCode()
재정의 - 유효성 검증: 생성 시점에 유효성을 보장한다. -
validate()
- 불변성 :
로직을 캡슐화한다.
값 객체(VO) 구현 : 일반 클래스 vs record
캡슐화
- 일반 클래스 : 모든 필드에 대한 접근 제어를 세밀하게 조정할 수 있다.
public
,protected
,default
,private
- record : 모든 필드가 public 접근제어자(
getter
)로 노출된다.
객체를 record로 구현하면 캡슐화를 보장하지 못한다.
값 객체라고 모든 필드를 공개할 필요는 없다
getter
를 제공하지 않는 값 객체가 존재한다.- 내부 구현을 숨기고 필요한 동작만 공개하는 것이 좋다.
- 아래 Password에서 value와 salt 필드 값이 노출되어서는 안된다.
public class Password {
private final String value;
private final String salt;
private Password(String value) {
this.value = value;
this.salt = generateSalt();
}
public String maskedValue() {
return "*".repeat(value.length());
}
public boolean matches(String inputPassword) {
return hashWithSalt(inputPassword).equals(value);
}
}
- record 사용시 모든 필드가 공개되어 캡슐화가 깨진다.
public record Password(String value, String salt) {
public String getMaskedValue() {
return "*".repeat(value.length());
}
}
사용 목적
- 일반 클래스 : 도메인 개념과 규칙을 표현한다.
- record : 데이터 전달 및 저장을 위해 사용된다.
record는 dto처럼 데이터를 단순히 담고 있는 컨테이너 역할로 보인다.
검증을 수행하고 캡슐화된 도메인 로직을 표현하기에는 적절하지 않다.
기타 record 제약사항
- 유지보수 어려움 :
private
필드를 추가해야 한다면record
를 일반 클래스로 수정해야한다. - 실수 유발하는 코드 :
record
에 필드를 추가하여 실수로 캡슐화가 깨질 수 있다.
실수를 방지하는 것이 중요하므로 모든 필드가 공개되어있더라도 도메인 객체로는 사용하지 않는 것이 좋다.
결론
- dto나 설정값 등 단순 데이터 전달 목적으로만
record
를 사용하자. - 도메인을 표현하는 값 객체는 일반 클래스로 구현하자.
728x90