이벤트 소싱 (Event Sourcing) - 상태 덮어쓰기 파괴 및 절대 이력 보존 아키텍처
핵심 인사이트 (3줄 요약)
- 본질: 이벤트 소싱(Event Sourcing)은 데이터베이스(DB)에 '현재 내 통장 잔고는 5만 원이다'라는 최종 결과값(State) 하나만 달랑 덮어쓰기(UPDATE)로 저장하는 수십 년 묵은 관행을 쓰레기통에 쳐박고, "만원 입금", "2만 원 출금" 등 상태를 변화시킨 모든 '과거의 사건(Event) 이력'을 절대 삭제되지 않는 스트림으로 영구 보존하는 극강의 인프라 철학이다.
- 가치: 낡은 덮어쓰기(CRUD)의 끔찍한 한계인 "데이터가 왜 5만 원이 됐는지 과거의 원인을 추적할 수 없다"는 블랙박스 맹점을 100% 분쇄한다. 서버가 터지거나 DB가 깨져도, **태초부터 쌓인 이벤트 로그를 처음부터 쭉 순서대로 재생(Replay)하기만 하면 언제든지 현재 상태(5만 원)를 1초 만에 0.01%의 오차 없이 완벽하게 복구해 내는 신의 권능(불변성)**을 획득한다.
- 융합: 이 기괴하고도 완벽한 로깅 철학은, 마이크로서비스(MSA) 환경에서 데이터베이스가 수십 개로 쪼개져 정합성이 박살 나는 지옥을 막기 위해 **아파치 카프카(Apache Kafka) 같은 실시간 이벤트 버스(Message Queue)**와 결합하며 클라우드 네이티브 분산 트랜잭션의 궁극적 마스터키로 융합 진화했다.
Ⅰ. 개요 및 필요성 (Context & Necessity)
-
개념: 이벤트 소싱(Event Sourcing)은 애플리케이션의 상태(State)를 변경하는 모든 개별 작업들을 '이벤트(Event)'라는 불변(Immutable)의 데이터 구조로 캡처하고, 이를 이벤트 스토어(Event Store)에 발생한 시간 순서대로 덧붙여(Append-only) 저장함으로써 최종 상태를 영속화하는 데이터 아키텍처 패턴이다.
-
필요성: 2000년대까지만 해도 오라클(Oracle) 관계형 DB에
UPDATE문을 날려 데이터를 덮어쓰는(CRUD) 방식은 절대 진리였다. 내 쇼핑몰 장바구니에 '사과'가 있었다가 '바나나'로 수정(UPDATE)했다. DB에는 '바나나'만 남고 '사과'는 영원히 지워졌다. 그런데 빅데이터와 마케팅 시대가 오며 임원진이 개발자를 다그쳤다. "야! 고객이 결제하기 전에 장바구니에 사과를 담았다가 뺐던 기록 다 어딨어? 그걸 분석해야 우리가 물건을 팔지!" 개발자는 멘붕이 왔다. "수정(UPDATE)해서 옛날 데이터 다 날아갔는데요?" 더 끔찍한 일은 서버 버그였다. 은행 서버에 버그가 나서 모든 고객 통장 잔고가 0원으로 바뀌었다. 기존UPDATE방식으로는 어제 백업해 둔 테이프를 가져와 덮어씌우는 원시적인 복구 외에는 방법이 없었고, 어제와 오늘 사이의 거래 내역은 공중 분해되었다. "과거 데이터를 함부로 덮어쓰고 지우는 파괴적(Destructive)인UPDATE/DELETE행위 자체를 물리적으로 금지하라! 오직 세상에 일어난 모든 일을 장부에 '추가(Append)'만 해라!" 이 편집증적인 역사 기록 보존의 욕망이 이벤트 소싱을 탄생시켰다. -
등장 배경 및 기술적 패러다임 전환: 사실 이 철학은 하늘에서 뚝 떨어진 게 아니다. 은행의 회계 장부(원장, Ledger)는 수백 년 전부터 이 방식을 썼다. 통장에 잔액(결과)만 연필로 지웠다 쓰는 게 아니라, 입금 내역과 출금 내역을 볼펜으로 밑으로 쭉쭉 적어나가는 방식이다. IT 세계에서는 마이크로서비스 아키텍처(MSA)가 터지며 이벤트 소싱이 부활했다. 통짜 서버 시절엔 DB가 1개라 덮어써도(ACID) 문제가 없었다. 하지만 서버가 100개로 찢어지고 DB가 100개로 나뉘자, 회원 DB를
UPDATE했는데 배송 DB는 업데이트가 안 돼서 물건이 잘못 가는 분산 트랜잭션(Distributed Transaction) 지옥이 열렸다. "DB를 함부로 수정하지 마! 그냥 회원 서버가 '회원 탈퇴함'이라는 쪽지(이벤트)를 중앙 카프카(Kafka) 게시판에 던져둬(Append)! 그럼 배송 서버든 결제 서버든 그 쪽지를 읽어보고 각자 자기들 DB를 수정하게 냅둬!" 데이터의 '결과'가 아닌 '사건의 흐름(Stream)' 자체를 시스템의 진실(Single Source of Truth)로 격상시킨 이 패러다임의 전복이 클라우드 네이티브 데이터 헌법을 완전히 갈아엎었다.
이 다이어그램은 과거의 데이터를 무참히 지워버리는 낡은 CRUD 방식과, 모든 역사를 박제하는 이벤트 소싱의 아키텍처 차이를 폭로한다.
┌───────────────────────────────────────────────────────────────┐
│ 데이터 저장 아키텍처: 낡은 상태 덮어쓰기(CRUD) vs 이벤트 소싱(Event) │
├───────────────────────────────────────────────────────────────┤
│ │
│ [A. 레거시 CRUD 아키텍처 (파괴적인 덮어쓰기 폭망 💣)] │
│ - 유저의 행동: ① 장바구니 생성 ➔ ② 사과 담기 ➔ ③ 사과 빼고 바나나 담기 │
│ │
│ [ RDBMS 테이블 상태 변화 ] │
│ Time 1: [ ID: 1, Item: Null ] (생성 INSERT) │
│ Time 2: [ ID: 1, Item: 사과 ] (수정 UPDATE - 이전 Null 삭제됨) │
│ Time 3: [ ID: 1, Item: 바나나 ] (수정 UPDATE - 사과 영구 삭제됨 💥)│
│ │
│ ★ 참사: 사장님이 "유저가 결제 전에 뭘 고민하다 바꿨어?" 물어보면 절대 대답 못함. │
│ 버그 나서 DB가 깨지면 '사과' 시절로 과거로 되돌릴(Time travel) 방법 0%.│
│ │
│ [B. 이벤트 소싱 아키텍처 (불멸의 로그 스트림 🚀)] │
│ - 오직 이벤트 장부(Event Store)에 일어난 사건을 '추가(Append)'만 함! │
│ │
│ [ 이벤트 스트림 (Kafka / Event Store) ] │
│ Sequence 1: { Type: "CartCreated", Time: "10:00", Data: {} } │
│ Sequence 2: { Type: "ItemAdded", Time: "10:05", Data: "사과" } │
│ Sequence 3: { Type: "ItemRemoved", Time: "10:08", Data: "사과" } │
│ Sequence 4: { Type: "ItemAdded", Time: "10:09", Data: "바나나" } │
│ │
│ ★ 기적 1 (분석): 유저가 '사과'를 샀다가 변심해 뺐다는 심리 궤적(역사)이 100% 남음!│
│ ★ 기적 2 (재생): 시스템이 박살 나면 빈 DB에 Seq 1부터 4까지 1초 만에 촤르륵 │
│ 재생(Replay)해서 "최종 상태(바나나)"를 완벽하게 셀프 복구함! │
└───────────────────────────────────────────────────────────────┘
[다이어그램 해설] 이 마법의 핵심은 **'불변성(Immutability)'과 '재현성(Reproducibility)'**이다. B 방식(이벤트 소싱)에서는 데이터베이스(Event Store)에 기존 데이터를 지우는 DELETE나 수정하는 UPDATE 쿼리문이 아예 물리학적으로 금지(Block)되어 있다. 오직 INSERT (Append)만 미친 듯이 일어난다.
이벤트(사건)들은 한 번 기록되면 영원히 변하지 않는 화석(불변 데이터)이 된다. 만약 누군가 "현재 장바구니에 뭐 들었어?"라고 물어보면, 시스템은 그 자리에서 Sequence 1번부터 4번까지의 이벤트를 빛의 속도로 메모리(RAM)에 쫙 더하고 빼서(계산, Fold/Reduce 연산) "음, 최종 결과(State)는 바나나 1개네!"라고 그 즉시 상태를 렌더링(Projection)해서 보여준다. 데이터를 정지된 '사진(Snapshot)'으로 보관하는 게 아니라, 언제든 처음부터 끝까지 되감기-빨리감기 할 수 있는 '동영상 필름(Log Stream)'의 형태로 캡처해 낸 위대한 아키텍처의 도약이다.
- 📢 섹션 요약 비유: 낡은 CRUD 방식은 칠판에 계산하는 것과 같습니다. $10+5=15$라고 적었다가, 마음이 바뀌면 $15$를 지우개로 벅벅 지우고 $20$이라고 덮어씁니다. 나중에 친구가 "너 아까 15에다 무슨 수 더해서 20 됐냐?" 물어보면 지워버려서 절대 증명할 수가 없죠. 이벤트 소싱은 **'은행 통장 거래 내역서'**입니다. 지우개가 아예 없습니다. $10$이 있었다가, $5$가 들어왔고, 다시 $5$가 들어왔다고 밑으로 계속 볼펜으로 기록만(Append) 남깁니다. 누군가 내 통장 잔고(최종 상태)를 물어보면, 처음부터 찍힌 숫자를 1초 만에 쭈르륵 더해서 "$20원이야!"라고 알려줍니다. 계산기(DB)가 박살 나도 잉크로 적힌 거래 내역서만 살아있으면 언제든 통장 잔고를 100% 똑같이 되살려낼 수 있는 궁극의 백업 마술입니다.
Ⅱ. 아키텍처 및 핵심 원리 (Deep Dive)
이벤트 소싱의 3대 물리적 렌더링 파이프라인
데이터를 이벤트로 저장하면 가져다 쓸 때가 피곤하다. 이를 돌파하는 3단계 기믹이다.
| 핵심 파이프라인 | 영문 명칭 | 작동 원리 및 시스템 역할 | 낡은 시스템의 무엇을 박살 냈는가? |
|---|---|---|---|
| 1. 불변 저장소 | Event Store | "주문 접수됨", "결제 완료됨" 같은 과거 완료형(Past Tense) 명제로 이벤트 객체를 만들어 타임스탬프와 함께 오직 덧붙이기(Append-only)만 수행하는 초고속 DB. (Kafka, EventStoreDB) | 테이블 행(Row)에 락(Lock)을 걸고 UPDATE를 치며 핑이 폭발하던 RDBMS의 관계형 쓰기 병목 지옥을 파괴. |
| 2. 상태 재생기 | Replay & Projection | "철수의 현재 등급이 뭐야?"라는 질문이 오면, 철수의 가입(Seq 1)부터 포인트 차감(Seq 100)까지의 이벤트를 0.001초 만에 덧셈/뺄셈(Reduce 연산)하여 '현재 상태' 모양을 만들어냄. | 데이터베이스 스키마(컬럼)가 바뀌면 ALTER TABLE 치느라 DB가 뻗던 낡은 경직성 파괴. (그냥 재생 로직만 바꾸면 새 스키마로 화면 뚝딱 렌더링 됨). |
| 3. 스냅샷 캐싱 | Snapshotting | 철수가 10년 동안 앱을 써서 이벤트가 100만 개 쌓였다. 잔고를 물어볼 때마다 100만 번을 더하기(재생)하면 CPU가 타죽는다. 그래서 1만 번 단위로 "이 시점의 잔고는 5만 원"이라고 찰칵! 중간 저장(스냅샷)을 떠놓는 물리적 성능 타협기. | 모든 과거를 다 읽어야 하는 이벤트 소싱 태생의 '읽기(Read) 지연(Latency)' 딜레마를 메모리 꼼수로 구원. |
딥다이브: CQRS (명령과 조회의 분리)와의 피할 수 없는 강제 융합 (250번 문서)
면접관이 묻는다. "이벤트 소싱 쓰면 매번 1번부터 이벤트를 다 더해야 현재 상태를 안다며? 만약 100만 명의 유저가 '현재 내 잔고 보여줘!'라고 조회 쿼리를 미친 듯이 때리면 CPU가 다 계산하다가 서버 터지는 거 아니야?!" 정확한 지적이다. 이벤트 소싱은 '쓰기(Write, Append)' 속도는 우주 최강이지만, '읽기(Read, Replay)' 속도는 우주 최악의 쓰레기 구조를 가졌다. 이 끔찍한 물리적 한계를 부수기 위해 탄생한 영혼의 단짝이 바로 CQRS (Command Query Responsibility Segregation, 명령과 조회의 책임 분리) 패턴이다.
- 쓰기(Command)의 전당: 유저가 돈을 입금/출금하면 무조건 **'이벤트 스토어(Kafka 등)'**에 이벤트를 쏜다. 여긴 쓰기 전용(Append-only)이라 미친 듯이 빠르다.
- 동기화 파이프라인 (Projection): 이벤트 스토어에 쪽지가 쌓이는 즉시, 뒤에 숨어있는 '동기화 로봇(Projector)'이 그 쪽지를 낚아채서 더하기 빼기 연산을 백그라운드로 0.1초 만에 해치운다.
- 읽기(Query)의 전당: 로봇은 그 계산된 '최종 결과(잔고 5만 원)'를 조회가 엄청나게 빠른 별도의 **'읽기 전용 데이터베이스(Redis, MongoDB, Elasticsearch)'**에 예쁘게 저장(Projection)해 둔다.
- 결과: 유저 100만 명이 접속해서 "내 잔고 보여줘!"라고 난리를 쳐도, 무거운 이벤트 스토어는 1도 안 건드리고 엄청나게 빠른 읽기 전용 DB(Redis)에서 "5만 원!"이라는 정답만 0.001초 만에 튕겨내 준다. 쓰기 DB와 읽기 DB를 물리적으로 찢어버려 서로의 병목을 원천 차단해 버리는, 이벤트 소싱 생태계의 호흡기를 단 절대 헌법이다.
- 📢 섹션 요약 비유: 이벤트 소싱만 쓰는 건 **'도서관에 책을 가나다순 정리도 안 하고 무식하게 창고에 다 때려 박는 것(쓰기 엄청 빠름)'**입니다. 근데 손님이 "해리포터 찾아줘" 하면 창고를 다 뒤져야 하니 1년 걸리죠(읽기 최악). CQRS와 융합하는 건, 창고에 책을 때려 넣자마자, 뒤에 숨어있는 똑똑한 알바생이 그 책 제목을 예쁜 **'가나다순 목차 책받침(읽기 전용 DB)'**에 빛의 속도로 적어두는 겁니다. 손님이 오면 무거운 창고(이벤트 스토어)를 안 뒤지고, 그냥 얇은 목차 책받침(Redis)만 쓱 보고 "3번 방에 있네!"라고 1초 만에 찾아주는 완벽한 쓰기/읽기 분업 시스템입니다.
Ⅲ. 융합 비교 및 다각도 분석 (Comparison & Synergy)
관계형 DB의 붕괴: 전통적 RDBMS vs Event Sourcing 생태계
왜 40년 묵은 오라클(Oracle) 테이블 덮어쓰기 사상을 버려야만 하는가?
| 비교 항목 | 전통적 RDBMS (CRUD) 아키텍처 | 이벤트 소싱 (Event Sourcing) 아키텍처 |
|---|---|---|
| 저장하는 것 | 현재 시점의 최종 결과값 (State) | 최종 결과가 나오기까지의 모든 과정 이력 (Event) |
| 데이터 수정/삭제 | UPDATE, DELETE 쿼리로 과거 데이터 덮어쓰거나 영구 파괴. | 수정/삭제 절대 금지! 잘못된 게 있으면 "취소 이벤트(-500원)"를 추가로 덧붙여서 보정(Compensating)함. |
| 타임 트래블 (과거로 회귀) | 불가능. DB를 과거 스냅샷 시점으로 끄고 덮어써야 함(대형 사고). | 1초 만에 가능. "어제 오후 3시까지의 이벤트만 재생해!"라고 하면 타임머신처럼 과거 상태로 완벽 복원. |
| MSA 트랜잭션 | 여러 서버의 DB를 묶을 때 2PC(Two-Phase Commit) 등 무거운 락(Lock) 걸다가 데드락 터져 다 죽음. | 락(Lock) 따위 없음. 그냥 카프카에 이벤트 던져두면 다른 서버들이 나중에 알아서 가져감 (최종적 일관성 보장 🚀). |
| 최악의 단점 | 데이터 분석 시 과거의 변화 궤적(히스토리) 추적 불가. | 학습 곡선(Learning Curve)이 헬게이트. 초보 개발자가 CQRS 엮다가 시스템 아키텍처 스파게티 됨 💀. |
딥다이브: 이벤트 소싱의 극강 무기, 다중 프로젝션 (Multiple Projections)
CRUD 기반 RDBMS에서는 "쇼핑몰 유저 정보를 RDBMS에 표(Table)로 저장했는데, 이번에 검색 기능을 짱짱하게 만들려고 엘라스틱서치(ES) 검색 엔진을 추가하고 싶어! 데이터 어떻게 옮겨?"라고 하면 죽음의 데이터 마이그레이션 노가다가 시작된다. 이벤트 소싱 아키텍처에서는 이 고민이 1초 만에 해결된다. 이벤트 스토어에 10년 치 '유저 가입/수정 이벤트' 기록이 불변의 덩어리로 쌓여있다(Single Source of Truth). 아키텍트는 오늘 새로 만든 '엘라스틱서치 검색용 프로젝터(Projector 로봇)' 스크립트 전원만 딱 켠다. 이 로봇은 이벤트 스토어의 1번 기록부터 오늘의 100만 번 기록까지 미친 듯이 0.1초 만에 쫙 스캔(Replay)하더니, 검색하기에 가장 완벽한 텍스트 형태(Document)로 조립하여 엘라스틱서치 DB 안에 예쁘게 쏟아붓는다. 내일은 머신러닝(AI) 팀에서 데이터를 달라고 한다? 그럼 머신러닝용 모델로 예쁘게 조립해 주는 프로젝터 로봇 2호기를 하나 더 켜서 또 재생(Replay)하면 끝이다. "절대 변하지 않는 거대한 원본 과거 기록 1개(이벤트)를 바탕으로, 내 입맛에 맞는 100가지 형태의 찰흙 결과물(뷰/Read Model)을 언제든, 몇 번이든 새로 찍어낼 수 있다!" 이것이 데이터 파편화를 부수고 클라우드 데이터 파이프라인을 통일하는 진정한 르네상스다.
- 📢 섹션 요약 비유: RDBMS(CRUD)는 **'이미 구워져서 굳어버린 도자기'**입니다. 도자기로 물은 마실 수 있지만, 갑자기 접시가 필요해졌다고 도자기를 부숴서 접시로 다시 빚을 순 없죠. 처음부터 접시를 다시 구워야 합니다. 이벤트 소싱은 도자기가 아니라, 도자기를 빚기 전의 **'무한한 찰흙 덩어리(이벤트 원본)'**를 랩에 싸서 영원히 보관하는 겁니다. 내일 접시가 필요하면 찰흙을 꺼내 접시로 빚어 쓰고(프로젝션 1), 모레 컵이 필요하면 똑같은 찰흙을 다시 뭉쳐서 컵으로 빚어 쓰면(프로젝션 2) 되는 무적의 자유도 변신 마법입니다.
Ⅳ. 실무 적용 및 기술사적 판단 (Strategy & Decision)
실무 시나리오 및 설계 안티패턴
-
시나리오 — 마이크로서비스(MSA) 결제 시스템의 분산 트랜잭션 붕괴 구원 (Saga Pattern 융합): 100만 명의 트래픽을 버티기 위해 코드를 찢었다. 유저가 결제 버튼을 누르면 1) '결제 컨테이너'가 돈을 빼고, 2) '재고 컨테이너'가 재고를 1개 줄여야 한다. 그런데 돈은 뺐는데 재고 컨테이너가 벼락을 맞아 서버가 죽어버렸다! 유저는 돈만 뺏기고 물건은 못 받는 미친 소송 사태(데이터 불일치)가 터졌다.
- 의사결정: 과거처럼 1번과 2번을 사슬(Lock)로 묶어놓고 "둘 다 성공할 때까지 절대 딴짓하지 마!(2PC)"라고 하면 DB가 렉이 걸려 다 뻗는다. 아키텍트는 이벤트 소싱과 사가(Saga) 패턴을 결합한다. '결제 컨테이너'는 그냥 돈을 빼고 이벤트 버스(Kafka)에
"결제 5만 원 성공함"이라는 불변의 쪽지(이벤트)를 확 던지고 지 갈 길을 간다. 만약 '재고 컨테이너'가 죽어있다면? 상관없다. 카프카 게시판에 쪽지는 절대 안 지워지고 영원히 남아있다. 10분 뒤 죽었던 재고 서버가 재부팅되어 살아나면, 자기가 뻗어있던 동안 못 봤던 카프카 게시판의"결제 성공함"쪽지를 그제야 주워 읽고 "아차차! 재고 1개 뺄게!" 하고 뒤늦게라도 무조건 처리(최종적 일관성, Eventual Consistency)해 낸다. 만약 진짜로 재고가 0개라서 뺄 수 없다면? 재고 서버가 다시"재고 부족해서 취소함"이라는 이벤트를 던지고, 결제 서버가 그걸 읽어 유저에게 "고객님 환불해 드렸습니다"라고 뒤늦게 돈을 꽂아주는(보상 트랜잭션) 우주 최강의 유연한 비동기 에러 방어막이 완성된다.
- 의사결정: 과거처럼 1번과 2번을 사슬(Lock)로 묶어놓고 "둘 다 성공할 때까지 절대 딴짓하지 마!(2PC)"라고 하면 DB가 렉이 걸려 다 뻗는다. 아키텍트는 이벤트 소싱과 사가(Saga) 패턴을 결합한다. '결제 컨테이너'는 그냥 돈을 빼고 이벤트 버스(Kafka)에
-
안티패턴 — 단순 CRUD 게시판 앱에 무지성 이벤트 소싱+CQRS 남용 (Over-engineering): 스타트업 팀장이 블로그에서 "요즘 대세는 넷플릭스처럼 이벤트 소싱에 CQRS 카프카(Kafka) 붙이는 거다!"라며 하루 방문자 1,000명짜리 사내 직원용 공지사항 게시판을 이 구조로 짜라고 지시했다.
- 결과: 공지사항 글 하나 쓸 때마다 이벤트 스토어에 데이터가 저장되고, 백그라운드 동기화 봇이 돌고, 조회용 Redis 캐시 DB에 저장되는 미친듯한 3단계 파이프라인이 굴러갔다. 동기화에 0.5초(지연)가 걸리는 바람에, 직원이 글을 쓰고 '새로고침'을 찰칵 눌렀는데 자기가 쓴 글이 화면에 안 보이는 현상(최종적 일관성의 딜레이)이 터졌다. 빡친 직원들이 "게시판 렉 오지네!"라며 쌍욕을 박았다. 게시판 띄우겠다고 AWS에 Kafka 클러스터 수백만 원짜리 비용이 매달 나가서 스타트업은 파산했다.
- 해결책: 이벤트 소싱은 **'수류탄을 잡는 핀'**이다. 돈이 오가는 은행, 복잡한 재고 추적이 필요한 커머스, 하루 100만 건 트래픽이 쏟아져 DB 락(Lock)이 걸리면 다 죽는 **'초핵심 트랜잭션 도메인'**에만 핀셋으로 투입해야 하는 수술 칼이다. 글 수정 내역(History)을 다 추적할 필요도 없고 트래픽도 널럴한 단순 게시판이나 설정 페이지는 무조건 구시대의 낡은 **'RDBMS CRUD 덮어쓰기 + 하나의 통짜 코드'**로 순식간에 찍어내는 것이 100만 배 똑똑한 기술적 겸손(K.I.S.S 원칙)이다.
데이터 일관성 (Consistency) 및 저장 아키텍처 의사결정 트리
우리는 덮어써도 되는 데이터를 다루는가, 과거의 흔적이 생명인 돈을 다루는가?
┌───────────────────────────────────────────────────────────────────┐
│ 마이크로서비스 데이터 영속성 (Data Persistence) 설계 의사결정 트리 │
├───────────────────────────────────────────────────────────────────┤
│ │
│ [사내 앱을 마이크로서비스(MSA)로 찢어 배포하며, 데이터 저장 방식을 결정할 요건 발생]│
│ │ │
│ ▼ │
│ 우리가 다루는 데이터가 장바구니 내용, 결제 로그, 배송 상태 변경 이력 등 │
│ "과거에 유저가 무슨 짓을 해서 이 결과가 나왔는지" 100% 추적(Audit)해야 하는가?│
│ ├─ 아니오 (사내 게시판 글, 임시 프로필 사진 등 과정은 알 바 아니고 최종본만 중요함)│
│ │ └──▶ [ 🚨 기존 RDBMS 상태 덮어쓰기 (CRUD) 방식 완벽 유지! ] │
│ │ - 굳이 어려운 이벤트 버스 태우지 말고 `UPDATE` 편하게 갈겨라.│
│ │ │
│ └─ 예 (회계 장부라서 중간에 돈이 어디로 샜는지 1원 단위 기록이 무조건 필수임) │
│ │ │
│ ▼ │
│ 여러 개로 쪼개진 서버(컨테이너)들이 하나의 결제 요청을 처리하기 위해 │
│ 절대로 1개의 트랜잭션 에러(데이터 꼬임)도 허용하지 않아야 하는가? │
│ ├─ 아니오 ──▶ [ 일반 로그 DB (Elasticsearch) 적재 등 단방향 로깅으로 타협 ]│
│ │ │
│ └─ 예 (결제 깎였는데 쿠폰 안 깎이면 회사 매출 수억 원 빵꾸남) │
│ │ │
│ ▼ │
│ [ 이벤트 소싱 (Event Sourcing) + 비동기 메시지 버스(Kafka) 전격 도입! 🚀 ]│
│ - 테이블에 덮어쓰기(`UPDATE`) 행위를 물리적으로 차단하고 `INSERT`만 허용. │
│ - 이벤트 스트림을 카프카로 쏴서 모든 MSA 서버가 알아서 자기 DB를 수정하게 유도.│
│ - 읽기 지연(Read Latency) 병목이 100% 터지므로 CQRS 분리 패턴 즉각 융합 공사 돌입!│
│ │
│ 판단 포인트: "과거를 지우는 행위(UPDATE)는 편하지만 정보의 영구적 파괴를 낳는다. │
│ 데이터가 100배 많이 쌓이는 비용을 감수하더라도, 시간을 멈추고 되감을 수 있는│
│ 신의 권능(Replay)을 얻는 것이 클라우드 시대 데이터 장악력의 핵심이다." │
└───────────────────────────────────────────────────────────────────┘
[다이어그램 해설] 이 트리는 백엔드 아키텍트가 MSA 뽕에 취해 시스템을 스파게티 지옥으로 밀어 넣는 것을 막는 생명줄이다. 코드를 MSA로 10개로 찢으면, DB도 무조건 10개로 찢어진다. (Database per Service 원칙). 이때 1번 서버의 A 테이블과 2번 서버의 B 테이블을 완벽하게 동시에 저장시키는 고전적인 2PC(Two-phase commit) 락(Lock)을 걸면 시스템 속도가 1/100로 느려지고 하나 뻗으면 다 뻗는 동반 자살 지옥이 된다. 이 분산 데이터의 저주를 푸는 유일한 탈출구가 바로 **'이벤트 소싱 기반의 비동기 메시지 큐 통신'**이다. 1번 서버는 그냥 할 일 끝내고 쪽지(이벤트)를 툭 남기고 돌아서면 끝이다(속도 우주 최강). 쪽지를 언제 주워가서 2번 서버가 자기 DB를 맞출지는 나중 문제다(Eventual Consistency). 찰나의 순간 동안 데이터가 짝짝이로 불일치하는 '지연 현상'을 비즈니스적으로 쿨하게 용인(Tolerate)할 수 있는 기업(예: 쿠팡 결제 후 "주문 확인 중입니다"라고 3초 띄워두고 백그라운드로 맞추는 꼼수)만이 이 무지막지한 아키텍처의 가속 페달을 밟을 자격이 있다.
- 📢 섹션 요약 비유: 일반 CRUD 방식은 친구 여러 명이 **'하나의 공용 일기장'**에 돌아가며 일기를 쓰는 겁니다. 한 친구가 일기장을 꽉 잡고(DB Lock) 글을 쓰는 동안, 다른 친구 99명은 일기를 못 쓰고 뒤에서 줄을 서서 멍때려야 하죠(병목 터짐). 이벤트 소싱(카프카 융합)은 일기장 대신 허공에 **'무한한 우체통(이벤트 버스)'**을 놔두는 겁니다. 100명의 친구가 남 눈치 보지 않고 각자 자기 할 일(이벤트 쪽지)을 적어서 우체통에 동시에 퍽퍽 집어 던집니다(미친 속도). 나중에 똑똑한 사서 요정(Projector)이 빈 시간에 그 쪽지들을 꺼내서 알아서 예쁜 일기장에 순서대로 붙여주니 100명이 1초도 기다릴 필요가 없는 궁극의 논블로킹(Non-blocking) 글쓰기 시스템입니다.
Ⅴ. 기대효과 및 결론
정량/정성 기대효과
| 구분 | 레거시 상태 덮어쓰기 (CRUD DB) | 이벤트 소싱 (Event Sourcing) 기반 아키텍처 | 개선 효과 |
|---|---|---|---|
| 정량 (DB 쓰기 속도) | UPDATE 락(Lock) 경합으로 트래픽 폭주 시 쓰기 지연 폭발 | 단순 INSERT (Append) 전용으로 락(Lock) 소멸 | 블랙 프라이데이 결제 Write 병목(I/O 지연) 99% 무결점 증발 |
| 정량 (감사/Audit 및 분석) | 최종 상태 1개만 남아 과거 유저 행동 패턴 이력 0% 보존 | 10년간 클릭한 모든 행동 이력이 100% 순서대로 박제 | 고객 데이터 이탈 원인 분석을 위한 AI 데이터 레이크 빅데이터 수집률 무한대 확장 |
| 정성 (시스템 복원력) | DB 백업본(.bak) 깨지면 롤백 불가. 영구적 데이터 소실 | 첫 이벤트부터 재실행(Replay)으로 특정 과거 시점(1초 전) 완벽 복원 | 치명적 배포 버그 발생 시 데이터 오염 전 시점(Time-travel)으로의 궁극적 무손실 롤백 달성 |
미래 전망
- 이벤트 스트리밍의 사실상 표준화 (Apache Kafka의 득세): 이벤트 소싱의 생명은 '이벤트 쪽지가 절대 지워지거나 순서가 꼬이지 않아야 한다'는 것이다. 과거 무거운 엔터프라이즈 서비스 버스(ESB)나 단순 MQ 장비들은 트래픽이 터지면 쪽지를 잃어버렸다(Loss). 지금은 하루에 수조 개의 이벤트를 때려 박아도 디스크에 무식하게 다 순서대로 적어버려서 절대 잃어버리지 않는 **아파치 카프카(Apache Kafka)**가 이벤트 소싱 생태계의 대동맥을 100% 천하 통일했다. 카프카 없는 이벤트 소싱은 총알 없는 기관총이다.
- 스트림 프로세싱 (Stream Processing, KSQL / Flink) 과의 극단적 융합: 이벤트가 쪽지로 카프카에 쌓였다. 옛날엔 그 쪽지(이벤트)를 나중에 꺼내서 계산(Batch)했다. 느렸다. 이제 넥스트 아키텍트들은 이벤트가 카프카 파이프를 타고 슝 날아가는 그 찰나의 공중(허공) 비행시간에 코드를 꽂아버린다. 날아가는 "1만 원 결제 이벤트" 1,000개를 파이프 안에서 실시간으로 낚아채 더하고(KSQL, Apache Flink), "10분 동안 결제액 1,000만 원 돌파!"라는 통계를 0.1초 만에 렌더링해서 대시보드에 뿌려버린다(Real-time Projection). 멈춰있는 웅덩이(DB)에서 데이터를 꺼내는 시대가 끝나고, 흐르는 강물(Stream) 위에서 낚시하며 실시간으로 장사를 끝내는 '진정한 의미의 실시간 빅데이터 아키텍처'가 세상을 집어삼키고 있다.
참고 표준
- CQRS (Command Query Responsibility Segregation, 250번 문서): 이벤트 소싱을 현업에서 쓸 때 반드시 1+1 세트로 도입해야 하는 디자인 패턴 표준 헌법. 명령(쓰기)과 조회(읽기) 모델을 물리적으로 찢어발겨 이벤트 소싱의 치명적인 "느린 조회 속도" 단점을 완벽하게 덮어버리는 구원 투수 패턴.
- 아파치 카프카 (Apache Kafka): 링크드인에서 시작해 전 세계 클라우드 데이터 파이프라인을 통일한 오픈소스 분산 이벤트 스트리밍 플랫폼. "데이터는 덮어쓰는 것이 아니라 흐르는(Stream) 것이다"라는 이벤트 소싱의 철학을 쇳덩어리와 코드로 완벽하게 물리적 구현해 낸 심장 엔진.
"우리는 결코 현재를 저장하지 않는다. 오직 수많은 과거의 사건들이 모여 지금의 현재를 렌더링 할 뿐이다." 이벤트 소싱(Event Sourcing)은 단순한 데이터베이스 저장 기법이 아니라, 컴퓨터 공학이 '시간(Time)'을 대하는 태도를 뿌리부터 뒤엎은 철학적 혁명이다. 기존의 IT 시스템은 용량을 아끼기 위해 과거의 흔적을 덮어쓰고(Update) 파괴하며 '오직 현재라는 찰나의 스냅샷'만을 남기는 피상적인 세계였다. 하지만 메모리와 하드디스크 값이 똥값이 된 빅데이터 시대에 과거를 파괴하는 행위는 최악의 범죄다. 이벤트 소싱은 우주의 모든 미세한 진동과 사건(Event)을 지워지지 않는 잉크로 뼛속에 새겨 영구 보존하는 디지털 역사학이다. 이 무식하지만 위대한 덧붙이기(Append-only) 장부 덕분에, 기업은 언제든 시계를 거꾸로 돌려 과거의 버그를 치료하고, 찢겨나간 100개의 마이크로서비스(MSA) 조각들을 하나의 운명처럼 흐르게 만드는 진정한 클라우드 네이티브의 시공간(Spacetime) 초월 지배력을 손에 거머쥐게 된 것이다.
- 📢 섹션 요약 비유: 옛날 장부(CRUD)는 **'칠판에 분필로 쓰는 계산기'**입니다. 100+50 = 150이라고 적었다가, 다시 20을 빼면 150을 지우개로 벅벅 지우고 130이라고 씁니다. 당장 숫자를 보기는 편하지만, 중간에 누가 150을 지우고 999로 훔쳐 적어(해킹/버그) 놔도 왜 숫자가 바뀌었는지 아무도 모르는 치명적 약점이 있죠. 이벤트 소싱은 경찰서 취조실의 **'절대 꺼지지 않는 24시간 녹음기'**입니다. 내가 한 말과 모든 행동을 테이프(카프카)에 절대 지워지지 않게 처음부터 끝까지 영구 녹음(Append) 해버립니다. 용의자(데이터)가 "난 그런 말 한 적 없다!"라고 우겨도, 녹음기를 되감기(Replay)해서 틀면 처음부터 진실이 1초 만에 100% 복원되어 범인(버그)을 잡아내는 반박 불가 무결점의 증거 보존 시스템입니다.
📌 관련 개념 맵 (Knowledge Graph)
| 개념 명칭 | 관계 및 시너지 설명 |
|---|---|
| CQRS (명령/조회 분리, 250번) | 이벤트 소싱을 실전에서 쓰기 위한 1+1 강제 동반자. 이벤트 소싱이 글쓰기(Write)의 스피드를 100배로 올렸다면, CQRS는 그 글을 읽는(Read) 스피드의 끔찍한 병목을 해소해 주는 찰떡 파트너. |
| 마이크로서비스 (MSA, 199번) | 결제 서버와 배송 서버가 분리되어 DB를 각자 쓸 때, 이 둘의 데이터를 억지로 하나로 묶지 않고 '이벤트 쪽지'를 던지게 만들어 꼬리 자르기 생존(Decoupling)을 가능하게 해 준 배경 사상. |
| Saga (사가 패턴) | 찢어진 MSA 서버들이 쪽지(이벤트)를 주고받다가 중간 서버가 뻗었을 때, "야 이전 서버들한테 취소하라고 쪽지(- 마이너스 이벤트) 날려!"라며 롤백을 지휘하는 분산 에러 복구 디자인 패턴. |
| REST API 성숙도 (245번) | REST API는 상태를 덮어쓰는 동기식 요청(PUT, PATCH)을 주로 쓰는데, 이벤트 소싱은 이 무식한 덮어쓰기 통신을 비동기식 이벤트 스트리밍(Kafka)으로 싹 다 박살 내버린 대척점의 끝판왕. |
| 불변 인프라 (Immutable, 204번) | 이벤트 소싱이 데이터(DB)를 덮어쓰지 않고 새로운 걸 계속 추가(Append)하는 철학이라면, 불변 인프라는 서버(인스턴스)를 덮어써서 패치하지 않고 새로 찍어내는 철학. 완벽한 프랙탈 구조다. |
👶 어린이를 위한 3줄 비유 설명
- 칠판에 "내 용돈 500원"이라고 적었다가, 엄마한테 500원을 더 받으면 지우개로 벅벅 지우고 "1,000원"이라고 덮어쓰죠? (이게 옛날 컴퓨터 방식이에요)
- 근데 나중에 동생이 몰래 지우고 "0원"이라고 써놓으면, 내가 언제 500원을 받았었는지 과거의 증거(기록)가 지워져서 억울하게 울게 돼요.
- 이벤트 소싱은 지우개를 아예 버려버리고, 공책에 "월요일 500원 받음, 화요일 500원 받음"이라고 절대 안 지워지는 펜으로 밑으로 쭉~ 계속 이어서 적어 내려가는 마법의 일기장이랍니다! 언제든 처음부터 읽기만 하면 내 진짜 용돈(1,000원)을 정확히 계산해 낼 수 있어요!