욱 연구소

슬기로운 개발생활

프로그래밍/Java

VO와 DTO 는 다르게 부르는 같은 말?

wook-lab 2022. 1. 27. 14:10
반응형
VO vs. DTO

DTO와 VO는 분명히 다른 개념과 목적을 가지고 사용하는 객체 이지만 그것에 대한 구분 없이 혼동하여 사용하는 경우가 많다. 그의 원인으로는 일반적으로 ⌜Core J2EE Patterns: Best Practices and Design Strategies⌟ 책의 초판에서 데이터 전송용 객체를 VO로 표기를 했었고 2판에서는 해당 객체를 TO로 정정하여 표기를 하였다고 한다. 이로인하여 DTO와 VO를 혼동하게 된 것으로 보고 있다.

개인적으론 실질적으로 개발자들간에 협업이나 커뮤니케이션을 할때 단편적인 개념으로 이야기를 하다보니 각각 다르게 이해를 하고 있어 더욱이 혼동하고 혼용하여 사용하게 된 것 같다. 

 

개념 정리하기

DTO(Data Transfer Object)

순수하게 값을 담아 전송하는 객체로 Layer간 데이터 전송을 위해 사용하는 클래스다.
getter/setter 메서드만 가지고 있으며, 그 외의 행동(method)은 갖지 않는다. (즉 비즈니스 로직을 포함하지 않는다)

(setter메서드를 제공하면 가변객체로 사용할 수 있지만, 생성자를 통해 필드변수를 초기화하여 불변객체로 사용한다면 임의로 값의 변경을 차단하므로 비즈니스 로직 처리 전까지의 값의 원자성을 보장받을 수 있다.)

DAO(Data Access Object)에서 DB로부터 조회한 데이터를 저장할때 사용하거나 ViewLayer에서 View를 위한 클래스로 보통 사용한다. 즉 계층간에 데이터를 넘겨줄 때 사용하게 된다.

// 가변 DTO
@Getter
@Setter
public class User {
    private String name;
    private int age;
}
 
 
// 불변 DTO
@Getter
@RequiredArgsConstructor
public class User {
    private final String name;
    private final int age;
}

 

VO(Value Object)

순수하게 값을 표현하는 객체로 특정한 비즈니스의 값을 담는 클래스다.
(getter 외의 행동(method)을 가질 수 있어 비즈니스 로직 포함이 가능하다)

값 자체를 표현하기 때문에 불변객체로 생성해야 하고, 같은 값을 담고 있다면 같은 객체로 봐야하기 때문에 hashCode()와 equals()를 오버라이드하여 반드시 재정의 해야 한다.

@Getter
@RequiredArgsConstructor
public class User {
    private final String name;
    private final int age;
 
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof User)) return false;
        User that = (User) o;
        return age == that.age && Objects.equals(name, that.name);
    }
 
    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}

 

불변객체는 VO다!?

단편적으로 본다면 틀린말은 아니지만, 사전 지식이나 정확한 개념이 없는 경우에는 'DTO는 불변객체가 아닌가?' 라고 생각할 수 있다. DTO도 setter를 제거하여 불변객체로 사용할 수 있으며, VO의 경우 반드시 불변객체로 사용해야 한다.

DTO와 VO 둘다 불변객체로 사용이 가능하기 때문에 해당 DTO와 VO에 대한 구분은 객체에 비즈니스로직을 포함하느냐 그리고 같은 객체로 보기위한 hashCode()와 equals()가 재정의 되어 있느냐에 따라 구분할 수 있다.

불변객체를 특정 객체의 정의로 보지말고 특징 또는 사용 방법 중 하나로 봐야 한다.

 

 

비슷한 성격의 객체

Entity(Domain Model로 표현하기도 함)

실제 DB테이블과 매칭되는 클래스로 ID를 가지며 테이블과 링크될 클래스다.
도메인 로직만 가질 수 있다. 여기서 도메인 로직이란 순수하게 특정 영역/분야의 도메인에서 문제 해결을 위해 수행해야할 로직을 말한다.

변경이 많이 발생하는 Presentation(ViewLayer)을 위한 필드나 로직을 갖지 않도록 하고, Persistent 만을 위해 사용하도록 경계를 분리해야 의존성을 줄일 수 있어 테스트코드 작성이 용이하고 유지보수와 리팩터링에도 이점이 된다.

 

Model

DomainLayer에서 비즈니스 로직(문제해결)을 처리하기 위해 값(상태)을 저장하고 책임에 맞는 메서드(행동)을 가지는 클래스다.
오브젝트에서 말하는 객체(객체는 상태와 행동을 갖는다)에 포함되는 것 같다.

Model객체?

현재 우리팀에선 데이터를 단순 저장하는 개념으로 보기도 한다. (일반적인 DTO와 구분하기 위한 정도로 생각) 일반적인 트랜잭션 처리가 필요한 DB로 사용하기 보다 검색을 위한 데이터를 가져오는 용도로 사용하다보니 JPA와 Entity를 활용하기에 적합하지 않다. 때문에 일반적인 DTO와 구분하고 DB로부터 조회한 값을 저장하고 있는 클래스로 사용하고 있다.

 

 

정리

 

  DTO VO Entity Model
용도 Layer간 데이터 전달 의미있는 값 표현 DB 테이블과 매핑되는 클래스 도메인의 문제 해결
가변/불변 setter 존재 시 가변
getter만 있다면 불변
불변 가변 기본적으로 불변객체
필요에 따라 가변
로직(행동) 로직을 포함하지 않음 로직을 포함할수 있음 로직을 포함할수 있음 로직을 포함
그 외 특징   객체의 필드 값이 모두 같으면 같은 객체
hashCode()/equals() 재정의 필요
  값(상태)과 메서드(행동)을 포함

 

반응형