| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | |||
| 5 | 6 | 7 | 8 | 9 | 10 | 11 |
| 12 | 13 | 14 | 15 | 16 | 17 | 18 |
| 19 | 20 | 21 | 22 | 23 | 24 | 25 |
| 26 | 27 | 28 | 29 | 30 | 31 |
- 인접 행렬
- 개발자취업
- 플로이드-워셜
- 갭체크
- 자바
- 코딩테스트준비
- 코테공부
- 항해99
- 코딩테스트사이트추천
- 코딩테스트
- 파이썬
- 연결 리스트
- 자바스크립트
- 개발자루틴
- 코드트리
- 데이터베이스
- til
- 자료구조
- 1일1코테
- 오블완
- 웹
- #코드트리 #코딩테스트 #코테공부 #백트래킹 #알고리즘 기초
- 알고리즘
- C
- 프로그래머스
- 프로그래밍 언어
- 99클럽
- 티스토리챌린지
- Java
- 운영체제
- Today
- Total
와드의 블로그
DTO 사용 전략 본문
DTO(Data Transfer Object)는 계층 간(Layer-to-Layer) 데이터 교환을 위해 사용하는 객체입니다. 비즈니스 로직을 포함하지 않고 순수하게 데이터만 담고 있어야 하며, 도메인 객체(Entity)를 보호하고 클라이언트 요구사항에 맞는 데이터만 전달하는 핵심적인 역할을 수행합니다.
안전하고 유지보수하기 쉬운 DTO를 설계하기 위한 주요 전략들을 정리했습니다.
1. 불변 객체와 record 사용
DTO는 데이터를 전달하는 '운반체' 역할을 하므로, 전달 과정에서 데이터가 변경되지 않음을 보장하는 불변(Immutable) 객체로 설계하는 것이 가장 이상적입니다. Java 14부터 도입된 record는 이러한 불변 DTO를 만드는데 최적화된 문법입니다.
사용하는 이유 및 장점
- 데이터 무결성 보장: 생성 시점에 값이 할당된 후에는 상태가 변하지 않으므로, 멀티스레드 환경이나 복잡한 비즈니스 로직 흐름 속에서도 데이터의 안전성이 보장됩니다.
- 보일러플레이트 코드 제거: record를 사용하면 생성자, getter, equals, hashCode, toString 메서드가 컴파일러에 의해 자동으로 생성되어 코드가 매우 간결해집니다.
- 명확한 의도: 클래스 선언부만 보아도 이 객체가 데이터를 담기 위한 용도임을 직관적으로 파악할 수 있습니다.
단점 및 한계
- 상속 불가능: record는 내부적으로 final 클래스이며 java.lang.Record를 상속받기 때문에 다른 클래스를 상속(extends)할 수 없습니다.
- 유연성 부족: 필드 수가 많아지거나 일부 필드만 선택적으로 초기화해야 하는 경우, 생성자 오버로딩이 번거로워질 수 있습니다.
한계 극복 방법
- 인터페이스 활용: 클래스 상속은 불가능하지만 인터페이스 구현(implements)은 가능합니다. 공통된 속성이나 행위 규약이 필요하다면 인터페이스를 추출하여 여러 record DTO가 이를 구현하도록 설계합니다.
- 컴팩트 생성자 활용: 데이터 바인딩 시점에 유효성 검증이 필요하다면 record가 제공하는 컴팩트 생성자(Compact Constructor)를 활용하여 간결하게 검증 로직을 추가할 수 있습니다.
public record UserResponse(String name, int age) {
public UserResponse {
if (age < 0) {
throw new IllegalArgumentException("나이는 0 이상이어야 합니다.");
}
}
}
2. 정적 팩토리 메서드 사용
DTO는 주로 도메인 객체(Entity)를 변환하여 생성됩니다. 이때 변환 로직을 어디에 둘 것인지가 중요한데, DTO 내부에 정적 팩토리 메서드(from, of 등)를 정의하여 도메인 객체를 인자로 받아 DTO로 변환하는 패턴을 가장 널리 사용합니다.
사용하는 이유 및 장점
- 캡슐화와 응집도 향상: Entity를 DTO로 변환하는 로직이 DTO 내부에 위치하므로, 서비스 계층(Service Layer)의 코드가 간결해집니다.
- 의존성 방향 제어: 도메인(Entity)은 DTO를 몰라도 되며, DTO만 도메인을 아는 단방향 의존성이 형성되어 도메인 객체의 순수성을 유지할 수 있습니다.
- 명확한 네이밍: UserResponse.from(user)와 같이 메서드 이름을 통해 변환 의도를 명확하게 표현할 수 있습니다.
단점 및 한계
- DTO와 Entity 간의 강한 결합: DTO가 특정 Entity의 구조에 종속됩니다. Entity가 변경되면 DTO의 팩토리 메서드도 함께 수정되어야 합니다.
- 복잡한 매핑의 한계: 여러 Entity를 조합해서 하나의 DTO를 만들어야 하거나, 변환 로직 자체가 매우 복잡한 경우 DTO 클래스가 비대해질 수 있습니다.
한계 극복 방법
- 별도의 Mapper 클래스 도입: 변환 로직이 단순하다면 DTO 내의 정적 팩토리 메서드를 유지합니다. 하지만 매핑이 복잡해지거나 외부 API 응답과 여러 도메인을 섞어야 한다면, MapStruct 같은 매핑 라이브러리를 사용하거나 별도의 Assembler/Mapper 클래스를 분리하여 책임을 위임합니다.
3. DTO와 빌더 패턴(Builder Pattern) 사용에 대한 고찰
실무에서는 DTO 내부 구현에 빌더 패턴을 적용하는 경우도 흔히 볼 수 있습니다. 이는 DTO의 성격(응답용인지 요청용인지)에 따라 권장 여부가 확연히 갈립니다.
3.1 Response DTO (권장함)
서버에서 클라이언트로 응답을 내려주기 위해 객체를 조립하는 과정에서는 빌더 패턴이 매우 유용합니다.
- 매핑 실수 방지: Entity에서 Response DTO로 데이터를 옮겨 담을 때 필드가 많다면, 정적 팩토리 메서드(from) 내부에서 빌더를 사용하여 매핑의 가독성과 안정성을 높일 수 있습니다.
- 다중 Entity 조합: 여러 Entity의 데이터를 모아서 하나의 응답 DTO를 구성해야 할 때 유연하게 대응할 수 있습니다.
- 테스트 용이성: 컨트롤러 테스트 등에서 Mock 응답 데이터를 생성할 때 빌더를 사용하면 매우 편리합니다.
3.2 Request DTO (권장하지 않음)
클라이언트의 요청 데이터를 서버에서 바인딩받기 위한 용도의 DTO에는 빌더 패턴이 거의 필요하지 않으며, 자칫 오버엔지니어링이 될 수 있습니다.
- 프레임워크의 역할: 클라이언트가 보낸 JSON 등의 요청을 자바 객체로 매핑하는 역할은 주로 Spring의 Jackson 라이브러리가 담당합니다. Jackson은 기본 생성자와 리플렉션을 사용하거나, record의 주 생성자를 분석하여 값을 매핑하므로 빌더가 전혀 필요하지 않습니다.
- 의도 혼동: Request DTO에 빌더를 열어두면, '이 객체가 애플리케이션의 비즈니스 로직 어딘가에서 명시적으로 생성되고 조작될 수 있다'는 잘못된 신호를 다른 개발자에게 줄 수 있습니다. Request DTO는 오직 외부의 요청 데이터를 담아오는 순수한 바인딩 객체로 남겨두는 것이 좋습니다. (단, 테스트 환경에서 Request DTO의 인스턴스를 직접 생성해야 할 때는 필요에 따라 생성자나 헬퍼 메서드를 활용하는 것으로 충분합니다.)
4. 종합 가이드
실무에서는 위 전략들을 DTO의 성격(Request vs Response)에 따라 적절히 적용하여 사용합니다.
Response DTO (서버 -> 클라이언트)
- 기본 전략: record를 사용하여 불변 객체로 정의합니다.
- 생성 전략: 내부에 from(Entity) 형태의 정적 팩토리 메서드를 두어 서비스 로직을 단순화합니다. 필드가 많거나 여러 도메인 객체를 조합해야 한다면, 정적 팩토리 메서드 내부 구현에 빌더 패턴을 활용하여 매핑의 안전성을 높입니다.
Request DTO (클라이언트 -> 서버)
- 기본 전략: record 혹은 불변 클래스(프레임워크의 바인딩 제약에 따라 기본 생성자가 필요한 경우)로 정의합니다.
- 생성 전략: 클라이언트의 요청 데이터를 프레임워크가 자동으로 매핑해주므로 빌더 사용을 지양하고 순수한 데이터 운반체의 목적에 충실하도록 둡니다. 테스트 환경 등에서 객체 생성이 필요할 때는 목적에 맞는 정적 팩토리 메서드나 생성자를 활용합니다.
'스터디 > 개발' 카테고리의 다른 글
| 정렬 알고리즘 정리 (0) | 2026.05.18 |
|---|---|
| 도메인 객체 생성 전략 (1) | 2026.03.30 |