콘텐츠로 바로가기

Data Modeling for Connected Data

Data Modeling for Connected Data의 정의, 범위, 선행 지식, 학습 주제, 참고 근거를 정리한 CS&E 학습 노드입니다.

목차 보기22

1. Overview

연결된 데이터를 위한 데이터 모델링(Data Modeling for Connected Data)은 테이블 쪼개기(RDBMS)나 통째로 쑤셔넣기(Document DB)를 넘어, 현실 세계의 **초연결성(Hyper-connectivity)**을 손실 없이 데이터베이스 물리 스키마로 옮겨 심는 데이터베이스 설계의 종착역을 해부합니다.

학습자는 단순한 ERD를 넘어 "어떤 질문(쿼리)을 가장 많이 던질 것인가?"라는 접근법으로, 쿼리를 스키마에 선(先)반영하는 **쿼리 주도 역설계(Query-driven Reverse Engineering)**의 본질을 뜯어봅니다. 나아가 폴리글랏 지속성(Polyglot Persistence) 철학 아래, 트랜잭션 덩어리는 RDBMS에, 검색어는 Elasticsearch에, 소셜 관계망은 Graph DB에 나누어 담고 이들을 하나의 논리적 백엔드로 엮어내는 하이브리드 데이터 아키텍처를 해부합니다. 마지막으로, 분산 환경에서 이벤트 소싱(Event Sourcing)과 CQRS(Command Query Responsibility Segregation)를 통해 데이터 쓰기 모델과 읽기 모델을 물리적으로 분리하는 최상위 마이크로서비스 설계 역량을 확보합니다.

2. Scope & Boundaries

In-Scope

  • NoSQL 데이터 모델링 (NoSQL Modeling): 쿼리 주도 설계(Query-Driven Design), 비정규화(Denormalization), 임베딩(Embedding) vs 참조(Referencing), 복합 키(Composite Key) 해킹.
  • 폴리글랏 지속성 (Polyglot Persistence): 멀티 모델 아키텍처, 데이터 성격(관계형, 시계열, 공간, 텍스트)에 따른 최적의 물리 엔진(RDBMS, TSDB, Vector DB 등) 매핑.
  • 고급 아키텍처 패턴 (Advanced Architecture): 이벤트 소싱(Event Sourcing: 상태를 이벤트의 연속으로 저장), CQRS (명령/조회 책임 분리).
  • 데이터 허브와 동기화 (Data Synchronization): CDC (Change Data Capture) 패턴, Debezium, Kafka를 활용한 이종 DB 간 릴레이.

Out-of-Scope

  • Kafka 파티션 및 브로커 깊은 튜닝: 메시지 큐 클러스터 유지보수 및 리더 선출 → 07-03-04 Message Queues & Event Streaming 영역으로 위임.
  • MSA 서비스 간 트랜잭션 (Saga Pattern): 비즈니스 트랜잭션 통제 → 07-02-04 Distributed Transactions 영역.

Boundaries

  • 정규화(Normalization) vs 쿼리 주도 모델링(Query-Driven): RDBMS는 "데이터를 어떻게 깔끔하게 보관할 것인가(정규화)"에 집착하며, 쿼리는 런타임에 Join으로 해결합니다. 반면 NoSQL 연결 데이터 모델링은 철저히 "앱 화면에서 어떤 데이터를 1번에 요청하는가(화면 주도)"에 집착합니다. 조회 성능 극대화를 위해 동일한 데이터를 여러 테이블(또는 문서)에 미친 듯이 복제(비정규화)하여 저장합니다. 즉, 쓰기 지연과 스토리지 공간을 희생해 궁극의 밀리초 조회(Read) 성능을 달성하는, RDBMS와는 정반대의 철학적 트레이드오프입니다.

