VO와 DTO의 차이는?
최근 면접에서 VO와 관련된 질문을 받았는데 VO라는 용어가 생소하여 대답을 제대로 못했습니다.
하여 VO에 대해 공부하던 도중 VO와 DTO가 같다고 생각했던 제 생각이 잘못되었음을 알게 되었고
공부하며서 VO와 DTO의 개념이 혼동되기 쉬운 개념이라 생각이 되어 나중에라도 했갈리지 않기 위해 공부해봤습니다.
※주관적인 견해가 많이 담겨있으므로 틀린 부분은 지적해주시면 감사하겠습니다.
먼저 VO란?
VO는 Value Object의 약자입니다. VO는 그 자체로 값을 가지는 데이터 객체라 할 수 있습니다.
예를 들어 요즘 이슈가 되고있는 팬톤컬러의 유료화 이슈를 조금 예시로 들어 설명해보겠습니다.
팬톤컬러에서 사용되는 RED, BLUE, PINK 등등의 컬러들은 모두 고유의 값을 가지고 있습니다. 어딜가나 팬톤컬러의 RED는 항상 똑같은 팬톤컬러의 RED인 셈이죠.
그리고 VO는 항상 red only이기 때문에 DTO처럼 Setter, Getter 메서드가 존재하지 않습니다.
마지막으로 앞서 얘기했듯 팬톤컬러의 레드는 어디서나 같은 레드임을 증명하기 위해 equals()와 hashCode() 메서드를 오버라이딩해줄 필요가 있습니다.
이 둘을 오버라이딩하지 않고 비교하면 테스트로 확인해봤을때 같은 값으로 생성한 두 VO가 서로 다르다고 나오기 때문입니다.
DTO란?
DTO는 Data Transfer Object의 약자입니다. 직역하는 그대로 데이터를 전송하기 위한 객체입니다.
DTO는 그 자체로 값을 가진다기보다는 특정한 규칙에 따라 데이터를 전송하기 위한 틀이라고도 할 수 있겠습니다.
예를 들면 한 교실의 학생의 정보를 담고 있는 DB에 이름/성별/연락처/주소/생년월일 정보를 저장해두고 있다 할때에 데이터베이스에 이 정보를 저장하기 위해서는 이름/성별/연락처/주소/생년월일 의 데이터를 전달해줘야합니다. 마구잡이로 정보를 막 전달해주면 뭐가 누구의 연락처인지, 뭐가 누구의 생일인지를 알 수가 없겠죠?
이때에 이름/성별/연락처/주소/생년월일 의 형식을 가지도록 규정한 것을 DTO라 할 수 있겠습니다.
DTO에는 setter와 getter가 존재합니다. 그리고 DTO에는 로직을 가지는 메서드가 존재할 수 없습니다.
그래서 DTO와 VO의 차이점은?
결정적인 부분으로는
DTO는
TestDTO dto1 = new TestDTO();
TestDTO dto2 = new TestDTO();
일 때에 dto1 != dto2 가 됩니다.
VO는
TestVO vo1 = new TestVO();
TestVO vo2 = new TestVO();
일 때 vo1 == vo2 가 됩니다.
코드로 비교를 해보기 위해 DTO, VO 그리고 테스트 코드를 한번 보겠습니다.
01. VO 클래스
02. DTO 클래스
03. 테스트 코드
01. VO 클래스
public class Pantone {
private int red, green, blue;
public Pantone(int red, int green, int blue) {
this.red = red;
this.green = green;
this.blue = blue;
};
public final static Pantone Red = new Pantone(255,0,0);
@Override
public boolean equals(Object obj) {
if(obj.getClass() != getClass()) return false;
if(this == obj) return true;
Pantone pantone = (Pantone)obj;
return red == pantone.red && green == pantone.green && blue == pantone.blue;
}
@Override
public int hashCode() {
return Objects.hash(red,green,blue);
}
}
VO에서는 생성자를 통해 사용하기도 하고 VO자체에 static으로 Red라는 컬러값을 하나 만들어보기도했습니다.
이 부분은 다른 분의 작성글을 볼때에 이렇게 예제를 들어주셔서 저도 참고했습니다.
02. DTO 코드
public class TestDTO {
private int red;
private int green;
private int blue;
public int getRed() {
return red;
}
public void setRed(int red) {
this.red = red;
}
public int getGreen() {
return green;
}
public void setGreen(int green) {
this.green = green;
}
public int getBlue() {
return blue;
}
public void setBlue(int blue) {
this.blue = blue;
}
}
03. 테스트 코드
@Test
void blue() {
Pantone blue1 = new Pantone(0, 0, 255);
Pantone blue2 = new Pantone(0, 0, 255);
assertThat(blue1).isEqualTo(blue2);
}
@Test
void red() {
Pantone red1 = Pantone.Red;
Pantone red2 = Pantone.Red;
assertThat(red1).isEqualTo(red2);
}
@Test
void hashBlue() {
Pantone blue1 = new Pantone(0, 0, 255);
Pantone blue2 = new Pantone(0, 0, 255);
assertThat(blue1).hasSameHashCodeAs(blue2);
}
@Test
void hashRed() {
Pantone red1 = Pantone.Red;
Pantone red2 = Pantone.Red;
assertThat(red1).hasSameHashCodeAs(red2);
}
@Test
void dtoTest() {
TestDTO dto1 = new TestDTO();
TestDTO dto2 = new TestDTO();
assertThat(dto1).isEqualTo(dto2);
}
테스트 코드의 결과 입니다.
첫번째 테스트 : VO의 static인 red를 두개 생성하여 비교한 결과
두번째 테스트 : 같은 값을 가진 두개의 VO를 생성하여 비교한 결과
세번째 테스트 : 같은 값을 가진 두개의 VO를 생성하여 hash를 비교한 결과
네번째 테스트 : VO의 static인 red를 두개 생성하여 hash를 비교한 결과
다섯번째 테스트 : DTO를 두개 생성하여 비교한 결과
결과적으로 DTO를 비교했을때에만 서로 다르다고 나오는 것을 확인할 수 있었습니다.