Web Programming Language/JAVA
DTO와 VO
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