
개요
데이터를 다루는 객체를 설계하다 보면 VO(Value Object)와 DTO(Data Transfer Object)를 자주 마주하게 됩니다.
얼핏 보면 비슷해 보이는 이 두 객체는 각각의 특성과 사용 목적이 명확히 다릅니다. 이번 글에서는 VO와 DTO의 차이점에 대해 정리하고자 합니다.
VO(Value Object)란❓
VO는 도메인에서 한 개 또는 그 이상의 속성들을 묶어서 특정 값을 나타내는 객체입니다.
또한 VO(Value Object)는 값을 표현하는 객체로, 불변(immutable) 특성을 가지며, 동일한 값을 가지면 같은 객체로 취급됩니다.
VO 예시
// VO: 값 자체를 표현, 불변
public class Money {
private final int amount; // 불변(final)
public Money(int amount) {
this.amount = amount;
}
// 새로운 객체 생성
public Money add(Money other) {
return new Money(this.amount + other.amount);
}
// 값이 같으면 같은 객체
@Override
public boolean equals(Object obj) {
if (!(obj instanceof Money)) return false;
return amount == ((Money)obj).amount;
}
}
VO의 특징
1. 불변성(Immutable)
- 번 생성된 후 값을
변경할 수 없음 - 값 변경이 필요한 경우 새로운 객체 생성
2. 동등성(Equality)
- 모든 속성값이 같다면 같은 객체로 판단
- equals()와 hashCode() 메서드 재정의 필요
DTO(Data Transfer Object)란❓

DTO는 계층 간 데이터 전송을 위해 사용되는 객체입니다.
DTO(Data Transfer Object)는 애플리케이션의 다양한 레이어나 시스템 간에 데이터를 전송하기 위해 사용하는 단순화된 데이터 구조입니다.
DTO는 데이터 전송에 필요한 필드만 포함하며, 비즈니스 로직이나 메서드는 포함하지 않아 데이터의 효율적 전송과 관리가 용이합니다.
DTO 예시
// DTO: 데이터 전달용
public class UserDTO {
private String name; // 가변
private int age; // 가변
// getter/setter
public void setName(String name) {
this.name = name;
}
}
DTO의 특징
1. 가변성(Mutable)
- setter를 통한 값 변경 가능
- 데이터 전달이 주 목적
2. 동일성(Identity)
- 인스턴스가 다르면 다른 객체로 취급
- 값이 모두 같아도 별개의 객체
3. 단순 데이터 전달
- Getter/setter 외 비즈니스 로직
미포함
DTO(Data Transfer Object)란❓
| 구분 | VO (Value Object) | DTO (Data Tranfer Object) |
| 목적 | 값 자체를 표현 | 데이터 전달 |
| 가변 / 불변 | ✅ 불변성 | setter 존재 시 가변 setter 미 존재 시 불변 |
| 동등 결정 | 송성값이 모두 같으면 같은 객체 ✅ | 속성값이 모두 같아도 같은 객체 ❌ |
| 로직 | getter/setter외의 로직을 포함할 수 있다. | getter/setter외의 로직을 갖지 않는다. |
| 사용 예시 | 금액, 좌표, 날짜 등 | API 요청 / 응답 데이터 |
결론
소프트웨어 설계의 대가이자 리팩토링, 엔터프라이즈 애플리케이션 아키텍처 패턴의 선구자인 마틴 파울러의 저서에 따르면 DTO와 VO의 개념이 혼용되어 개발자 커뮤니티에 혼란을 초래했다고 언급한 바 있습니다.
실제로 저를 포함하여 많은 개발자들이 두 개념을 유사하게 인식하는 이유는 둘 다 데이터를 전달하는 역할을 하기 때문인 것 같습니다.
그러나 VO는 동일한 값을 가지면 같은 객체로 판단하는 불변 객체이며, 비즈니스 로직을 포함할 수 있습니다.
반면, DTO는 단순한 데이터 전달 객체로, 불변성을 보장하지 않으며 비즈니스 로직을 포함하지 않습니다.
'TIL,일일 회고' 카테고리의 다른 글
| [TIL, 일일 회고] 2025.02.09 - MySQL : CONCAT_WS로 문자열 손쉽게 결합하기 (0) | 2025.02.09 |
|---|---|
| [TIL, 일일 회고] 2025.02.08 - Java 입력 처리: BufferedReader vs Scanner 실제 성능 비교해보기 (0) | 2025.02.08 |
| [TIL, 일일 회고] 2025.02.06 - 호출 스택(Call Stack)의 동작 원리와 예제로 이해하기 (0) | 2025.02.06 |
| [TIL, 일일 회고] 2025.02.05 - 하노이 탑 알고리즘, 왜 재귀여야 할까❓ (0) | 2025.02.05 |
| [TIL, 일일 회고] 2025.02.04 - Docker : Container Inspect로 컨테이너 세부 정보 확인하기 (0) | 2025.02.04 |