it 서적 독후감

[DDIA 12장] 데이터 시스템의 미래 — 올바른 시스템을 만든다는 것

데일리코딩 2026. 4. 3. 11:57

[DDIA 12장] 데이터 시스템의 미래 — 올바른 시스템을 만든다는 것

Designing Data-Intensive Applications (Martin Kleppmann) 를 읽으며 정리한 시리즈입니다. 1장 | 2장 | 3장 | 4장 | 5장 | 6장 | 7장 | 8장 | 9장 | 10장 | 11장


들어가며

DDIA의 마지막 장이다.

1장에서 신뢰성, 확장성, 유지보수성으로 시작한 여정이 여기까지 왔다. 스토리지 엔진, 분산 복제, 트랜잭션, 합의 알고리즘, 배치와 스트림 처리까지.

12장은 기술적인 심화가 아니다. 지금까지 배운 모든 것을 올려놓고 더 높은 질문을 던진다.

"기술적으로 가능한 것"과 "올바른 것" 사이의 간격. 데이터 시스템이 현실 세계에 미치는 영향. 그리고 우리가 만드는 시스템이 어떤 세상을 만들어가고 있는가.

이 장이 다른 챕터들과 가장 다른 점은 정답이 없다는 것이다. 책이 질문을 던지고, 독자가 스스로 답을 찾아야 한다.


데이터 통합의 문제

현실의 데이터 시스템은 단일 도구로 모든 것을 해결할 수 없다. OLTP DB, 검색 인덱스, 캐시, 데이터 웨어하우스, 스트림 처리 시스템이 공존한다. 각각이 잘하는 게 다르기 때문이다.

문제는 이 시스템들 사이에서 데이터를 어떻게 일관성 있게 유지하느냐다.

파생 데이터 vs 원본 데이터

책은 시스템 내 데이터를 두 종류로 나눈다.

원본 데이터(Source of Truth): 다른 데이터의 기준이 되는 데이터. 사용자가 직접 입력한 것. 주문 DB, 이벤트 로그.

파생 데이터(Derived Data): 원본 데이터에서 변환하거나 집계한 것. 검색 인덱스, 캐시, 추천 결과, 집계 통계. 원본이 있으면 언제든 다시 만들 수 있다.

이 구분이 중요한 이유는 파생 데이터를 신성시하면 안 된다는 것이다. 파생 데이터는 원본에서 만들어진 뷰일 뿐이다. 버그가 생기면 원본은 그대로 두고 파생 데이터를 다시 만들면 된다.

데이터 흐름 중심의 설계

책은 현대 데이터 시스템을 데이터 흐름(Dataflow) 관점으로 바라볼 것을 제안한다.

시스템을 "어떤 DB를 쓸 것인가"가 아니라 "데이터가 어떻게 흐르는가"로 설계하는 것이다.

사용자 주문 (원본)
    │
    ├──▶ 주문 DB (OLTP)
    │
    ├──▶ Kafka (이벤트 스트림)
    │         │
    │         ├──▶ 실시간 재고 갱신 (스트림 처리)
    │         ├──▶ 검색 인덱스 갱신 (CDC)
    │         ├──▶ 추천 모델 학습 데이터 (배치)
    │         └──▶ 데이터 웨어하우스 (분석)
    │
    └──▶ 감사 로그 (이벤트 소싱)

이 구조에서 Kafka가 중앙 허브 역할을 한다. 원본 데이터의 변경이 이벤트로 흘러 여러 파생 시스템을 갱신한다. 각 시스템은 독립적으로 자신의 역할에 집중한다.


정확성과 완전성

분산 시스템에서 "정확히 한 번" 처리는 달성하기 어렵다. 하지만 정확성을 완전히 포기할 필요는 없다. 다른 방식으로 접근할 수 있다.

멱등성으로 At-Least-Once를 Exactly-Once처럼

At-Least-Once 전달에서 중복 처리를 허용하되, 연산 자체를 멱등(Idempotent)하게 만드는 것이다.

잘못된 방식:
UPDATE balance SET amount = amount + 100 WHERE user_id = 1;
→ 중복 실행 시 200이 증가함

멱등한 방식:
UPDATE balance SET amount = 1600 WHERE user_id = 1 AND tx_id = 'tx_abc';
→ 같은 tx_id로 여러 번 실행해도 결과가 같음

