핵심 인사이트 (3줄 요약)
- 본질: 이벤트 소싱(Event Sourcing)은 시스템 상태를 이벤트 스트림으로 저장해 언제든 과거 시점으로 재현(Replay)하는 패턴이다.
- 가치: 람다(Lambda)는 배치+스트림 이중 경로로 정확성과 저지연을 동시에 달성하고, 카파(Kappa)는 스트림 단일 경로로 운영 복잡도를 줄인다.
- 판단 포인트: 배치 재처리 비용과 운영 복잡도 허용 수준에 따라 람다 vs 카파를 선택하며, 최신 스트림 엔진(Flink, Spark Structured Streaming)은 카파 전환을 가속한다.
Ⅰ. 개요 및 필요성
1.1 이벤트 소싱 (Event Sourcing) 정의
이벤트 소싱은 애플리케이션 상태 변화를 이벤트(Event) 의 연속으로 저장하고, 현재 상태를 이 이벤트들을 순서대로 재적용(Replay)하여 도출하는 패턴이다. 전통적 CRUD 방식이 최종 상태만 저장하는 것과 대비된다.
| 방식 | 저장 단위 | 특징 |
|---|---|---|
| CRUD | 현재 상태(스냅샷) | 이력 손실, 간단한 구현 |
| 이벤트 소싱 | 이벤트 스트림 | 완전한 이력, 재현 가능 |
1.2 왜 이벤트 소싱이 필요한가?
- 감사 추적(Audit Trail): 모든 변경 사항이 불변(Immutable) 이벤트로 기록됨
- 시간 여행(Time Travel): 특정 시점의 상태 재현 가능
- 새 로직 소급 적용: 비즈니스 로직 변경 시 과거 이벤트 전체 재처리
- CQRS(Command Query Responsibility Segregation) 와 자연스러운 결합
1.3 이벤트 소싱 이벤트 스트림 개념
시간 흐름 →
┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐
│E1 │→ │E2 │→ │E3 │→ │E4 │→ │E5 │
│주문생성│ │결제완료│ │배송시작│ │배송완료│ │리뷰작성│
└──────┘ └──────┘ └──────┘ └──────┘ └──────┘
↓ Replay(재현)
┌─────────────────────────────────┐
│ 현재 상태 = 순서대로 이벤트 적용 │
│ 언제든 원하는 시점 상태 재현 가능 │
└─────────────────────────────────┘
📢 섹션 요약 비유: 이벤트 소싱은 "가계부"와 같다. 잔액만 적어두면 과거를 알 수 없지만, 입출금 내역을 모두 기록하면 어느 날의 잔액이든 계산해낼 수 있다.
Ⅱ. 아키텍처 및 핵심 원리
2.1 람다 아키텍처 (Lambda Architecture)
Nathan Marz가 제안한 빅데이터 처리 아키텍처로, 배치와 스트림 두 레이어를 병렬 운영하여 정확성과 저지연을 동시에 달성한다.
데이터 소스
│
├─────────────────────────────┐
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ 배치 레이어 │ │ 스피드 레이어 │
│ (Batch Layer) │ │ (Speed Layer) │
│ │ │ │
│ HDFS / S3 │ │ Kafka + Flink │
│ Spark Batch │ │ 실시간 처리 │
│ 고정확도 │ │ 저지연 │
│ 고지연(시간~일) │ │ 근사치(최근 데이터)│
└────────┬────────┘ └────────┬────────┘
│ │
▼ ▼
┌─────────────────────────────────┐
│ 서빙 레이어 │
│ (Serving Layer) │
│ │
│ 배치 뷰(Batch View) + │
│ 실시간 뷰(Realtime View) 병합 │
│ → 사용자 쿼리 응답 │
└─────────────────────────────────┘
| 레이어 | 역할 | 기술 스택 | 지연 |
|---|---|---|---|
| Batch Layer | 전체 데이터 정확 처리 | HDFS, Spark, Hive | 시간~일 |
| Speed Layer | 최근 데이터 빠른 처리 | Kafka, Flink, Storm | 초~분 |
| Serving Layer | 두 레이어 결과 병합 조회 | Cassandra, Redis, Druid | ms |
2.2 카파 아키텍처 (Kappa Architecture)
Jay Kreps(Kafka 창시자)가 람다의 복잡성을 비판하며 제안. 스트림 처리 단일 레이어로 배치 역할까지 수행.
데이터 소스
│
▼
┌─────────────────────────────────┐
│ 메시지 큐(Kafka) │
│ 이벤트 영구 보관 (무한 보존) │
│ 파티셔닝, 순서 보장 │
└────────────────┬────────────────┘
│
┌────────┴────────┐
│ │
▼ ▼
┌──────────────┐ ┌──────────────┐
│ 실시간 처리 │ │ 재처리(Replay) │
│ (Consumer v1)│ │ (Consumer v2) │
│ 현재 로직 적용│ │ 새 로직 소급 │
└──────┬───────┘ └──────┬───────┘
│ │
└────────┬────────┘
▼
┌─────────────────────────────────┐
│ 서빙 레이어 │
│ 단일 스트림 처리 결과 조회 │
└─────────────────────────────────┘
2.3 람다 vs 카파 비교
| 항목 | 람다 아키텍처 | 카파 아키텍처 |
|---|---|---|
| 처리 경로 | 배치 + 스트림 이중 경로 | 스트림 단일 경로 |
| 운영 복잡도 | 높음 (코드 이중화) | 낮음 (단일 코드베이스) |
| 재처리 방법 | 배치 재실행 | Kafka 오프셋 리셋 후 재처리 |
| 정확도 | 배치 기준 높은 정확도 | 스트림 처리 정확도 (근사 허용) |
| 적합 사례 | 복잡한 집계, ML 학습 | 이벤트 소싱, 실시간 파이프라인 |
| 도입 난이도 | 복잡 | 비교적 단순 |
2.4 CQRS (Command Query Responsibility Segregation)
CQRS는 명령(Command: 쓰기)과 쿼리(Query: 읽기)를 분리하는 패턴이다. 이벤트 소싱과 결합하면 강력한 시스템을 구성한다.
┌─────────────────────────────────────────────────┐
│ CQRS + 이벤트 소싱 │
│ │
│ 클라이언트 │
│ │ │
│ ├──── Command(쓰기) ──→ Command Handler │
│ │ │ │
│ │ ▼ │
│ │ 이벤트 스토어 │
│ │ (Kafka/EventStore) │
│ │ │ │
│ │ Projection │
│ │ (읽기 모델 생성) │
│ │ │ │
│ │ ▼ │
│ └──── Query(읽기) ──→ Read DB(최적화) │
│ (Redis/Elasticsearch) │
└─────────────────────────────────────────────────┘
📢 섹션 요약 비유: 람다는 두 개의 주방에서 요리하는 레스토랑(빠른 패스트푸드 창구 + 정성스러운 정식 주방)이고, 카파는 한 주방에서 빠르고 정확하게 모든 요리를 처리하는 효율적 주방이다.
Ⅲ. 비교 및 연결
3.1 이벤트 소싱 Replay 상세 흐름
[기존 로직 v1 처리 중]
Kafka Topic: order-events (offset 0 ~ 100,000)
오프셋 0 ──→ 이벤트 소비 ──→ 로직 v1 적용 ──→ DB 저장
[새 로직 v2 적용 필요]
┌─────────────────────────────────┐
│ 새 컨슈머 그룹(Consumer Group) │
│ 오프셋 0부터 재시작 │
│ 오프셋 0 ──→ 로직 v2 적용 │
│ 오프셋 1 ──→ 로직 v2 적용 │
│ ... │
│ 오프셋 100,000 ──→ v2 완료 │
└─────────────────────────────────┘
결과: v2 기준 전체 히스토리 재계산 완료
v1 결과와 병행 운영 후 전환 (Blue/Green)
3.2 Kafka를 이벤트 소싱 스토어로 활용
| Kafka 기능 | 이벤트 소싱 활용 |
|---|---|
| 토픽(Topic) 파티션 | 엔티티(Entity) ID 기준 파티셔닝 |
| 오프셋(Offset) | 이벤트 순서 보장 |
| 보존 기간(Retention) | 무기한 보존으로 완전한 이력 |
| 컨슈머 그룹 | 여러 Projection 병렬 처리 |
| 압축(Log Compaction) | 최신 상태 스냅샷 유지 |
3.3 이벤트 소싱 vs 전통 DB
| 비교 항목 | 전통 DB (CRUD) | 이벤트 소싱 |
|---|---|---|
| 저장 단위 | 현재 상태 | 이벤트 스트림 |
| 쿼리 복잡도 | 단순 SELECT | Projection 생성 필요 |
| 이력 추적 | 별도 감사 테이블 필요 | 기본 제공 |
| 확장성 | 수직적 확장 | 수평적 확장 |
| 디버깅 | 어려움 | 이벤트 재현으로 용이 |
| 스토리지 | 효율적 | 많은 스토리지 필요 |
📢 섹션 요약 비유: Kafka의 오프셋(Offset)은 책의 페이지 번호와 같다. 어느 페이지부터든 다시 읽을 수 있고, 새 독자(새 컨슈머 그룹)는 1페이지부터 자신만의 읽기를 시작할 수 있다.
Ⅳ. 실무 적용 및 기술사 판단
4.1 람다 vs 카파 선택 기준
| 상황 | 권장 아키텍처 | 이유 |
|---|---|---|
| 복잡한 집계 쿼리 + 정확도 최우선 | 람다 | 배치 레이어의 정확한 처리 |
| 실시간 이벤트 파이프라인 | 카파 | 단순한 운영, 재처리 용이 |
| 팀 규모 작음 | 카파 | 이중 코드베이스 유지 부담 |
| 레거시 배치 시스템 공존 | 람다 | 점진적 마이그레이션 |
| Flink/Spark Structured Streaming 도입 | 카파 | 배치/스트림 통합 API |
4.2 실무 구현 패턴: 이벤트 소싱 + 카파
[주문 시스템 예시]
사용자 주문
│
▼
Order Command Handler
│ OrderCreated 이벤트 발행
▼
Kafka Topic: order-events
├─ Partition 0: userId % N
├─ 무기한 보존(log.retention.ms=-1)
└─ Log Compaction 활성화
│
├──→ Consumer Group A: 재고 Projection → Redis
├──→ Consumer Group B: 결제 Projection → PostgreSQL
├──→ Consumer Group C: 검색 Projection → Elasticsearch
└──→ Consumer Group D: 분석 Projection → ClickHouse
4.3 재처리(Replay) 운영 절차
- 새 Consumer Group 생성:
order-events-v2-consumer - 오프셋 초기화:
--reset-offsets --to-earliest - 새 로직 컨테이너 배포: Blue/Green 전략으로 병렬 운영
- 재처리 완료 검증: 처리 카운트 및 결과 검증
- 트래픽 전환: 서빙 레이어에서 v2 결과로 전환
- 기존 Consumer 제거: v1 Consumer Group 중단
4.4 주요 도전과 해결책
| 도전 과제 | 해결 방법 |
|---|---|
| 이벤트 스키마 변화 | Apache Avro + Schema Registry |
| 재처리 시 성능 부하 | 별도 토픽/클러스터에서 재처리 |
| 이벤트 순서 보장 | 동일 파티션 키 사용 |
| 대용량 이벤트 스토어 | Kafka Tiered Storage |
| 스냅샷 없이 긴 이력 재현 | 주기적 스냅샷 + 이후 이벤트 적용 |
📢 섹션 요약 비유: 재처리(Replay)는 비디오 녹화본을 처음부터 다시 돌려보는 것과 같다. 같은 영상이지만 새로운 시각(로직)으로 다시 분석하면 다른 결론을 낼 수 있다.
Ⅴ. 기대효과 및 결론
5.1 이벤트 소싱 + 카파 아키텍처의 기대효과
| 효과 | 정량적 지표 |
|---|---|
| 운영 복잡도 감소 | 람다 대비 코드베이스 50% 감소 |
| 재처리 자동화 | 수동 배치 재실행 제거 |
| 완전한 감사 추적 | 모든 상태 변화 100% 추적 |
| 새 기능 빠른 배포 | Consumer Group 추가로 무중단 확장 |
5.2 기술 선택 가이드라인 (기술사 관점)
이벤트 소싱/스트림 아키텍처 의사결정 트리
스트림 처리 필요?
├─ NO → 전통 CRUD + 배치 ETL
└─ YES → 실시간 정확도 요구?
├─ 높음 + 복잡 집계 → 람다 아키텍처
│ (배치 + 스트림 병렬 운영)
└─ 보통 + 운영 단순화 → 카파 아키텍처
(스트림 단일 경로)
└─ 이력/감사 필요? → 이벤트 소싱 결합
5.3 현업 채택 현황
| 기업 | 아키텍처 | 용도 |
|---|---|---|
| Netflix | 람다 → 카파 전환 | 실시간 추천 |
| 카파 (Kafka 기반) | 사용자 활동 스트림 | |
| Uber | 이벤트 소싱 + CQRS | 실시간 주문/위치 처리 |
| Airbnb | 람다 → Flink 기반 카파 | 가격 최적화 |
5.4 결론 요약
이벤트 소싱은 데이터의 불변성과 재현성을 보장하는 강력한 패턴이며, 카파 아키텍처와 결합 시 운영 단순성과 실시간 처리를 동시에 달성한다. 기술사 시험에서는 람다 vs 카파의 트레이드오프와 Kafka를 이벤트 소싱 스토어로 활용하는 방법이 핵심 논점이다.
📢 섹션 요약 비유: 이벤트 소싱 + 카파 아키텍처는 "블랙박스 + 실시간 내비게이션" 조합과 같다. 블랙박스(이벤트 스토어)로 모든 주행 기록을 저장하고, 내비게이션(스트림 처리)이 실시간으로 최적 경로를 안내하며, 사고(장애) 시 블랙박스로 정확한 원인을 파악한다.
📌 관련 개념 맵
| 관계 | 개념 | 설명 |
|---|---|---|
| 저장 패턴 | Event Sourcing (이벤트 소싱) | 상태를 이벤트 스트림으로 저장 |
| 아키텍처 패턴 | Lambda Architecture (람다 아키텍처) | 배치 + 스트림 이중 경로 |
| 아키텍처 패턴 | Kappa Architecture (카파 아키텍처) | 스트림 단일 경로 |
| 설계 패턴 | CQRS | 명령과 쿼리 분리 |
| 인프라 | Kafka | 이벤트 스토어 + 메시지 큐 |
| 재처리 | Replay (재현) | 오프셋 리셋 후 재처리 |
| 표현 변환 | Projection (프로젝션) | 이벤트 → 읽기 모델 변환 |
| 스키마 관리 | Schema Registry | 이벤트 스키마 버전 관리 |
👶 어린이를 위한 3줄 비유 설명
- 이벤트 소싱은 일기를 매일 쓰는 것과 같아요. 일기를 처음부터 다시 읽으면 오늘의 나를 정확히 이해할 수 있죠.
📈 관련 키워드 및 발전 흐름도
CRUD 기반 상태 저장 (현재 상태만 유지)
│
▼
이벤트 소싱 (Event Sourcing): 모든 변화를 이벤트로 기록
├─► 이벤트 스토어: 불변 로그 (Kafka · EventStoreDB)
└─► 상태 재구성: 이벤트 리플레이 (Replay)
│
▼
람다 아키텍처: Batch Layer + Speed Layer 분리
│
▼
카파 아키텍처: Speed Layer만으로 통합 (Kafka 중심)
│
▼
CQRS: 쓰기 모델(Command)과 읽기 모델(Query) 분리
- 람다 아키텍처는 두 개의 창구가 있는 은행이에요. 빠른 창구(스트림)와 정확한 창구(배치) 두 곳에서 결과를 합쳐요.
- 카파 아키텍처는 스트리밍 서비스처럼 한 곳에서 모든 영상을 보여주는데, 필요하면 처음부터 다시 재생해서 새로운 자막(로직)도 입힐 수 있어요.