DTO vs Domain entities
프론트엔드 개발에서 서버 응답 데이터를 DTO(Data Transfer Object) 형태로 그대로 사용하지 않고, 클라이언트의 도메인 엔티티(Domain Entity) 형태로 변환하여 사용하는 것은 애플리케이션의 유지보수성, 유연성, 그리고 관심사의 분리를 위해 매우...
❓ DTO를 도메인 엔티티로 변환하여 사용하는 이유
프론트엔드 개발에서 서버 응답 데이터를 DTO(Data Transfer Object) 형태로 그대로 사용하지 않고, 클라이언트의 도메인 엔티티(Domain Entity) 형태로 변환하여 사용하는 것은 애플리케이션의 유지보수성, 유연성, 그리고 관심사의 분리를 위해 매우 중요합니다.
이유 1: 서버와의 느슨한 결합(Loose Coupling) 유지
서버와 클라이언트는 서로 독립적인 시스템입니다. 서버의 응답 데이터 형태는 언제든 변경될 수 있으며, 이러한 변경이 클라이언트 코드 전반에 걸쳐 영향을 미치면 개발 비용과 리스크가 크게 증가합니다.
-
문제점: 만약 클라이언트가 서버의 DTO를 직접 사용한다면, 서버의 DTO 필드명, 타입, 구조 등이 변경될 때마다 해당 DTO를 사용하는 클라이언트 코드의 수많은 부분을 수정해야 합니다.
-
해결책: 클라이언트에서 서버 DTO를 클라이언트의 도메인 엔티티로 변환하는 매퍼(Mapper) 계층을 두면, 서버 응답 형태가 변경되더라도 매퍼 내부의 코드만 수정하면 됩니다. 클라이언트의 다른 비즈니스 로직은 서버 DTO 변경에 대해 알 필요가 없습니다. 이는 서버와 클라이언트 간의 불필요한 의존성을 제거하고 결합도를 낮추는 효과를 가져옵니다.
이유 2: 관심사의 분리(Separation of Concerns) 및 역할 명확화
도메인 엔티티와 DTO는 애초에 목적과 역할이 다릅니다. 이들을 구분하지 않고 사용하면 각자의 고유한 책임이 모호해지고, 이는 결국 코드의 복잡성을 증가시키고 유지보수를 어렵게 만듭니다.
-
DTO (Data Transfer Object):
-
목적: 데이터 전송(Transport)이 주된 목적입니다. 주로 네트워크를 통해 데이터를 주고받을 때 사용되며, 데이터 구조 자체에 초점을 맞춥니다.
-
역할: 서버에서 클라이언트로 데이터를 전달하거나, 클라이언트에서 서버로 데이터를 전달하는 통신 규약으로서의 역할을 합니다.
-
-
도메인 엔티티 (Domain Entity):
-
목적: 애플리케이션의 핵심 비즈니스 로직과 데이터를 캡슐화하는 것이 목적입니다.
-
역할: 클라이언트 애플리케이션 내부에서 데이터를 다루고, 특정 비즈니스 규칙이나 행위(메서드)를 포함합니다. 예를 들어,
Product도메인 엔티티는 가격 계산, 재고 관리 등의 로직을 가질 수 있습니다.
-
❗️ 만약 구분하지 않는다면? 도메인 엔티티가 API 스펙에 종속되거나, API 스펙이 도메인 로직에 의해 불필요하게 영향을 받는 상황이 발생합니다. 이는 소프트웨어 아키텍처에서 가장 중요한 “내부”에 해당하는 도메인이 “외부”에 해당하는 통신 스펙에 의해 오염되는 결과를 초래합니다. 가장 내부의 핵심이 흔들리면 그 위에 쌓인 모든 계층이 영향을 받을 수밖에 없습니다.
이유 3: 클라이언트 자체적인 데이터 모델의 필요성
모노레포 환경에서 서버와 클라이언트가 동일한 도메인 지식을 공유하더라도, 각자의 역할과 책임은 다릅니다.
-
서버: 데이터의 저장, 가공, 인증/인가 등 보이지 않는 백엔드 로직에 초점을 맞춥니다. 서버의 도메인 엔티티는 데이터베이스 스키마나 내부 비즈니스 로직에 최적화된 형태를 가집니다.
-
클라이언트: 사용자에게 데이터를 어떻게 보여줄 것인가에 초점을 맞춥니다. 클라이언트의 도메인 엔티티는 UI 상태 관리, 사용자 경험, 프레젠테이션 로직에 더 적합한 형태를 가집니다. 예를 들어, 서버에서는
priceInCents로 표현된 가격이 클라이언트에서는formattedPrice문자열로 필요할 수 있습니다.
서로 같은 도메인 엔티티를 공유한다면, 클라이언트의 UI 변경이나 서버의 데이터베이스 변경이 불필요하게 상대방의 코드에 영향을 미칠 수 있습니다. 이는 “굳이 필요 없는 의존성”을 만들게 되어 독립적인 개발과 배포를 방해합니다.
멘탈 모델 예시: 주문 처리 시스템
이 개념을 더 쉽게 이해하기 위해, 여러분이 레스토랑 주문을 처리하는 시스템을 만든다고 상상해 보세요.
음식점
-
DTO (주문서 봉투): 주방에 배달되는 ‘주문서’ 라고 생각할 수 있습니다.
- 웨이터(클라이언트)가 고객에게 주문을 받고, 그 정보를 담아 주방(서버)에 전달하는 종이입니다.
- 이 종이에는 고객이 주문한 메뉴명, 수량, 특별 요청 사항(예: “치즈 빼주세요”) 등이 적혀있습니다.
- 이 종이의 목적은 오로지 정보를 주방으로 ‘전달’ 하는 것입니다. 종이 자체는 요리하는 방법이나 재료 보관법에 대한 정보를 담고 있지 않습니다. 주방 직원은 이 종이를 보고 요리를 시작합니다.
- 만약 레스토랑 시스템이 변경되어 주문서의 형식이 바뀐다고 해도 (예: 특별 요청 사항을 적는 란이 위에서 아래로 바뀜), 주방 직원은 바뀐 형식에 맞춰 읽기만 하면 됩니다. 요리하는 방식 자체가 바뀌는 것은 아닙니다.
-
도메인 엔티티 (주방의 레시피 카드): 주방 내부에는 ‘레시피 카드’ 들이 있습니다.
- 각 메뉴마다 레시피 카드가 있어서, 어떤 재료를 얼마나 넣고, 어떤 조리법으로 요리해야 하는지 상세하게 적혀있습니다.
- 이 레시피 카드는 주방의 핵심 비즈니스 로직을 담고 있습니다. ‘치킨 커리’ 레시피 카드는 치킨 커리를 만드는 데 필요한 모든 지식(재료, 순서, 조리 시간 등)을 가지고 있습니다.
- 주방 직원은 ‘주문서(DTO)‘를 받으면, 그 안에 적힌 메뉴(예: 치킨 커리)를 보고 자신들의 ‘레시피 카드(도메인 엔티티)‘를 찾아 요리를 시작합니다. 주문서 봉투의 내용이 아무리 바뀌어도, 주방의 레시피 카드 내용이 직접적으로 바뀌는 경우는 드뭅니다.
❗️ 분리의 중요성 이 비유에서 볼 수 있듯이, ‘주문서’는 정보를 전달하는 DTO 역할을 합니다. 반면, ‘레시피 카드’는 각자의 역할에 맞게 실제 비즈니스 로직과 관련된 도메인 엔티티 역할을 합니다.
만약, 웨이터가 주방의 레시피 카드를 직접 보고 주문을 받는다면, 웨이터는 주문을 받을 때 레시피의 내용을 설명하여 주문을 받아야 하고, 주방에서 레시피를 조금만 변경해도 웨이터가 고객에게 설명하는 방식까지 매번 바꿔야 하는 불필요한 상황이 발생할 것입니다.
이처럼 도메인 엔티티와 DTO를 분리하면, 각자의 관심사가 명확해지고, 한 쪽의 변경이 다른 쪽에 불필요한 영향을 미치는 것을 방지하여 더 유연하고 유지보수하기 쉬운 시스템을 만들 수 있습니다.