학습 목표
topic·partition·offset으로 Kafka 로그 모델을 설명할 수 있다.
partition key와 ordering 범위를 설계할 수 있다.
consumer group으로 병렬 소비·lag를 이해할 수 있다.
retention·replay로 backfill·재처리 경로를 설계할 수 있다.
앞 편 ingest 레이어에서 Kafka가 어떤 역할인지 연결할 수 있다.
문제 상황
- 앱이 DB에 직접
INSERT— 피크에 WAL·lock 포화, 분석 쿼리와 경합 - RabbitMQ로 이벤트 보냈는데 한 consumer만 — 처리량 천장, 메시지 유실 걱정
- “Kafka 썼다” — topic 하나에 파티션 1개, consumer 10개인데 9개 idle
order_id순서가 중요한데 전역 순서를 기대 — partition 설계 미스- retention 7일인데 한 달 전 데이터 재처리 필요 — replay 방법을 모름
- 3편에서 unbounded ingest에 Kafka가 나왔지만 — topic·group 개념이 없음
이번 편은 스트림 ingest의 표준 버퍼 — Kafka 로그 모델과 확장·순서·재처리 설계다. Spark로 처리하는 방법은 5~6편.
1. Kafka가 ingest에 쓰이는 이유
앞 편 data plane에서 ingest는 소스와 process 사이 버퍼다. Kafka는 그 역할의 대표 구현이다.

| 역할 | Kafka가 해주는 것 |
|---|---|
| Decouple | producer·consumer 속도·장애 분리 |
| Buffer | burst 흡수, downstream 처리 지연 허용 |
| Durability | 디스크 복제 로그 — broker 재시작 후 복구 |
| Fan-out | 같은 topic을 여러 consumer group이 독립 소비 |
| Replay | retention 내 offset 되감기 — backfill·재처리 |
App / CDC ──► Kafka topic ──► Spark streaming
├──► warehouse sink (Connect)
└──► fraud detector (another group)
- Message queue와 다름 — consumer가 읽어도 로그 삭제 안 됨 (retention까지 보관)
- ELK — 로그 검색 축; Kafka는 분석·처리용 이벤트 버스
- Database CDC — Debezium 등이 change event를 topic에 append
- Kafka 없이도 ingest 가능 — 파일 landing·Kinesis 등 — 원리는 버퍼·내구·fan-out
2. Topic · Partition · Offset
Kafka 데이터는 topic 아래 partition 로그에 append-only로 쌓인다.

| 개념 | 정의 |
|---|---|
| Topic | 논리 스트림 이름 (예: orders.events) |
| Partition | topic을 나눈 순서 로그 — 병렬 단위 |
| Offset | partition 내 순번 (0, 1, 2, …) — 불변 |
| Broker | partition 복제본을 저장하는 서버 |
topic: orders.events
partition 0: [o0][o1][o2]... offsets 0..N
partition 1: [o0][o1]... offsets 0..M
partition 2: [o0][o1][o2][o3]...
- Append-only — 기존 레코드 수정·삭제 없음 (compact topic은 키 기준 예외, §5)
- Partition 수 — topic 생성 시 정함 — 늘리기는 가능, 줄이기는 사실상 불가
- Throughput — partition마다 순서 I/O — partition 많을수록 쓰기·읽기 병렬
- Replication — partition leader + follower —
replication.factor로 내구 (운영)
3. Ordering과 Partition Key
순서는 partition 안에서만 보장된다. 전 topic 전역 순서는 기대하지 않음.

| 설계 | key | 순서 보장 |
|---|---|---|
| 주문 상태 | order_id |
같은 주문 이벤트 순서 |
| 사용자 클릭 | user_id |
같은 사용자 내 순서 |
| 순서 무관 | null / random |
라운드로빈 — 최대 throughput |
| 전역 순서 필요 | partition 1개 | 병렬성 0 — 흔히 피함 |
# code/producer_partition_key.py (발췌)
producer.send(
topic="orders.events",
key=str(order_id).encode(), # same key → same partition
value=json.dumps(event).encode(),
)
# hash(key) % num_partitions → partition id
- Hot partition — key 편향 (대형 tenant) → 한 partition만 포화
- Key 없음 — producer가 round-robin — 순서 없음, 균등 분산
- Ordering vs scale — 필요한 최소 key 범위만 순서 — 나머지는 partition 늘림
- Downstream — Spark·Flink도 같은 key로 shuffle·state — ingest 설계와 맞춤
4. Consumer Group
Consumer group — 같은 group.id를 쓰는 consumer들이 partition을 나눠 읽는다.