3. Counterexample

  • 단일 DB 원리주의 (Silver Bullet Anti-pattern): 게시판 플랫폼을 만드는데 RDBMS(MySQL) 하나에 모든 걸 구겨 넣음. 조회수를 기록하기 위해 UPDATE posts SET views=views+1이 초당 1만 번 발생하여 트랜잭션 락 지옥 유발. 게시글 텍스트 검색을 위해 LIKE '%keyword%' 풀스캔으로 CPU 마비. RDBMS 하나로 해결하려는 고집을 버리고, 조회수 카운터는 Redis(Key-Value)로, 전문 검색은 Elasticsearch(Search Engine)로, 메인 데이터는 MySQL(RDBMS)로 찢어 담는 폴리글랏(Polyglot) 분배 없이는 현대적 스케일링이 불가합니다.
  • 이벤트 소싱(Event Sourcing) 없는 상태(State) 덮어쓰기의 참사: 장바구니에서 상품을 삭제할 때 DELETE 쿼리로 기존 데이터를 날려버림. 한 달 뒤 비즈니스 팀에서 "장바구니에 담았다가 삭제한 상품들의 장바구니 이탈률(Drop-off)을 분석해줘"라고 요청. 하지만 이미 상태를 덮어쓰고 삭제했으므로 과거 이력(History) 데이터가 지구상에서 증발했습니다. 데이터를 상태(State)가 아니라 이벤트(추가됨, 삭제됨)의 순차적 누적으로(Append-only) 저장하는 모델링 파이프라인 부재가 낳은 비즈니스 손실입니다.

4. Prerequisites

  • RDBMS 스키마 설계 (Basic): 정규화 및 ER 다이어그램. (06-01-01 Relational Modeling)
  • NoSQL 스토리지 엔진 (Basic): Document DB, Graph DB의 특성. (06-02-01 ~ 06-02-04)

5. Learning Map

Sequence Core Cluster Objective & Description Evidence (BoK)
1 Query-Driven Data Modeling 데이터 저장이 아닌 화면(UI) 조회 패턴부터 역추적하여 NoSQL의 물리적 테이블을 깎아내는 비정규화 역학을 쥡니다. P1
2 Polyglot Persistence Architecture 텍스트, 관계, 그래프 데이터를 각각 가장 잘하는 물리 엔진 3개로 찢어 붓고 논리적으로 묶어내는 마이크로서비스 DB 설계를 해부합니다. P5
3 CDC (Change Data Capture) MySQL에 데이터가 쓰이는 순간 트랜잭션 로그(Binlog)를 낚아채어 Kafka를 통해 Elasticsearch로 0.1초 만에 쏴주는 릴레이망을 뜯어봅니다. Industry
4 CQRS & Event Sourcing 쓰기(명령) 전용 DB와 읽기(조회) 전용 DB를 완전히 갈라치고, 덮어쓰기 대신 불변 이벤트 로그를 누적하는 극한의 비동기 아키텍처를 장악합니다. Industry

6. Learning Topics

Basic

Core Topic 01: 화면에서 DB를 역추적하다, 쿼리 주도 모델링 (Query-Driven Modeling)

  • Why to Learn: RDBMS 관성으로 Cassandra나 MongoDB를 설계했다가 조인(Join)이 불가능해 애플리케이션에서 수백 번 API 루프를 도는 치명적인 N+1 쿼리 참사를 막기 위함입니다.
  • What to Learn:
    • Concepts: Application Query Pattern, 비정규화 뷰(Materialized View), 임베딩 객체 배열, 역방향 모델링(Reverse Modeling), 데이터 중복(Data Duplication) 허용.
    • Skills: 화면 목업(UI Mockup) 기반의 읽기 쿼리 식별, NoSQL 컬렉션 매핑.
  • How to Learn:
    • 1단계: RDBMS ERD의 폐기: '아마존 상품 상세 페이지'. RDBMS라면 상품, 카테고리, 리뷰, 판매자 테이블을 조인합니다. NoSQL 쿼리 주도 방식은 다릅니다. "상세 페이지 그릴 때 1번의 쿼리로 다 가져오고 싶다"는 목표를 세우고, 아예 저 4개 데이터를 묶은 거대한 JSON 덩어리(Document)나 와이드 로우(Wide Row)를 설계하는 역발상을 해부합니다.
    • 2단계: 중복 관리의 짐: 리뷰 작성자 이름을 Document에 임베딩(중복)했습니다. 유저가 이름을 개명하면, 그 유저가 쓴 모든 상품의 수만 개 리뷰 Document를 찾아가 이름을 모조리 덮어써 주어야 하는 애플리케이션 레벨의 짐(Write Penalty)을 물리적으로 뜯어봅니다.
  • Implement: 쇼핑몰 리뷰 시스템 NoSQL 매핑 스크립트. 1) 유저 정보 분리된 RDBMS형 딕셔너리 구조(조회 시 2번 루프). 2) 리뷰 안에 유저 이름과 프사가 전부 박혀있는 쿼리 주도형 구조(조회 시 1번 딕셔너리 로드 끝). 단일 fetch_review() 함수 실행 속도를 재어 데이터 구조가 I/O에 미치는 영향 렌더링.

