1. DAO (Data Access Object)
•
역할: 데이터베이스(DB)와 직접 소통하는 객체.
•
책임: SQL 실행, CRUD 수행, Entity 객체 반환.
•
왜 필요한가?
◦
데이터 접근 로직을 분리해서 비즈니스 로직과 섞이지 않게 하기 위함.
◦
나중에 DBMS가 변경되어도, DAO만 수정하면 서비스 로직은 그대로 사용 가능.
@Repository
public class UserDao {
@Autowired
private JdbcTemplate jdbcTemplate;
public User findById(Long id) {
String sql = "SELECT * FROM users WHERE id = ?";
return jdbcTemplate.queryForObject(sql, new Object[]{id}, new BeanPropertyRowMapper<>(User.class));
}
}
Java
복사
•
여기서 UserDao는 DB 접근을 담당하는 DAO.
2. DTO (Data Transfer Object)
•
역할: 계층 간 데이터 전송을 위한 객체.
•
책임: 데이터를 담아서 컨트롤러
서비스
외부 시스템 간에 이동할 때 사용.
•
왜 필요한가?
◦
엔티티를 외부로 노출하는 것은 보안, 유지보수 측면에서 위험하다.
◦
불필요한 데이터 노출 방지 (민감한 필드 제외)
◦
API 스펙에 맞는 형태로 변환 가능 (예: JSON 구조 맞추기)
public class UserResponseDto {
private Long id;
private String username;
private String email;
// 생성자, Getter
}
Java
복사
•
엔티티 User 는 비밀번호, 가입일자 등 여러 필드가 있지만, 클라이언트에게는 id, username, email만 전송해줌.
•
DTO 패턴의 핵심: “전송에 적합한 형태로 데이터를 포장하는 것”.
3. VO (Value Object)
•
역할: 값 자체를 표현하는 객체. 불변(immutable) 성격.
•
책임: 값의 동등성 “값 자체”로 판단하고, 객체의 참조가 아닌 값 비교가 핵심.
•
왜 필요한가?
◦
불변 객체이므로 스레드 세이프(thread-safe).
◦
비즈니스 규칙에서 값으로 취급하는 데이터를 표현하기에 적합.
◦
예시: 주소, 전화번호, 금액(Money) 같은 것들.
public class Address {
private final String city;
private final String street;
public Address(String city, String street) {
this.city = city;
this.street = street;
}
// Getter, equals, hashCod
}
Java
복사
•
Address는 VO로 취급. new Address("서울", "강남구") 두 객체가 값이 같으면 같은 것으로 봄.
4. 왜 DTO로 데이터를 전송해야 하나?
1. 보안
•
엔티티에는 민감한 데이터(비밀번호, 권한, 내부 ID)가 포함되어 있음.
•
DTO는 필요한 데이터만 담아서 불필요한 노출 방지 가능.
2. 유지보수
•
API 스펙이 바뀌어도 DTO만 수정하면 되고, 엔티티나 DB 구조는 그대로 유지 가능.
3. 계층 분리
•
Controller, Service, Repository 계층이 깔끔하게 분리됨.
•
엔티티가 직접 외부로 노출되지 않으므로, 엔티티와 API의 독립성 확보.
4. 성능 최적화
•
엔티티를 직접 반환할 경우 불필요한 필드까지 직렬화됨 → 네트워크 비용 증가.
•
DTO는 필요한 필드만 골라서 보내므로 전송 데이터가 가볍다.
5. 요약
구분 | 역할 | 특징 | 예시 |
DAO | DB 접근 객체 | SQL 실행, CRUD 담당 | UserDao, ProductDao |
DTO | 계층 간 데이터 전송 객체 | API, 클라이언트 데이터 포장 | UserResponseDto, LoginRequestDto |
VO | 값 객체 | 불변, 값 비교 | Address, Money, PhoneNumber |
6. 실무에선 어떻게?
•
Controller
Service
Repository
◦
Controller에서는 Request DTO로 데이터를 받고,
◦
Service에서는 비즈니스 로직 수행 후 Response DTO로 반환.
◦
Repository(DAO)에서는 Entity로 DB와 소통.
[Client] → [Controller: RequestDTO] → [Service: Entity] → [DAO: DB]
↓
[Service: ResponseDTO] → [Controller] → [Client]
Java
복사
•
엔티티를 절대 외부 API로 직접 반환하지 말 것! (보안 문제, 유지보수 문제)
•
DTO는 요구사항에 맞게 유연하게 설계 (필드 선택 가능)
•
VO는 불변으로 설계하고, 값 비교 로직 (equals, hashCode)을 꼭 구현할 것