DTO, Domain, Local 모델링

2026. 3. 16. 19:59BE

TL;DR

RDB에서는 N쪽(Element)이 FK로 1쪽(Group)을 참조하는 구조가 기본이다.
클라이언트나 ORM(CoreData, SwiftData 등)에서는 편의상 양방향 관계처럼 보일 수 있지만 실제 DB 구조는 동일하다.


Local --> Domain --> DTO --> RDB

  • DTO는 벡엔드와 프론트의 계약(Spec)
  • DTO는 편의를 위해 join된 결과
  • DTO는 정규화를 염두한 구조일 필요는 없음

착각한 사실

  • RDB에서는 GroupElement 목록을 직접 알고 있다고 생각했다.
  • 또는 로컬 DB에서는 Group → Element 구조가 기본이라고 생각했다.
  • DTO 구조가 nested라서 Domain 모델도 같은 구조여야 한다고 생각했다.

예시 (착각)

Group  
 └ elements: [Element]  

Element  
 └ group을 모름  

또는

Group  
 └ element_ids  

정확한 정보

RDB 기본 관계 (1:N)

Group  
- id (PK)  

Element  
- id (PK)  
- group_id (FK)  

관계 방향

Element → Group  

  • Elementgroup_id를 가진다
  • GroupElement를 직접 모르고
  • 필요할 때 JOIN 또는 query로 가져온다

SELECT *  
FROM element  
WHERE group_id = 1  

왜 이런 구조일까

1️⃣ 정규화 (Normalization)

중복 데이터를 줄이기 위해

Group  
- element_ids [1,2,3]  

같은 구조를 사용하지 않는다.


2️⃣ 데이터 변경 비용 최소화

만약 Group이 element 목록을 가지고 있다면

  • element 추가
  • element 삭제
  • element 이동

할 때마다 Group row도 수정해야 한다.

하지만 FK 구조라면

Element.group_id 변경  

만 하면 된다.


3️⃣ 확장성

이 구조는

  • indexing
  • join
  • sharding
  • pagination

모두에서 유리하다.


ORM Local DB에서는 왜 반대로 보일까

예 (SwiftData / CoreData)

GroupEntity  
- elements: [ElementEntity]  

ElementEntity  
- group: GroupEntity  

코드에서는

Group → elements  
Element → group  

양방향 관계처럼 보인다.

하지만 실제 DB에서는

Element.group_id  

하나의 FK만 존재한다.

ORM이 개발 편의를 위해 양방향 접근자를 제공하는 것뿐이다.


활용 예시 (Client Architecture)

일반적인 앱 구조

Server  
 ├ DB Model  
 └ API DTO  

Client  
 ├ Domain Model  
 └ Local DB Model  

1️⃣ DTO (API Response)

서버는 보통 nested 구조로 내려준다.

{  
  "groups": [  
    {  
      "id": 1,  
      "elements": [  
        { "id": 1 },  
        { "id": 2 }  
      ]  
    }  
  ]  
}  

2️⃣ Domain Model (Normalized)

클라이언트에서는 보통 flatten 한다.

Group  
- id  

Element  
- id  
- groupId  

예시

elements.filter { $0.groupId == group.id }

Dictionary<groupId, [Element]>

3️⃣ Local DB

로컬 DB도 보통 동일한 구조를 사용한다.

GroupEntity  
- id  

ElementEntity  
- id  
- groupId  

4️⃣ 실제 사용 예

예: 특정 그룹의 element 가져오기

elements.filter { $0.groupId == group.id }  

또는

Dictionary<GroupID, [Element]>  

최종 정리

레이어 구조
RDB Element.group_id → Group.id
DTO nested 구조 가능
Domain normalized (groupId)
Local DB RDB와 유사

핵심

RDB는 항상 N쪽이 FK로 1쪽을 참조한다.