트랜잭션 ID를 포함한 연산은 중복 실행에 안전하다.

보정 트랜잭션 (Compensating Transaction)

잘못된 처리가 발생했을 때 이를 취소하는 보정 이벤트를 추가한다. 원본 이벤트는 건드리지 않는다.

T1: 주문 생성 이벤트
T2: 재고 차감 이벤트 (버그로 잘못 차감)
T3: 재고 보정 이벤트 (+올바른 재고 복구)

이벤트 소싱에서 자연스럽게 나오는 패턴이다. 데이터를 직접 수정하는 대신 보정 이벤트를 추가해서 결과를 수정한다. 이력이 완전히 보존된다.

불변 이벤트와 재처리의 힘

책이 11장에서 시작해서 12장에서 강조하는 핵심 아이디어다.

원본 이벤트가 불변으로 보존되면 언제든 재처리가 가능하다. 버그를 발견했을 때 이벤트는 그대로 두고 처리 코드를 고쳐서 다시 돌린다. 잘못된 파생 데이터는 버리고 새로 만든다.

이 접근의 전제 조건은 충분한 저장 공간이다. 모든 이벤트를 영구 보존하려면 스토리지 비용이 든다. 하지만 스토리지 단가는 계속 낮아지고 있고, 재처리 가능성이 주는 안정감은 크다.


데이터베이스 내부 구조를 드러내기

12장의 기술적으로 흥미로운 아이디어 중 하나다.

책은 전통적인 DB가 내부적으로 유지하는 것들 — 인덱스, Materialized View, 복제 로그, 캐시 — 을 애플리케이션 레벨로 끌어올리는 방향을 제안한다.

DB가 알아서 인덱스를 관리하는 대신, 이벤트 스트림과 스트림 처리로 직접 인덱스에 해당하는 파생 데이터를 만들고 갱신한다.

장점: 더 유연하다. DB 인덱스는 DB 안에서만 쓸 수 있지만, 이벤트 기반 파생 데이터는 어떤 시스템에도 쓸 수 있다. DB → Elasticsearch 검색 인덱스, DB → Redis 캐시, DB → 데이터 웨어하우스가 모두 같은 원리로 동작한다.

단점: 일관성 관리가 복잡해진다. DB 트랜잭션 안에서 인덱스를 갱신하는 것과 달리, 이벤트 스트림으로 여러 시스템을 갱신할 때는 일부만 성공하는 상황이 생길 수 있다.


올바른 일 하기 — 데이터 윤리

책의 마지막 섹션이 가장 개발자답지 않으면서 가장 중요한 내용이다.

예측 분석과 차별

머신러닝 모델로 신용 점수를 매기고, 취업 지원자를 심사하고, 가석방 여부를 결정한다. 이런 자동화된 결정 시스템이 만들어지고 있다.

문제는 이 시스템들이 과거 데이터의 편향을 학습한다는 것이다.

과거에 특정 집단이 대출 심사에서 불이익을 받았다면, 그 데이터로 학습한 모델은 같은 집단에게 계속 불이익을 준다. 편향이 자동화되고 스케일이 커진다. 더 나쁜 건 "알고리즘이 결정했다"는 말로 책임 소재가 흐릿해진다.

이 시스템을 만드는 개발자로서 어떤 책임이 있는가?

피드백 루프의 위험

추천 시스템이 특정 콘텐츠를 많이 보여주면 그 콘텐츠에 더 많은 클릭이 발생한다. 더 많은 클릭은 추천 모델에 더 많은 학습 신호를 준다. 모델은 더 극단적인 콘텐츠를 추천하게 된다.

자기 강화 피드백 루프가 의도치 않은 결과를 만든다. 필터 버블, 극단화, 잘못된 정보의 확산. 모두 최적화된 시스템이 만들어낸 부작용이다.

시스템을 만들 때 "사용자가 더 많이 클릭하게 만들었는가"뿐만 아니라 "이 시스템이 사용자에게 좋은가"도 물어야 한다.

프라이버시

데이터를 모으고 처리하는 시스템을 만드는 개발자는 필연적으로 프라이버시 문제에 부딪힌다.

책은 프라이버시를 단순히 "개인정보를 암호화한다"의 문제가 아니라 더 근본적으로 바라본다.