Core Topic 02: 적재적소의 DB 무기고, 폴리글랏 지속성 (Polyglot Persistence)

  • Why to Learn: 결제는 RDBMS의 ACID가 필요하고, 상품 검색은 역인덱스 속도가 필요하며, 장바구니 세션은 Key-Value 메모리가 필요한 현대 서비스에서 '단 하나의 완벽한 DB'라는 환상을 부수기 위함입니다.
  • What to Learn:
    • Concepts: 폴리글랏 프로그래밍, 다중 언어 지속성(Polyglot Persistence), 워크로드 특화 데이터베이스(Workload-optimized DB), 마이크로서비스 간 데이터 분리(Database per Service).
    • Skills: 비즈니스 도메인별 최적 DB 엔진 선택 아키텍처 스케치.
  • How to Learn:
    • 1단계: 강점의 조합: 유저 프로필과 결제는 MySQL(무결성), 넷플릭스 영화 추천망은 Neo4j(연결성 탐색), 영화 대사 전문 검색은 Elasticsearch(검색엔진), 사용자 로그인 세션은 Redis(휘발성 고속 버퍼). 각 서비스 블록이 본인 도메인에 가장 잘 맞는 물리 스토리지 위에서 뛰노는 아키텍처를 해부합니다.
    • 2단계: 파편화의 대가: 4개의 분리된 DB. 사용자가 회원 탈퇴를 누르면 MySQL, Neo4j, Elasticsearch, Redis 4곳에서 유저 데이터를 말끔히 지워야 합니다. 분산 트랜잭션(Saga)이 얽히고, 하나라도 실패 시 데이터가 꼬이는 시스템 복잡도 폭발을 뜯어봅니다.
  • Implement: Python 딕셔너리 기반 다중 DB 라우터 시뮬레이터. MySQL_Mock, Redis_Mock, Elastic_Mock 3개 클래스 생성. Router 객체가 route(query_type, data)를 받아 payment면 MySQL에, session이면 Redis에 삽입. 전체 서비스 통신 맵이 비즈니스 맥락에 따라 물리 스토리지를 찢어버리는 라우팅 로직 데모.

Practical

