Files
2nd/01_Archive/2026-05-03/DTO (Data Transfer Object).md
T

9.6 KiB

DTO (Data Transfer Object)

📌 Brief Summary

DTO(Data Transfer Object)는 애플리케이션의 계층 간이나 외부 시스템과의 통신 시 데이터를 전송하기 위해 사용되는 순수한 데이터 객체입니다 [1]. 도메인 엔티티(Entity)와 물리적, 논리적으로 분리되어 API의 입출력 데이터 형태를 정의하며, 데이터베이스 스키마나 내부 로직이 외부에 노출되는 것을 방지합니다 [2, 3]. 주로 애플리케이션 계층이나 전용 패키지에 위치하며, 시스템의 외부 상호작용과 내부 비즈니스 로직을 격리하는 안전장치 역할을 수행합니다 [3-5].

📖 Core Content

  • 역할과 책임의 분리 DTO는 상호작용(Interaction) 및 인프라스트럭처(Infrastructure) 계층과 애플리케이션(Application) 계층 사이에서 데이터를 전달하는 계약(Contract) 역할을 합니다 [6, 7]. 비즈니스 로직을 포함하지 않으며, 외부로부터 데이터를 수신하거나 전송하는 영역에 엄격하게 국한되어 사용되어야 합니다 [1, 5, 8].

  • 엔티티(Entity)와의 분리 전략 DTO와 데이터베이스 모델을 표현하는 엔티티는 초기 단계에는 구조가 비슷해 보일 수 있으나, 시스템이 진화함에 따라 서로 다른 이유로 변경되므로(Diverge) 반드시 분리하여 관리해야 합니다 [2]. 컨트롤러에서 엔티티를 직접 노출할 경우 내부 필드가 유출되고, 데이터베이스 스키마에 API가 종속되며, 향후 API 변경 비용을 급격히 증가시키는 문제가 발생합니다 [4].

  • 프레임워크 및 아키텍처별 실전 패턴

    • NestJS 기반 패턴: DTO는 별도의 디렉터리(<feature>/dto/)에 배치되며, class-validator를 통해 유효성을 검증하고 컨트롤러 레이어에서 소비됩니다 [4]. 프론트엔드와 백엔드를 모두 TypeScript로 구성하는 풀스택 팀의 경우, 공유 패키지(예: libs/)를 두어 DTO 타입과 유효성 검사 로직을 모노레포(Monorepo) 환경에서 공유할 수 있습니다 [9, 10]. 또한 데코레이터를 이용해 DTO로부터 Swagger/OpenAPI 문서를 자동 생성하여 실제 코드와 문서를 동기화합니다 [11].
    • Java & Spring Boot 패턴: Lombok의 @Data 어노테이션을 활용해 getter/setter와 생성자를 손쉽게 생성하여 DTO를 구성할 수 있습니다 [12]. 대규모 아키텍처에서는 OpenAPI 스펙을 이용해 빌드 단계에서 DTO를 자동 생성하기도 하며 [13], ModelMapper 등의 라이브러리를 통해 DTO와 도메인 모델, 엔티티 간의 변환을 자동화합니다 [14].
    • 헥사고날 아키텍처(Hexagonal Architecture) 적용: DTO는 주로 애플리케이션 계층에 위치하여 외부에서 들어온 데이터를 수신합니다 [5, 15]. 이를 통해 인프라스트럭처나 기술 스택이 변경되더라도 순수한 도메인 로직이 오염되지 않도록 보호합니다 [15].

⚖️ Trade-offs & Caveats

  • 보일러플레이트 코드 증가: DTO와 도메인/엔티티를 엄격히 분리하면, 계층을 통과할 때마다 데이터를 매핑(Mapping)하고 변환해야 하는 추가적인 작업이 필수적으로 발생합니다 [3, 14].
  • 공유 모듈 관리의 복잡성: 분산 아키텍처나 마이크로서비스 환경에서 DTO와 인터페이스를 중앙 공유 라이브러리에 두고 사용할 경우, 순수한 API 계약으로만 유지해야 합니다. 여기에 특정 서비스만 필요로 하는 비즈니스 로직이 섞이기 시작하면 공유 라이브러리가 여러 서비스에 예기치 않은 문제를 일으키는 원인이 될 수 있습니다 [8].
  • 유효성 검사 책임의 분산: JSON 직렬화나 입력 형식에 대한 유효성 검사는 DTO 레벨(예: 라이브러리를 통한 검증)에서 처리되지만, 데이터가 실제 도메인에 유효한지에 대한 비즈니스 룰 검증은 도메인 클래스의 생성자에서 다루어야 하므로, 유효성 검사가 여러 계층에 나뉘어 설계되는 제약 사항이 존재합니다 [4, 16]. 다양한 요청 변형마다 개별 DTO를 생성하는 과정이 오버헤드로 느껴질 수도 있습니다 [17].

🔗 Knowledge Connections

[아키텍처 패턴 / 기반 기술]

  • Hexagonal Architecture (Ports and Adapters)
    • 연결 이유: DTO가 애플리케이션 계층에 위치하며, 도메인을 외부 인터페이스(컨트롤러 등)로부터 고립시키는 설계적 맥락을 제공합니다 [5, 15].
    • 이 개념을 통해 더 깊게 이해할 수 있는 부분: 아키텍처의 의존성 방향과 계층 간의 경계를 보호하기 위해 데이터 구조를 어떻게 격리하는지 파악할 수 있습니다.
  • Entity (엔티티)
    • 연결 이유: DTO와 구조가 비슷하지만 역할이 완전히 달라 반드시 분리되어야 하는 데이터베이스 영속성 객체입니다 [2, 3].
    • 이 개념을 통해 더 깊게 이해할 수 있는 부분: DTO가 필요한 근본적 이유(내부 필드 유출 방지 및 스키마 결합도 낮춤)를 명확히 이해할 수 있습니다.

