DAO(Data Access Object)는 데이터베이스 접근을 담당하는 객체다. 비즈니스 로직과 데이터 접근 로직을 분리하여 코드의 유지보수성과 재사용성을 높이는 것이 주요 목적이다.
DTO(Data Transfer Object)는 계층 간 데이터 전송을 위한 객체다. 주로 Controller와 Service, Service와 Repository 사이에서 데이터를 주고받을 때 사용한다.
우선 Entity는 데이터베이스 테이블과 직접 매핑되는 클래스로, 데이터베이스의 구조를 그대로 반영한다.
Entity 클래스를 직접 Controller에서 사용하면 여러 문제가 발생할 수 있다
1. 보안 문제
Entity에는 외부에 노출되면 안 되는 민감한 정보(패스워드, 내부 ID 등)가 포함될 수 있다. DTO를 사용하면 필요한 데이터만 선별해서 전송할 수 있다.
2. 성능 문제
Entity는 연관관계 매핑으로 인해 불필요한 데이터까지 함께 조회될 수 있다. DTO는 정말 필요한 데이터만 담아서 네트워크 비용을 줄일 수 있다.
3. 유지보수 문제
Entity 구조가 변경되면 API 스펙도 함께 변경되어 클라이언트에 영향을 줄 수 있다. DTO는 API 스펙을 안정적으로 유지하는 완충 역할을 한다.
DTO는 구현 방식에 따라 가변 객체 또는 불변 객체로 만들 수 있다:
가변 DTO
불변 DTO
VO(Value Object)는 값 자체를 나타내는 객체다. 가장 중요한 특징은 동일한 값을 가진 두 VO 객체는 같은 객체로 취급된다는 것이다.
VO의 핵심 개념은 "값이 같으면 같은 객체"라는 것이다. 하지만 Java의 기본 동작은 객체의 참조값(메모리 주소)으로 비교하기 때문에, 같은 값을 가져도 다른 객체로 인식한다.
예를 들어, 10,000원짜리 지폐 두 장이 있다면
이것이 바로 VO의 개념이다. equals()와 hashCode()를 오버라이딩해야 Java에서도 이 개념을 구현할 수 있다.
| 구분 | DAO | DTO | VO |
|---|---|---|---|
| 목적 | 데이터베이스 접근 | 계층 간 데이터 전송 | 값 표현 |
| 주요 역할 | CRUD 연산 수행 | 데이터 운반체 | 값 객체 |
| 가변성 | 상관없음 | 가변/불변 모두 가능 | 불변 (Immutable) |
| 비즈니스 로직 | 데이터 접근 로직만 | 일반적으로 없음 | 포함 가능 |
| equals/hashCode | 불필요 | 불필요 | 필수 오버라이딩 |
| setter 메서드 | 상관없음 | 일반적으로 있음 | 없음 |
| 생명주기 | 스프링 빈으로 관리 | 요청별로 생성/소멸 | 값이 같으면 동일 |

각각의 목적과 특성을 이해하고 적절한 상황에서 올바르게 사용한다면, 더욱 견고하고 유지보수하기 쉬운 코드를 작성할 수 있다. 특히 VO의 경우 equals/hashCode 오버라이딩을 잊지 말자!