익명화의 한계: 이름과 주민번호를 지워도 재식별이 가능한 경우가 많다. 위치 데이터, 구매 패턴, 브라우저 지문은 개인을 특정할 수 있다.

동의의 의미: 서비스 이용약관에 동의한다고 해서 데이터를 어떤 방식으로든 써도 된다는 의미가 아니다. 사용자가 예상하지 못한 방식으로 데이터를 사용하는 건 동의의 범위를 벗어난다.

데이터 최소화: 필요한 데이터만 수집한다. 모으면 나중에 쓸 수 있을 것 같다는 이유로 모든 것을 수집하는 건 리스크를 키운다. 수집하지 않은 데이터는 유출될 수 없다.


신뢰할 수 있는 시스템 만들기

기술적인 관점에서 12장이 제안하는 마지막 원칙들이다.

감사 가능성 (Auditability)

시스템이 어떤 결정을 내렸는지 나중에 설명할 수 있어야 한다. 이벤트 로그를 영구 보존하는 것이 이 목적에도 부합한다.

"왜 이 사용자에게 이 결정이 내려졌는가?"를 소급해서 확인할 수 있어야 한다. 불변 이벤트 로그가 있으면 가능하다. 상태만 저장하고 이력을 지우는 시스템에서는 불가능하다.

가정 명시하기

데이터 파이프라인 어딘가에 항상 가정이 있다. "이 데이터는 항상 이 포맷이다", "이 값은 항상 양수다", "이 이벤트는 항상 이 순서로 온다".

이 가정이 깨지면 시스템이 조용히 잘못된 결과를 만든다. 잘못된 집계, 잘못된 추천, 잘못된 결정.

가정을 코드에 명시적으로 검증하고, 위반 시 알림이 가야 한다. 조용한 실패보다 시끄러운 실패가 낫다.

발전 가능성 (Evolvability) 재확인

1장에서 언급했던 유지보수성의 핵심 속성이 마지막 장에서 다시 등장한다.

요구사항은 계속 바뀐다. 법이 바뀌고, 비즈니스 모델이 바뀌고, 사용자의 기대가 바뀐다. 시스템이 이 변화에 적응할 수 있어야 한다.

불변 이벤트 + 재처리 가능한 파생 데이터 아키텍처는 발전 가능성이 높다. 새로운 요구사항이 생기면 기존 이벤트를 새 방식으로 재처리하면 된다. 데이터를 건드리는 게 아니라 처리 방식을 바꾸는 것이다.


DDIA 전체를 마무리하며

12장을 포함해 DDIA 전체를 완독했다.

책이 1장에서 던진 질문 — "좋은 데이터 시스템이란 무엇인가?" — 에 책 전체가 하나의 긴 답변이었다.

기술적으로 배운 것들을 정리하면 이렇다.

1장에서 신뢰성, 확장성, 유지보수성이라는 프레임을 잡았다. 2~4장에서 단일 노드에서의 스토리지, 데이터 모델, 인코딩을 다뤘다. 5~9장에서 분산 환경의 복잡함을 마주했다. 복제의 지연, 파티셔닝의 핫스팟, 트랜잭션의 격리 수준, 합의 알고리즘의 어려움. 10~12장에서 대용량 데이터를 배치와 스트림으로 처리하는 방법을 배웠다.

하지만 12장이 말하는 더 중요한 메시지가 있다.

우리가 만드는 시스템은 사람들의 삶에 영향을 미친다. 데이터를 수집하고, 분석하고, 결정을 자동화하는 시스템을 만드는 사람으로서 기술적 정확성 너머의 책임이 있다.

코드 한 줄이 수백만 명의 신용 점수에 영향을 줄 수 있고, 추천 알고리즘이 사람들이 보는 세상을 형성할 수 있다. 이 힘을 인식하고 책임감 있게 사용하는 것이 좋은 엔지니어의 조건이라고 책은 말한다.

12장의 마지막 문장을 내 말로 다시 쓰면 이렇다.

데이터 시스템을 잘 만드는 것과, 올바른 데이터 시스템을 만드는 것은 다른 문제다. 둘 다 해야 한다.


DDIA 12장 시리즈를 끝까지 읽어주셔서 감사합니다. 1장부터 함께해주신 분들께 특히 감사드립니다. 틀린 내용이나 보완할 점은 댓글로 알려주세요.