Core Topic 03: 숨소리까지 가로채는 파이프라인, CDC 패턴 (Change Data Capture)

  • Why to Learn: 폴리글랏 구조에서 "MySQL에 상품이 등록되면, 그 즉시 Elasticsearch에 검색되게 해라"는 동기화 요구사항을 쌍방향 API 호출(스파게티 코드) 없이, DB 엔진의 뒷구멍(Log)으로 우아하게 해결하기 위함입니다.
  • What to Learn:
    • Concepts: Dual Write 안티 패턴, CDC (Change Data Capture), 트랜잭션 로그 스니핑(WAL / Binlog Tailing), Debezium, 데이터 동기화 지연(Replication Lag).
    • Skills: Kafka를 허브로 사용하는 아키텍처 파이프라인 이해.
  • How to Learn:
    • 1단계: Dual Write의 붕괴: 백엔드 코드에서 mysql.save() 호출 후 elastic.save() 호출. 중간에 서버가 다운되면 MySQL에는 쓰였는데 Elastic에는 안 쓰이는 정합성 파괴(Dual Write 한계).
    • 2단계: 로그 추적(Log Tailing): 애플리케이션은 오직 MySQL에만 save() 합니다. 뒤편에서 Debezium 같은 CDC 툴이 MySQL의 기계적 Binlog(쓰기 로그) 파일 변경을 몰래 감시하다가, 변경분이 생기면 낚아채서 Kafka 이벤트 버스로 던져줍니다. 다른 DB들은 Kafka 구독(Subscribe)만으로 데이터를 최종 동기화(Eventual Consistency)하는 우아한 인프라 분리 기법을 뜯어봅니다.
  • Implement: 파이썬 Binlog 감시 봇 모사 스크립트. 주 스레드가 1초마다 파일 db_log.txt에 "INSERT id=5" 라인 추가(DB 쓰기). 백그라운드 스레드(CDC 봇)가 tail -f 처럼 파일의 끝을 감시하다 새로운 줄이 생기면 이를 파싱하여 search_engine_array 메모리 리스트에 주입해주는 릴레이 파이프라인 데모.

Advanced

Core Topic 04: 상태를 찢어버린 극한의 비동기, CQRS와 이벤트 소싱 (CQRS & Event Sourcing)

  • Why to Learn: 조회가 쓰기를 방해하고 쓰기가 조회를 블로킹하는 단일 모델의 한계를 박살 내어, 대규모 엔터프라이즈(은행, 거대 이커머스)에서 성능과 불변 이력 보존을 동시에 달성하는 최상위 아키텍처 패턴을 장악하기 위해서입니다.
  • What to Learn:
    • Concepts: CQRS (Command Query Responsibility Segregation), 이벤트 소싱(Event Sourcing), 불변(Immutable) 이벤트, 상태 머신(State Machine), 투영(Projection) 뷰, 최종적 일관성(Eventual Consistency).
    • Skills: 덮어쓰기 로직을 '이벤트 스트림 Append' 로직으로 발상 전환.
  • How to Learn:
    • 1단계: 이벤트 소싱(시간의 지배): 장바구니 테이블에 사과오렌지UPDATE 하지 않습니다. [이벤트1: 사과 담음], [이벤트2: 사과 뺌], [이벤트3: 오렌지 담음] 이라는 불변 이벤트를 순차적으로 Append만 합니다. 장애 발생 시 처음부터 이벤트를 다시 재생(Replay)하면 장바구니 상태가 완벽 복구되는 타임머신 역학을 해부합니다.
    • 2단계: CQRS (명령과 조회의 분리): 수백만 건의 이벤트를 순회 계산해 현재 잔액을 보여주는 건 화면 렌더링에 너무 느림. 쓰기 서비스(Command)는 이벤트 큐에 이벤트를 쏘고 끝냅니다. 이벤트를 비동기로 빨아들인 프로젝션(Projection) 스레드가 덧셈 뺄셈을 완료하여 '현재 잔액 1,500원'을 캐시 DB(Redis/조회 DB)에 박아 넣습니다. 읽기 서비스(Query)는 그냥 캐시를 0.01초 만에 읽고 끝나는 아키텍처의 분리를 뜯어봅니다.
  • Implement: 파이썬 장난감 CQRS/Event Sourcing 모델. 1) Command 객체: bank_events_log 리스트에 {'type': 'DEPOSIT', 'amount': 100} 등 추가(Append Only). 2) Projection 스레드: 큐에 들어온 이벤트를 소화하여 별도 딕셔너리 read_model_db['balance'] = 100 계산 갱신. 3) Query 객체: read_model_db['balance'] 단순 출력. 읽기/쓰기가 다른 변수(DB)를 바라보는 상태 격리 데모.

7. Terminology

