DTO는 layer 간 데이터 전달 객체, VO는 값을 표현하는 객체임을 알고 개발에서 그렇게 사용한다.
그러나 속성에 의해 생기는 사용법의 차이와 그 이유를 대충 알았어서 글로 정리해본다.
DTO
DTO는 단순히 데이터를 전달하는 역할만 수행해야 한다.
따라서 Getter, Setter 이외의 로직이 있어선 안된다.
(엔티티를 받아서 DTO로 변환하는 생성자가 있는 정도? 그러나 이 역시 로직이라고 보기 어려운 것 같다.)
VO
VO를 사용할 때 기억해야 할 세 가지가 있다.
VO는 값을 표현하는 역할만을 수행한다. 그렇기 때문에 값의 안정성을 보장받기 위해 일반적으로 생성자를 통해서만 속성 값을 주입하고 setter를 통한 값 주입은 구현되지 않는다.
VO는 값을 표현하는 객체이므로 실제 객체가 달라도 값이 같다면 같아야 한다.
따라서 값이 같은 경우 equals true가 되도록 overriding 해야 한다.
hashMap, hashSet 등 hashCode를 사용하는 경우를 고려하여 hashCode 메소드를 오버라이딩한다.
hashCode를 오버라이딩 해야 하는 이유
상황 예시를 통해 이유를 알아보자.
@Getter
public static class ExampleVO{
private final int value;
public ExampleVO(int value){
this.value = value;
}
@Override
public boolean equals(Object other){
if (this == other)
return true;
if (other == null || getClass() != other.getClass())
return false;
return this.value == ((ExampleVO)other).getValue();
}
}
public static void main(String[] args){
ExampleVO vo1 = new ExampleVO(1);
ExampleVO vo2 = new ExampleVO(1);
System.out.println(vo1.equals(vo2));
HashSet<ExampleVO> set = new HashSet<>();
set.add(vo1);
set.add(vo2);
System.out.println(set.size());
}
그러나,
실행결과
true
2
따라서 다음과 같이 hashCode를 재정의해줘야 HashSet에서 역시 두 객체가 같음을 판별하고 중복제거할 수 있다.
public static class ExampleVO{
private final int value;
public ExampleVO(int value){
this.value = value;
}
@Override
public int hashCode(){
return Objects.hashCode(value);
}
@Override
public boolean equals(Object other){
if (this == other)
return true;
if (other == null || getClass() != other.getClass())
return false;
return this.value == ((ExampleVO)other).getValue();
}
public int getValue(){
return value;
}
}
실행결과
true
1
@Getter
@AllArgsConstructor
@EqualsAndHashCode(of={"value"})
public static class ExampleVO{
private final int value;
}