| 개념 | 의미 |
|---|---|
| Group | group.id — 한 논리 consumer |
| Assignment | partition 1개는 group 내 최대 1 consumer |
| Rebalance | consumer 추가·제거 시 partition 재할당 |
| Lag | log end offset − consumer offset — 처리 지연 |
topic 6 partitions, group "spark-orders":
C1 ← P0, P1
C2 ← P2, P3
C3 ← P4, P5
group "warehouse-sink" (별도): 같은 topic, 독립 offset
- Parallelism 상한 — consumer 수 ≤ partition 수 (초과 consumer는 idle)
- 다른 group — 독립 offset — warehouse·fraud·debug 동시 소비
- At-least-once — offset commit 전 crash → 재처리 (멱등 sink 필요, 이후 편)
- Rebalance — 처리 중 stop-the-world — cooperative sticky assignor로 완화 (운영)
# code/consumer-group.yaml (발췌)
consumer:
group.id: spark-orders-v2
enable.auto.commit: false # process 후 manual commit
max.poll.records: 500
session.timeout.ms: 45000
monitoring:
alert: consumer_lag > 100000 for 15m
5. Retention · Replay
로그는 retention 정책까지 디스크에 남는다. 삭제가 아니라 만료다.

| 정책 | 설정 | 용도 |
|---|---|---|
| Time retention | retention.ms (예: 7d) |
이벤트 원본 보관 |
| Size retention | retention.bytes |
디스크 상한 |
| Compact | cleanup.policy=compact |
키별 최신 값 (CDC snapshot) |
| Replay | seek(offset) / reset |
backfill·버그 수정 후 재처리 |
Day 1 ──► events append ──► Day 7 retention expires old segments
Bug fix ──► new consumer group OR seek to offset 0 ──► reprocess
- Replay ≠ 무한 — retention 넘으면 데이터 삭제 — 장기 원본은 lake (S3)
- Kappa (3편) — topic retention + replay로 배치 대체 — lake와 병행 흔함
- Compact topic —
user_id → profile최신만 — changelog·CDC 메타 - Tiered storage — 오래된 segment object storage — 비용·retention 연장 (운영)
6. Topic 설계 체크리스트
| # | 질문 |
|---|---|
| 1 | 어떤 key로 순서가 필요한가 |
| 2 | partition 수가 peak throughput·consumer 병렬에 맞는가 |
| 3 | consumer lag SLA·알림이 있는가 |
| 4 | retention이 replay·backfill 요구를 커버하는가 |
| 5 | 장기 원본은 lake로 offload하는가 |
| 6 | 여러 downstream이 별도 group으로 fan-out되는가 |
# code/topic-design.yaml (발췌)
topics:
- name: orders.events
partitions: 12
replication.factor: 3
config:
retention.ms: 604800000 # 7 days
min.insync.replicas: 2
key: order_id # ordering per order
consumers:
- group: spark-streaming-orders
- group: warehouse-connect-sink
정리
- Kafka — ingest 버퍼·decouple·fan-out·replay
- Topic / partition / offset — append-only 로그, 순서는 partition 단위
- Partition key — 필요한 범위만 ordering, hot partition 주의
- Consumer group — partition 분할 소비, lag = 처리 지연
- Retention — replay 창; 장기 보관은 lake로
다음에 다룰 것
- Spark 배치 처리
- RDD·DataFrame, shuffle, partition, action vs transformation
해당 내용은 Kleppmann — Designing Data-Intensive Applications, Confluent Kafka Core concepts 를 기반으로 합니다.
'Data & AI' 카테고리의 다른 글
| Spark Structured Streaming (0) | 2026.07.13 |
|---|---|
| Spark 배치 처리 (0) | 2026.07.09 |
| Batch vs Stream (0) | 2026.07.03 |
| 데이터 규모와 아키텍처 (0) | 2026.07.02 |
| Data · AI 오리엔테이션 (0) | 2026.07.01 |