Term (EN / ko, abbr) 1문장 정의 단계(기본/권장/실무/심화) 역할/맥락 관련 개념 유사/대비/함께 사용 오해 포인트 Evidence(Primary/Secondary/Industry) Flags(core)
Query-Driven Modeling 데이터를 어떻게 쪼개어 저장할지(정규화)보다 애플리케이션의 화면(UI)과 조회 패턴에 맞춰 데이터를 뭉쳐 저장하는 NoSQL 설계 철학입니다. 기본 스키마 뼈대 설계 Denormalization Data-Driven Modeling RDBMS의 정규화 법칙을 그대로 NoSQL에 쓰면 최악의 N+1 쿼리가 터짐 P5:SFIA core
Polyglot Persistence 하나의 만능 데이터베이스에 모든 걸 구겨 넣지 않고, 비즈니스 특성에 맞게 RDBMS, 캐시, 검색 엔진 등 다중 엔진을 혼용하는 아키텍처입니다. 권장 시스템 설계 Multimodal DB Database-per-Service 관리해야 할 DB 인프라가 기하급수적으로 늘어나는 운영 비용이 따름 P1:CS2023 core
CDC (Change Data Capture) 데이터베이스 쓰기 로그(WAL/Binlog)를 백그라운드에서 추적하여, 데이터 변경 발생 시 이를 비동기 이벤트 큐로 밀어주는 파이프라인 패턴입니다. 심화 데이터 동기화 Debezium / Binlog Dual Write 애플리케이션 코드(Dual Write)로 동기화를 시도하면 트랜잭션 꼬임이 발생함 Industry core
Event Sourcing & CQRS 현재 상태를 덮어쓰지 않고 변경 이력(Event) 자체를 누적(Append-only)하며, 복잡도를 줄이기 위해 쓰기 모델과 읽기 모델을 물리적으로 찢어버린 패턴입니다. 실무 동시성 및 복원력 Event Log / Projection CRUD 코드가 극단적으로 복잡해지며 최종적 일관성(Eventual Consistency)에 대한 내성이 필요함 Industry core

8. References

Primary

  • [P1] CS2023 - Software Engineering (SE) - Software Architecture (Microservices)
  • [P5] SFIA - Enterprise IT Architecture (ARCH) - Integration Architecture

Secondary

  • [Designing Data-Intensive Applications] Martin Kleppmann - Derived Data (CDC & Event Sourcing)
  • [Microservices Patterns] Chris Richardson - CQRS and Event Sourcing

Industry

  • [Debezium Documentation] - Change Data Capture Architecture
  • [Martin Fowler's Patterns] - Event Sourcing & CQRS Bliki

9. Final Checklist

Primary

  • NoSQL 문서 설계 시, 1 관계를 별도 테이블로 분리할 때(참조)와 문서 내 배열로 포함시킬 때(임베딩)의 트레이드오프를 논증할 수 있는가?
  • 사용자 세션, 금융 트랜잭션 원장, 전문 검색 영역에 대해 각각 Redis, RDBMS, Elasticsearch를 매핑하는 폴리글랏 논리를 설명할 수 있는가?

Secondary

  • 애플리케이션 코드 내에서 2개의 이종 DB에 동시 Save() 호출(Dual Write) 시 발생하는 정합성 붕괴와, CDC 패턴이 이를 해결하는 릴레이 구조를 해부할 수 있는가?
  • 이벤트 소싱(Event Sourcing) 아키텍처에서 과거 특정 시점으로 상태(State)를 재생(Replay/Time Travel)하는 메커니즘의 이점을 분석할 수 있는가?

Industry

  • CQRS 적용 시, 쓰기(Command) 로직이 발생시킨 이벤트를 프로젝션(Projection)이 연산하여 읽기(Query) 전용 캐시 DB에 꽂아주는 물리적 격리를 설계할 수 있는가?
  • 분산된 폴리글랏 아키텍처 환경에서 시스템 간 데이터가 불일치하는 찰나의 시간, 즉 최종적 일관성(Eventual Consistency)을 비즈니스적으로 어떻게 통제할지 시나리오를 구성할 수 있는가?

Data & Databases · NoSQL & Specialized Stores

7 / 7