manchesterandthecity 2025. 2. 12. 21:07

DTO와 VO란?

DTO(Data Transfer Object)와 VO(Value Object)는 서로 다른 목적을 가진 객체이지만, 개발자들 사이에서 혼용되어 사용되는 경우가 많음.
이러한 혼동의 원인은 일부 서적에서 VO를 DTO로 잘못 정의한 데에서 비롯됨.

 

DTO와 VO의 핵심 차이점

개념DTO (데이터 전달 객체)VO (값 객체)

목적 계층 간 데이터 전달 값 자체를 표현
변경 가능 여부 가변 (Setter 존재 가능) 불변 (Setter 없음)
로직 포함 여부 Getter, Setter만 가능 추가 로직 포함 가능
비교 방식 객체 주소(레퍼런스) 비교 속성 값이 같으면 동일 객체로 판단

DTO (Data Transfer Object)

  • 데이터 전송을 위한 바구니 역할을 하는 객체.
  • 주로 컨트롤러 → 서비스 → DAO 등 계층 간 데이터 전달을 위해 사용됨.
  • Setter가 존재하면 가변 객체가 되지만, Setter 없이 생성자로 초기화하면 불변 객체로 만들 수 있음.
  • Entity와 DTO는 반드시 분리해야 함!
    • Entity는 DB와 직접 연결되는 핵심 클래스이므로 요청 및 응답 데이터 전달용으로 사용하면 안 됨.
    • DTO를 사용하면 뷰 변경이 Entity에 영향을 주지 않음.

DTO 예제

public class CrewDto {
    private final String name;
    private final String nickname;

    public CrewDto(String name, String nickname) {
        this.name = name;
        this.nickname = nickname;
    }

    public String getName() {
        return name;
    }

    public String getNickname() {
        return nickname;
    }
}
 

VO (Value Object)

  • 값 자체를 표현하는 객체이며, 속성 값이 같으면 같은 객체로 판단됨.
  • 불변(Immutable) 객체로 설계되어야 하며, Setter를 사용하지 않음.
  • 비즈니스 로직을 포함할 수 있음.
  • equals()와 hashCode()를 오버라이딩하여 값이 동일하면 같은 객체로 판단하도록 구현해야 함.

VO 예제

public class Money {
    private final int value;

    public Money(int value) {
        this.value = value;
    }

    public int getValue() {
        return value;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Money money = (Money) o;
        return value == money.value;
    }

    @Override
    public int hashCode() {
        return Objects.hash(value);
    }
}

 

✅ 같은 금액을 가진 Money 객체들은 동일한 객체로 판단됨.


DTO vs VO 비교 정리

비교 항목 DTO (데이터 전달 객체) VO (값 객체)
목적 레이어 간 데이터 전달 값 자체를 표현
변경 가능 여부 가변 객체 가능 불변 객체
로직 포함 여부 Getter, Setter만 가능 추가 로직 포함 가능
비교 기준 객체 주소 비교 속성 값 비교 (equals, hashCode 오버라이딩)

 

📌 결론:

  • DTO는 데이터를 계층 간 전달하는 역할을 하므로 불필요한 로직을 포함해서는 안 됨.
  • VO는 값 자체를 표현하는 객체이므로 값이 같으면 같은 객체로 인식해야 함.
  • Entity는 DB와 직접 연결되는 객체이므로, DTO와 혼용해서 사용하지 않도록 주의해야 함!

 

Entity란?

Entity는 데이터베이스의 테이블과 직접 매핑되는 클래스로,
도메인 모델에서 비즈니스 로직을 포함할 수 있는 핵심 객체이다.

 

Entity의 주요 특징

  • DB 테이블과 1:1 매핑되는 클래스
  • 비즈니스 로직을 포함할 수 있음
  • 고유 식별자(PK, ID)를 통해 객체를 구분
  • DB 변경을 반영해야 하는 경우 Entity를 수정해야 함
  • Setter를 최소화하고, 필요할 때만 값 변경 허용

Entity 예제

@Entity
public class User {
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String username;
    private String email;

    protected User() {} // JPA 기본 생성자

    public User(String username, String email) {
        this.username = username;
        this.email = email;
    }

    // 비즈니스 로직 포함 가능
    public void changeEmail(String newEmail) {
        this.email = newEmail;
    }
}

 

📌 특징:

  • @Entity 어노테이션으로 DB 테이블과 매핑됨
  • @Id를 이용해 PK(고유 식별자)를 사용하여 비교
  • 비즈니스 로직을 포함할 수 있음
  • Setter 대신 변경 메서드(changeEmail())를 제공하여 데이터 변경 관리

DTO vs VO vs Entity 비교

구분 DTO (Data Transfer Object) VO (Value Object) Entity (엔티티)
목적 데이터 전달 (계층 간 통신) 값 자체를 표현 DB 테이블과 매핑
영역 컨트롤러 ↔ 서비스 ↔ DAO 도메인 모델 데이터베이스
변경 가능 여부 가변(Setter 존재 가능) 불변(Setter 없음) 가변 (Setter 최소화)
비교 기준 객체 주소(레퍼런스) 비교 속성 값 비교 (equals & hashCode 오버라이딩) ID(PK) 비교
로직 포함 여부 Getter/Setter만 포함 로직 포함 가능 비즈니스 로직 포함 가능
사용 사례 API 응답, 요청 객체 Money, Color 같은 값 표현 객체 User, Order 같은 도메인 모델

 

Entity, DTO, VO 활용 사례

사용 예시 Entity DTO VO
DB 저장용
API 요청/응답 데이터
비즈니스 로직 포함
불변 객체 ❌ (Setter가 없으면 가능)
데이터 전송 용도

정리

DTO는 데이터를 안전하게 전달하는 역할을 하며, API 응답 및 요청에서 활용됨.
VO는 값 자체를 표현하는 불변 객체이며, equals()와 hashCode()를 오버라이딩해야 함.
Entity는 DB 테이블과 매핑되며 비즈니스 로직을 포함할 수 있음.
Entity와 DTO를 반드시 분리해야 하며, VO는 특정 값 표현에만 사용해야 함.

 

📌 결론:

  • Entity를 API 응답에 직접 사용하지 않고 DTO를 활용하자!
  • VO는 값 기반 비교가 필요할 때 사용하자!
  • DTO는 데이터를 전달하는 역할에 충실하자!

 

참조
https://www.youtube.com/watch?v=J_Dr6R0Ov8E
https://www.youtube.com/watch?v=z5fUkck_RZM