본문 바로가기
android

DTO, Entity, Model(Domain, UI)의 차이

by liz_devel 2025. 12. 8.

 

우선 DTO, Entity, Model(Domain, UI) 다 데이터를 표현하는 구조적 클래스 아니야?

-> 맞다!

 

그런데 데이터를 담는다고 다 똑같은 게 아니다. 다르다. 다르기 때문에 어떤 데이터 구조에 어떤 이름을 쓰는지 명확히 알아야 한다.

일단 이렇게 구분해서 쓰는 이유는 책임을 분리하기 위해서다.(SRP 원칙)

변경 발생 영향을 받는 구조
서버 Response/Request 변경 DTO
DB 스키마 변경 Entity
비즈니스 규칙 변경 Domain Model
UI 요구 변경 UI Model

하나 바뀌었을 때 다른 레이어에 영향이 없어진다.

때문에, 클린아키텍처 공식 가이드에서 권장하고 있다.

 

 

두번째 이유로는 의존성 방향 규칙을 지키기 위해서다

Domain → Data는 의존 가능
Data → Domain은 의존 불가 (역참조 금지)

DTO (Remote/Data Layer)
 ↓
Entity (Local/Data Layer)
 ↓
Domain Model (Domain Layer)
 ↓
UI Model (Presentation Layer)

상위 계층이 하위 계층에 의존하지 않도록 함 → 유지보수성 상승.

 

이렇게 나눠 두면 테스트 또한 쉬워진다.

 

DTO(Data Transfer Object)

 

DTO의 영어를 해석하면 데이터 전송 객체라는 뜻이다.

 

  • Data = 데이터
  • Transfer = 전송하다, 옮기다
  • Object = 객체

 

시스템 간 데이터 전달 용도로 만들어진 객체다.

 

  • 서버 ↔ 앱 사이에서 Request/Response를 담는다.
  • Retrofit, Ktor 등 네트워크 계층에서 사용.

 

예시

data class UserDto(
    val id: String,
    val name: String,
    val age: Int
)

 

 

필드는 API 응답 필드와 거의 동일하다.(가공 금지)

DTO 필드는 API 응답과 동일해야 하는데 왜 거의 동일이라고 했냐면 원칙적으로는 DTO는 서버 응답과 1:1 매핑되는 것이 맞지만 실무에서 다음과 같은 예외가 존재할 수 있기 때문이다.

1) nullable → non-null 변환

서버는 null 보낼 수 있지만
앱은 null을 원하지 않아 default 값을 넣을 수 있음.

2) nested JSON 구조 단순화 (Flattening)

이건 실무에서 흔함.

예:

{
  "user": {
    "id": "123",
    "profile": {
      "name": "Tom",
      "age": 20
    }
  }
}

 

 

 

원래 DTO:

data class UserDto(
    val id: String,
    val profile: ProfileDto
)

 

하지만 너무 깊으면 → 아래처럼 flatten:

data class UserDto(
    val id: String,
    val name: String,
    val age: Int
)

 

3) snake_case → camelCase 변환

서버는 snake_case
앱은 보통 camelCase

@Json(name = "user_name")
val userName: String

동일한 데이터지만 “필드 이름”은 달라짐.

 

 

 

 

 

Entity

 

  • 로컬 DB(Table)와 연결된 데이터 구조
  • Room, SQLite 스키마에 직접 매핑됨
  • DB가 바뀌면 Entity도 반드시 바뀜

 

 

 

예시

@Entity(tableName = "users")
data class UserEntity(
    @PrimaryKey val id: String,
    val name: String,
    val age: Int
)

 

 

 

 

Model (Domain Model)

 

  • 앱의 비즈니스 로직을 표현하는 핵심 데이터 구조
  • 앱이 “의미 있게” 사용하는 형태로 가공
  • DTO/Entity와 구조가 달라도 됨
  • Domain Layer 내부에서만 사용되는 순수 구조체
  • Clean Architecture 공식에서 Domain Model을 가장 중요한 레이어로 봄
  • ViewModel/UseCase 등 비즈니스 계층에서 사용

예시

data class User(
    val id: String,
    val displayName: String,
    val isAdult: Boolean
)

 

 

 

UI Model (ViewState)

  • 화면에 필요한 데이터만 모아둔 구조
  • Domain Model과 다를 수 있음
  • UI 상태를 표현하기 위해 사용
data class UserUiModel(
    val name: String,
    val ageText: String,
    val showAdultBadge: Boolean
)

 

👉 결론적으로 Entity·DTO는 있는 그대로, Model은 앱이 실제로 쓰기 좋게 가공된 형태이다.

 

 

개념 목적 특징

개념 목적 특징
DTO 서버 ↔ 앱 데이터 전달 API 응답/요청 그대로, 가공 없음
Entity DB 저장 Room 등 DB 스키마 기반
Model (Domain) 앱 내부 핵심 로직 앱에서 쓰기 좋은 형태, 가공·로직 가능
Model(UI) UI가 바로 사용할 수 있는 형태로 데이터 표현 화면 전용 구조, 가공된 값 가능, View의 상태를 안정적으로 관리

 

 

📌 실제 흐름 예시

서버 응답(JSON) → DTO → Model → UI 표시
로컬 저장(DB) ←→ Entity ←→ Model

fun UserDto.toModel() = User(
    id = id,
    displayName = name,
    isAdult = age >= 20
)

fun UserEntity.toModel() = User(
    id = id,
    displayName = name,
    isAdult = age >= 20
)

 

 

 

  • DTO → Model 변환 = Domain Layer
  • Entity → Model 변환 = Domain Layer

model로 변환하기 위해서는 Mapper는 Domain Layer에 둬야 한다.

 

data
 ├── dto
 ├── entity
 └── repositoryImpl

domain
 ├── model      ← 순수 데이터 구조
 ├── mapper     ← 변환 책임
 └── usecase    ← 비즈니스 규칙

presentation
 └── viewmodel

 

 

 

네이밍 컨벤션

DTO

  • 반드시 Dto suffix 사용
    UserDto, LoginResponseDto, OrderRequestDto

Entity

  • Entity suffix
    UserEntity, OrderEntity

Domain Model

  • 아무 suffix 안 붙임
    User, Order, Store

UI Model(ViewState)

  • UserUiModel or UserViewState
  • UiModel, UiState, ViewState
    UserUiModel, OrderViewState

이 네이밍 방식은 Android·Spring·Clean Architecture에서 공통적으로 채택되는 방식이다.

 

반응형

'android' 카테고리의 다른 글

Module 생성하기  (0) 2026.01.11
[프로그래머스] A로 B 만들기  (0) 2025.12.22
[Compose] Recomposition  (1) 2025.07.13
딥링크란?  (5) 2025.06.02
CoroutineDispatcher  (0) 2025.03.23