[구현 / 활용 도구]

  • Mapper / ModelMapper
    • 연결 이유: DTO와 엔티티 간의 데이터를 변환하는 과정을 자동화하여 개발자의 보일러플레이트 작성 부담을 줄여줍니다 [3, 14].
    • 이 개념을 통해 더 깊게 이해할 수 있는 부분: 계층 간 데이터 객체를 분리할 때 발생하는 변환 비용(Trade-off)을 도구로 어떻게 해결하는지 알 수 있습니다.
  • class-validator
    • 연결 이유: NestJS 프레임워크 등에서 DTO 객체 내 입력값의 형식을 검증하는 데 필수적인 라이브러리입니다 [4].
    • 이 개념을 통해 더 깊게 이해할 수 있는 부분: 인프라/입력 계층에서의 데이터 유효성 검사가 어떻게 캡슐화되는지 이해할 수 있습니다.
  • OpenAPI / Swagger
    • 연결 이유: DTO 정의 및 데코레이터를 기반으로 API 명세서를 자동 생성하거나 역으로 DTO를 빌드해주는 기술입니다 [11, 13].
    • 이 개념을 통해 더 깊게 이해할 수 있는 부분: DTO가 클라이언트-서버 간의 명확한 '계약(Contract)'으로 활용되는 실무 생태계를 배울 수 있습니다.

Deeper Research Questions

  • 마이크로서비스 또는 모노레포(Monorepo) 환경에서 수많은 DTO를 효과적으로 버전 관리하고 프론트엔드와 안전하게 공유하는 최적의 설계 전략은 무엇인가?
  • DTO와 엔티티를 매핑하는 과정에서 발생하는 성능 오버헤드와 보일러플레이트를 최소화하기 위한 가장 발전된 도구 및 기법은 무엇인가?
  • 입력된 DTO 형식 기반 유효성 검사(class-validator 등)와 도메인 주도 설계(DDD) 관점에서 값 객체(Value Object)를 활용한 비즈니스 룰 유효성 검사는 어떻게 책임을 분리하고 협력해야 하는가?
  • OpenAPI 기반의 DTO 자동 생성(Code Generation) 방식과 개발자 수동 작성 방식의 장단점은 무엇이며, 프로젝트 규모와 상황에 따라 어떤 방식을 택해야 하는가?
  • 모든 요청의 변형(Variation)마다 별도의 DTO 클래스를 생성하는 것과 단일 모델을 재사용하는 것 사이에서 설계의 적정선(Sweet Spot)을 찾는 기준은 무엇인가?

Practical Application Contexts

  • Implementation: NestJS의 경우 <feature>/dto/ 하위에 클래스로 DTO를 정의하고 class-validator로 장식(Decorator)하여 유효성 검사를 수행하며, Java의 경우 Lombok @Data로 코드를 간소화하여 컨트롤러와 서비스의 입력 객체로 사용합니다 [4, 12].
  • System Design: 아키텍처 설계 시 시스템 외부 API와 내부 DB 계층 사이에 DTO를 완충재로 배치합니다. 이를 통해 DB 스키마가 변경되더라도 DTO를 통해 응답 포맷을 고정할 수 있어, 모바일 앱이나 프론트엔드 API 클라이언트가 파괴적 변경(Breaking Changes)을 겪지 않게 설계합니다 [2, 3].
  • Operation / Maintenance: 다중 서비스가 존재하는 환경에서는 공통 DTO를 libs/와 같은 단일 공유 모듈(Shared module)로 추출해 관리함으로써 유지보수성을 확보합니다. 이때 순수 데이터 전송 규약 외의 비즈니스 로직이 유입되지 않도록 운영해야 합니다 [8, 9].
  • Learning Path: 단순한 MVC 프레임워크의 컨트롤러 학습에서 시작하여, 이후 대규모 백엔드 구조인 헥사고날 아키텍처로 넘어가면서 DTO가 왜 계층 간 통신 규약으로서 애플리케이션 계층에 선언되어야 하는지 진화된 설계를 학습하게 됩니다 [5, 15].
  • My Project Relevance: 프레임워크 기반(NestJS, Spring Boot 등) API 개발을 진행할 때, 컨트롤러에서 엔티티를 외부로 직접 반환하던 기존의 안티 패턴을 교정하고 DTO 기반의 응답/요청 매핑 로직을 강제화하는 데 즉시 적용할 수 있습니다.

Adjacent Topics

  • Microservices Architecture
    • 확장 방향: 분산된 시스템들끼리 데이터를 주고받을 때 직렬화 가능한 DTO 패키지를 어떻게 중앙에서 배포하고 관리하는지 확장하여 조사할 수 있습니다 [8, 9].
  • Domain-Driven Design (DDD)
    • 확장 방향: 외부에서 들어온 DTO가 도메인 계층에 진입할 때 어떻게 도메인 엔티티나 값 객체(Value Object)로 안전하게 번역(Translate)되는지 아키텍처 철학적 관점을 확장할 수 있습니다 [1, 18].

Last updated: 2026-05-03