"메시지를 발행하는 것과, Kafka가 어떻게 순서와 내구성을 보장하는지 아는 것은 다르다"
"
@KafkaListener붙이면 메시지를 받겠지 — 와 — 리밸런싱이 왜 발생하고 처리 중 리밸런싱이 데이터 중복을 일으키는지,acks=all이 실제로 무엇을 보장하는지 아는 것의 차이를 만드는 레포"
파티션이 왜 병렬성의 기본 단위인지, 로그 세그먼트 .log/.index/.timeindex 파일이 순차 I/O로 빠른 이유, ISR이 이탈할 때 min.insync.replicas가 가용성과 내구성을 어떻게 트레이드오프하는지, Exactly-Once가 Producer ID + Sequence Number + Two-Phase Commit으로 어떻게 실제 구현되는지까지
왜 이렇게 설계됐는가 라는 질문으로 Kafka 내부를 끝까지 파헤칩니다
Kafka에 관한 자료는 넘쳐납니다. 하지만 대부분은 "어떻게 쓰나" 에서 멈춥니다.
| 일반 자료 | 이 레포 |
|---|---|
"@KafkaListener를 붙이면 메시지를 받습니다" |
ContainerFactory → KafkaMessageListenerContainer → Fetch 루프 → Offset 커밋 전 과정, max.poll.records가 리밸런싱 위험에 미치는 영향 |
"acks=all로 설정하면 안전합니다" |
acks=all이어도 ISR이 1개뿐이면 단일 브로커 장애로 데이터 유실이 발생하는 이유, min.insync.replicas와의 조합이 왜 필수인가 |
| "파티션을 늘리면 처리량이 증가합니다" | 파티션 수가 Consumer 스레드 수와 어떻게 연결되는지, Leader 선출 비용과 Fetch 병렬성 트레이드오프 |
| "Exactly-Once를 사용하세요" | enable.idempotence=true의 Producer ID + Sequence Number 중복 제거 원리, transactional.id로 멀티 파티션 Two-Phase Commit이 동작하는 방식 |
| "리밸런싱이 발생하면 잠깐 멈춥니다" | Eager 리밸런싱의 Stop-The-World 구간, max.poll.interval.ms 초과로 그룹에서 제거될 때 미커밋 오프셋이 재처리되는 이유, Cooperative 리밸런싱이 이를 개선하는 방식 |
| "Consumer Lag을 모니터링하세요" | Lag이 쌓이는 원인 트리(파티션 수 부족 / 처리 로직 병목 / 리밸런싱 반복 / DB 왕복), kafka-consumer-groups로 진단하는 방법 |
| 이론 나열 | 실행 가능한 kafka-console-producer/consumer, kafka-topics, kafka-consumer-groups, kafka-log-dirs CLI 실험 + 3-브로커 Docker Compose 환경 + Spring 연결 |
각 챕터의 첫 문서부터 바로 학습을 시작하세요!
💡 각 섹션을 클릭하면 상세 문서 목록이 펼쳐집니다
핵심 질문: Kafka는 왜 메시지 큐가 아닌 분산 로그인가? 파티션이 왜 병렬성의 기본 단위이고, 브로커는 어떻게 순차 I/O로 고처리량을 달성하며, Producer와 Consumer는 내부에서 어떻게 동작하는가?
Kafka 설계 철학부터 KRaft 아키텍처까지 (6개 문서)
| 문서 | 다루는 내용 |
|---|---|
| 01. Kafka 설계 철학 — 왜 메시지 큐가 아닌 분산 로그인가 | RabbitMQ 등 전통적 MQ와 Kafka의 근본적 차이(Push vs Pull, 메시지 삭제 시점, Consumer 독립성), 분산 커밋 로그(Distributed Commit Log)로 설계된 이유, 토픽을 여러 Consumer Group이 독립적으로 소비할 수 있는 원리 |
| 02. 토픽과 파티션 — 병렬성의 기본 단위 | 파티션이 병렬 처리의 단위인 이유(Consumer 1개 = 파티션 1개 할당), 파티션 수가 처리량과 지연에 미치는 영향(Leader 선출 비용, Fetch 병렬성), 파티션 키로 메시지 순서를 보장하는 방식과 한계, 파티션 수 선택 기준 |
| 03. 브로커 내부 구조 — 로그 세그먼트와 순차 I/O | .log / .index / .timeindex 파일 구조와 각각의 역할, 순차 I/O(Sequential Write)가 랜덤 I/O보다 빠른 이유(디스크 헤드 이동 최소화), 페이지 캐시(Page Cache)를 적극 활용하는 설계의 의도, kafka-log-dirs로 세그먼트 실제 확인 |
| 04. Producer 내부 동작 — RecordAccumulator와 배치 전략 | RecordAccumulator가 메시지를 파티션별로 배치로 묶는 구조, linger.ms(대기 시간)와 batch.size(최대 배치 크기)의 상호작용, 파티션 선택 전략(Key 해시 / Round-Robin / Sticky Partitioner) 비교, buffer.memory 초과 시 블로킹 동작 |
| 05. Consumer 내부 동작 — Fetch 루프와 폴링 간격 | Kafka가 Push 대신 Pull(Fetch) 방식을 택한 이유(Consumer 속도 독립성), max.poll.records로 한 번에 가져오는 레코드 수 제어, max.poll.interval.ms 초과가 리밸런싱을 유발하는 메커니즘, fetch.min.bytes / fetch.max.wait.ms로 Fetch 효율 조정 |
| 06. ZooKeeper vs KRaft — 메타데이터 관리의 진화 | ZooKeeper가 Kafka 메타데이터(브로커 등록, 파티션 리더, 컨트롤러 선출)를 관리해온 방식, ZooKeeper 의존성의 운영 복잡성 문제, KRaft(Kafka Raft)에서 Controller 쿼럼이 메타데이터를 직접 관리하는 구조, 마이그레이션 고려사항 |
핵심 질문: ISR은 어떤 조건에서 이탈하고,
acks=all이어도 데이터가 유실될 수 있는 조건은 무엇이며,min.insync.replicas는 가용성과 내구성을 어떻게 트레이드오프하는가?
파티션 복제부터 Log Compaction까지 (6개 문서)
| 문서 | 다루는 내용 |
|---|---|
| 01. 파티션 복제 — Leader/Follower 구조 | Leader 파티션이 모든 읽기/쓰기를 처리하는 이유, Follower가 Leader를 Fetch 방식으로 따라가는 과정(Pull 기반 복제), High Watermark(HW)와 Log End Offset(LEO)의 차이, kafka-topics --describe로 복제 상태 확인 |
| 02. ISR(In-Sync Replicas) — 조건과 이탈/복귀 | ISR 멤버 조건(replica.lag.time.max.ms 내에 Leader LEO 따라잡기), Follower가 느려질 때 ISR에서 이탈하는 과정, ISR 이탈이 acks=all의 동작에 미치는 영향, ISR 복귀 조건과 복귀 중 Fetch 동기화 과정 |
| 03. acks 설정 완전 분해 — 보장하는 것과 보장하지 않는 것 | acks=0(응답 안 기다림) / acks=1(Leader만) / acks=all(ISR 전체) 각각이 실제로 보장하는 범위, acks=all이어도 ISR=1이면 단일 브로커 장애로 유실 가능한 이유, 설정별 처리량과 내구성 트레이드오프 수치 비교 |
| 04. min.insync.replicas — 가용성과 내구성의 트레이드오프 | min.insync.replicas가 쓰기 요청을 거부하는 조건(NotEnoughReplicasException), replication.factor=3, min.insync.replicas=2, acks=all 황금 조합의 근거, 브로커 장애 시 시나리오별 가용성 vs 내구성 계산 |
| 05. Leader Election — 파티션 리더 선출과 Unclean Election | 파티션 리더 브로커 장애 시 새 리더 선출 과정(Controller가 ISR에서 선택), Unclean Leader Election(unclean.leader.election.enable=true)이 데이터 유실을 감수하는 이유, Preferred Leader Election으로 리더 분산을 재조정하는 방법 |
| 06. Log Compaction — 키 기반 최신값 보존 | Compaction 대상 세그먼트(clean 영역 vs dirty 영역) 선택 기준, 키별 최신 값만 남기는 Compaction 과정, __consumer_offsets 토픽이 Log Compaction으로 운영되는 이유, Consumer가 Compacted 토픽을 읽을 때의 동작 |
핵심 질문: At-Least-Once와 Exactly-Once는 실제로 어떻게 구현이 다른가? Producer 멱등성은 내부에서 어떻게 중복을 제거하고, Consumer Offset 커밋 타이밍이 왜 중복/유실을 결정하는가?
전달 보장 3단계부터 EOS 전 과정까지 (5개 문서)
| 문서 | 다루는 내용 |
|---|---|
| 01. 전달 보장 3단계 — At-Most-Once / At-Least-Once / Exactly-Once | 각 보장 단계의 구현 방법(At-Most-Once: 커밋 후 처리 / At-Least-Once: 처리 후 커밋 / Exactly-Once: 멱등성 + 트랜잭션), 실무에서 At-Least-Once가 기본인 이유, Exactly-Once가 필요한 시나리오와 비용 |
| 02. Producer 멱등성 — Producer ID와 Sequence Number | enable.idempotence=true 활성화 시 브로커가 (ProducerID, PartitionID, SequenceNumber) 조합으로 중복 메시지를 감지하는 원리, Producer 재시도(retries)로 인한 중복이 멱등성으로 제거되는 방식, 멱등성이 보장하는 범위(단일 파티션, 단일 세션) |
| 03. 트랜잭션 Producer — 멀티 파티션 원자적 쓰기 | transactional.id와 Transaction Coordinator의 역할, Two-Phase Commit으로 여러 파티션에 원자적으로 쓰는 과정(beginTransaction → send → commitTransaction), Epoch 번호로 좀비 Producer를 차단하는 메커니즘 |
| 04. Consumer Offset 관리 — 자동 커밋 vs 수동 커밋 | enable.auto.commit=true의 자동 커밋이 중복/유실을 발생시키는 시나리오, commitSync(처리 완료 후 블로킹) vs commitAsync(비블로킹, 콜백)의 차이, __consumer_offsets 토픽에 오프셋이 저장되는 구조, 수동 커밋 패턴 구현 |
| 05. Exactly-Once Semantics 전 과정 — Read-Process-Write | isolation.level=read_committed로 Consumer가 커밋된 메시지만 읽는 방식, Read-Process-Write 패턴에서 EOS 달성 전체 흐름, Kafka Streams의 processing.guarantee=exactly_once_v2와 일반 Producer/Consumer EOS의 차이 |
핵심 질문: 리밸런싱은 정확히 무엇이 방아쇠를 당기고, Stop-The-World가 발생하는 구간은 어디이며, 처리 중 리밸런싱이 왜 중복 메시지를 만드는가?
Consumer Group 상태 머신부터 Consumer Lag까지 (5개 문서)
| 문서 | 다루는 내용 |
|---|---|
| 01. Consumer Group 내부 동작 — Group Coordinator와 상태 머신 | Group Coordinator 브로커가 Consumer Group의 멤버십과 오프셋을 관리하는 방식, Consumer Group 상태 머신(Empty → PreparingRebalance → CompletingRebalance → Stable), Group Leader Consumer가 파티션 할당을 결정하는 역할 분리 |
| 02. 리밸런싱 발생 조건 — heartbeat와 poll 간격 | heartbeat.interval.ms(생존 신호)와 session.timeout.ms(장애 판단 임계값)의 차이, max.poll.interval.ms 초과(처리 지연)가 그룹 이탈을 유발하는 과정, Consumer 추가/제거 외에 리밸런싱을 유발하는 숨겨진 원인들 |
| 03. 리밸런싱 전략 비교 — Eager vs Cooperative | Eager(기존) 리밸런싱의 Stop-The-World: 모든 Consumer가 파티션을 반납하고 재할당받는 과정, Cooperative(Incremental) 리밸런싱이 이동이 필요한 파티션만 재할당하는 방식, partition.assignment.strategy=CooperativeStickyAssignor 적용 방법 |
| 04. 리밸런싱 중 중복 처리 — 멱등 처리 패턴 | 처리 완료 후 Offset 커밋 전 리밸런싱이 발생하면 다음 Consumer가 동일 메시지를 재처리하는 과정, ConsumerRebalanceListener.onPartitionsRevoked()에서 수동 커밋으로 중복을 최소화하는 패턴, DB 멱등 키(Idempotent Key)로 중복 처리를 무해하게 만드는 설계 |
| 05. Consumer Lag — 원인 분석과 처리 속도 향상 | Consumer Lag이 쌓이는 원인 트리(파티션 수 < Consumer 수 → 유휴 Consumer 발생 / 처리 로직 병목 / DB 왕복 / 반복 리밸런싱), kafka-consumer-groups --describe로 파티션별 Lag 진단, 병렬 처리와 배치 처리로 소비 속도 높이는 전략 |
핵심 질문: 처리량을 높이려면 Producer와 Consumer의 어느 설정을 건드려야 하고, 파티션 핫스팟은 어떻게 진단하며, 운영 중 Under-Replicated Partitions가 발생하면 어떻게 대응하는가?
Producer/Consumer 튜닝부터 운영 장애 패턴까지 (5개 문서)
| 문서 | 다루는 내용 |
|---|---|
| 01. Producer 처리량 최적화 — 배치와 압축 | batch.size(배치 최대 크기)와 linger.ms(전송 대기 시간) 조합으로 처리량을 높이는 원리, buffer.memory 설정이 배압(Backpressure)에 미치는 영향, 압축 방식(gzip / snappy / lz4 / zstd) 별 CPU 비용 vs 네트워크 절감 비교 |
| 02. Consumer 처리량 최적화 — Fetch 튜닝 | fetch.min.bytes(최소 Fetch 크기)와 fetch.max.wait.ms(최대 대기 시간)로 소량 Fetch 왕복을 줄이는 방법, max.partition.fetch.bytes(파티션당 최대 Fetch)로 메모리 사용 제어, Consumer 병렬화 전략(스레드 풀 vs Consumer 인스턴스 확장) |
| 03. 파티션 핫스팟 — 원인과 Custom Partitioner | 키 기반 파티셔닝에서 특정 키에 메시지가 집중될 때 단일 파티션 브로커 CPU/디스크 병목이 발생하는 원인, kafka-log-dirs로 파티션별 크기 불균형 진단, Custom Partitioner로 핫스팟 키를 여러 파티션에 분산하는 구현 |
| 04. Kafka 모니터링 — JMX 메트릭과 Grafana 대시보드 | 핵심 JMX 메트릭(UnderReplicatedPartitions, BytesInPerSec, RequestHandlerAvgIdlePercent, OfflinePartitionsCount), kafka-exporter + Prometheus + Grafana로 대시보드 구성하는 방법, Consumer Lag 알림 설정 |
| 05. 운영 중 발생하는 문제 패턴 — 진단과 대응 | Under-Replicated Partitions 원인 분석 및 복구 절차, Consumer Group 무한 리밸런싱 진단(kafka-consumer-groups --describe의 State 확인), 오프셋 리셋 절차(--reset-offsets --to-earliest/--to-datetime)와 재처리 시 주의사항 |
핵심 질문: Kafka Streams의 상태 저장소는 어떻게 장애에도 복구되고, KTable과 KStream은 개념적으로 어떻게 다르며, Outbox Pattern은 DB 트랜잭션과 Kafka 발행의 원자성 문제를 어떻게 해결하는가?
Kafka Streams 아키텍처부터 Outbox Pattern까지 (5개 문서)
| 문서 | 다루는 내용 |
|---|---|
| 01. Kafka Streams 아키텍처 — Topology와 상태 저장소 | Topology(처리 그래프)와 StreamTask(파티션별 처리 단위)의 관계, RocksDB 기반 로컬 상태 저장소가 Changelog 토픽으로 내구성을 보장하는 원리, Task 재배치 시 RocksDB 상태를 Changelog에서 복원하는 과정 |
| 02. KTable과 KStream — 스냅샷 vs 이벤트 스트림 | KStream(이벤트의 무한 흐름)과 KTable(키별 최신 상태의 스냅샷) 개념 차이, KStream-KTable 조인이 동작하는 원리(이벤트 발생 시 현재 KTable 상태와 결합), GlobalKTable이 모든 파티션을 로컬에서 유지하는 방식과 용도 |
| 03. 윈도우 연산 — 시간 기반 집계와 지연 이벤트 | Tumbling(겹치지 않는 고정 창) / Hopping(겹치는 고정 창) / Sliding(이벤트 간격 기반) / Session(비활동 간격 기반) Window 차이, Event Time vs Processing Time 기준 선택 이유, grace period로 늦게 도착하는 이벤트(Late Arrival)를 수용하는 방법 |
| 04. Exactly-Once in Kafka Streams — processing.guarantee | processing.guarantee=exactly_once_v2(EOS-V2)가 내부적으로 트랜잭션 Producer와 read_committed Consumer를 자동 구성하는 방식, EOS-V1 대비 EOS-V2가 브로커 트랜잭션 코디네이터 부하를 줄인 개선 내용, EOS 활성화 시 성능 비용 |
| 05. 이벤트 기반 아키텍처 패턴 — Outbox Pattern | DB 트랜잭션 커밋 후 Kafka 발행 사이에 장애가 발생할 때의 원자성 문제, Outbox Pattern(DB 트랜잭션 내 Outbox 테이블 기록 → Debezium CDC로 Kafka 발행)으로 원자성을 보장하는 구조, Saga Pattern과 Choreography vs Orchestration 비교 |
핵심 질문:
@KafkaListener는 내부에서 어떻게 동작하고, Spring Kafka의 에러 처리는 어떻게 구성하며,@Transactional과 Kafka 트랜잭션은 어떻게 경계를 나누는가?
Spring Kafka 기초부터 Spring Batch + Kafka까지 (5개 문서)
| 문서 | 다루는 내용 |
|---|---|
| 01. Spring Kafka 기초 — KafkaTemplate과 @KafkaListener 내부 | KafkaTemplate.send()가 ProducerFactory → KafkaProducer로 연결되는 과정, @KafkaListener가 ConcurrentKafkaListenerContainerFactory를 통해 KafkaMessageListenerContainer를 생성하는 방식, ContainerProperties로 Offset 커밋 모드(BATCH, RECORD, MANUAL) 설정 |
| 02. 에러 처리와 재시도 — DefaultErrorHandler와 DLT | DefaultErrorHandler(Spring Kafka 2.8+)가 BackOff 정책으로 재시도하고 소진 시 Dead Letter Topic(DLT)으로 라우팅하는 구조, SeekToCurrentErrorHandler의 동작과 차이, DLT 메시지 재처리 전략(수동 재처리 vs 자동 재시도) |
| 03. 트랜잭션 처리 — @Transactional과 KafkaTransactionManager | DB 트랜잭션(@Transactional)과 Kafka 트랜잭션(KafkaTransactionManager)의 경계가 다른 이유, ChainedKafkaTransactionManager로 DB + Kafka 트랜잭션을 묶는 Best Effort 방식의 한계, 트랜잭션 Kafka Producer 설정과 transactional.id 관리 |
| 04. Spring Cloud Stream — 함수형 모델과 Kafka Binder | Spring Cloud Stream의 함수형 프로그래밍 모델(Function<Flux<Message>, Flux<Message>>), Kafka Binder가 내부적으로 KafkaMessageListenerContainer와 KafkaTemplate을 구성하는 방식, 바인더 추상화로 RabbitMQ → Kafka 교체 시 코드 변경 범위 |
| 05. Spring Batch + Kafka — Chunk 처리와 Exactly-Once | KafkaItemReader가 Consumer Offset을 JobExecutionContext에 저장하는 방식(Kafka Offset 커밋과 Batch Checkpoint 동기화), KafkaItemWriter의 트랜잭션 처리, Chunk 단위 Exactly-Once 달성을 위한 KafkaTransactionManager + JpaTransactionManager 조합 전략 |
# ⚠️ 이 docker-compose.yml은 ZooKeeper 모드입니다 (Kafka 3.x 기준)
# KRaft 아키텍처 학습(Ch6-06)은 문서 내 kafka-storage 실험을 참고하세요
# 3-브로커 클러스터 + Schema Registry + Kafka UI 시작
docker compose up -d
# 브로커 상태 확인 (컨테이너 내부 포트 9092 사용)
docker exec kafka-1 kafka-broker-api-versions --bootstrap-server localhost:9092
# Kafka UI 접속
open http://localhost:8080# 토픽 생성 (복제 팩터 3, 파티션 3)
kafka-topics --bootstrap-server localhost:19092 \
--create --topic orders \
--replication-factor 3 \
--partitions 3
# ISR 상태 확인 (Leader, Replicas, ISR 구분)
kafka-topics --bootstrap-server localhost:19092 \
--describe --topic orders
# 메시지 발행 (Key 지정)
kafka-console-producer --bootstrap-server localhost:19092 \
--topic orders \
--property "key.separator=:" \
--property "parse.key=true"
# 메시지 소비 (처음부터)
kafka-console-consumer --bootstrap-server localhost:19092 \
--topic orders \
--from-beginning \
--property print.key=true \
--group order-consumer-group
# Consumer Group Lag 확인
kafka-consumer-groups --bootstrap-server localhost:19092 \
--describe --group order-consumer-group
# 오프셋 리셋 (재처리)
kafka-consumer-groups --bootstrap-server localhost:19092 \
--group order-consumer-group \
--topic orders \
--reset-offsets --to-earliest --execute
# 로그 세그먼트 파일 상세 확인
kafka-log-dirs --bootstrap-server localhost:19092 \
--topic-list orders \
--describe
# 브로커별 파티션 리더 분포 확인
kafka-topics --bootstrap-server localhost:19092 \
--describe --topic orders | grep Leaderspring:
kafka:
bootstrap-servers: localhost:19092,localhost:29092,localhost:39092
producer:
acks: all # ISR 전체 확인
batch-size: 16384 # 16 KB (처리량 우선이면 65536)
linger-ms: 5 # 5ms 대기 (처리량 우선이면 20~100)
buffer-memory: 33554432 # 32 MB
key-serializer: org.apache.kafka.common.serialization.StringSerializer
value-serializer: org.apache.kafka.common.serialization.StringSerializer
properties:
enable.idempotence: true # 멱등성 활성화 → retries=MAX, acks=all 자동 설정
max.in.flight.requests.per.connection: 5
consumer:
group-id: my-consumer-group
auto-offset-reset: earliest
enable-auto-commit: false # 수동 커밋
max-poll-records: 500
key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
value-deserializer: org.apache.kafka.common.serialization.StringDeserializer
properties:
max.poll.interval.ms: 300000 # 5분 처리 허용
session.timeout.ms: 10000
heartbeat.interval.ms: 3000
isolation.level: read_committed # EOS 소비
listener:
ack-mode: MANUAL_IMMEDIATE # 수동 커밋 모드
concurrency: 3 # Consumer 스레드 수 (≤ 파티션 수)모든 문서는 동일한 구조로 작성됩니다.
| 섹션 | 설명 |
|---|---|
| 🎯 핵심 질문 | 이 문서를 읽고 나면 답할 수 있는 질문 |
| 🔍 왜 이 개념이 중요한가 | 실무에서 마주치는 문제 상황과 이 개념의 연결 |
| 😱 흔한 실수 | Before — Kafka를 블랙박스로 두는 접근과 그 결과 |
| ✨ 올바른 접근 | After — 원리를 알고 난 후의 설계/운영 |
| 🔬 내부 동작 원리 | 브로커/클라이언트 내부 분석 + ASCII 구조도 |
| 💻 실전 실험 | kafka-console-producer/consumer, kafka-topics, kafka-consumer-groups, kafka-log-dirs |
| 📊 성능/비용 비교 | acks 설정별 처리량, 압축 방식별 비교, 리밸런싱 전략별 다운타임 등 |
| ⚖️ 트레이드오프 | 이 설계의 장단점, 언제 다른 접근을 택할 것인가 |
| 📌 핵심 정리 | 한 화면 요약 |
| 🤔 생각해볼 문제 | 개념을 더 깊이 이해하기 위한 질문 + 해설 |
🟢 "acks=all과 min.insync.replicas의 차이를 모른다" — 내구성 긴급 투입 (3일)
Day 1 Ch2-02 ISR 조건과 이탈/복귀 → ISR이 무엇인지 이해
Ch2-03 acks 완전 분해 → acks=all이 보장하는 것과 보장하지 않는 것
Day 2 Ch2-04 min.insync.replicas → 가용성 vs 내구성 트레이드오프
Ch2-05 Leader Election → Unclean Election 데이터 유실 시나리오
Day 3 Ch3-01 전달 보장 3단계 → At-Least-Once가 기본인 이유
Ch3-02 Producer 멱등성 → 중복 제거 원리
🟡 "리밸런싱이 왜 발생하는지, 중복 처리를 어떻게 막는지 모른다" — Consumer 집중 (1주)
Day 1 Ch1-05 Consumer 내부 동작 → Fetch 루프, max.poll 설정
Day 2 Ch4-01 Consumer Group 상태 머신 → Group Coordinator 역할
Ch4-02 리밸런싱 발생 조건 → heartbeat vs poll 간격
Day 3 Ch4-03 리밸런싱 전략 → Eager Stop-The-World vs Cooperative
Ch4-04 리밸런싱 중 중복 → 멱등 처리 패턴
Day 4 Ch3-01 전달 보장 3단계 → At-Least-Once 이해
Ch3-04 Consumer Offset 관리 → 수동 커밋 패턴
Day 5 Ch3-05 EOS 전 과정 → Exactly-Once 달성 방법
Day 6 Ch4-05 Consumer Lag → 원인 진단 트리
Day 7 Ch7-01 Spring Kafka 기초 → @KafkaListener 내부 동작
Ch7-02 에러 처리와 DLT → 실패 메시지 처리 전략
🔴 "Kafka 내부를 브로커 파일 수준까지 완전히 정복한다" — 전체 정복 (7주)
1주차 Chapter 1 전체 — Kafka 핵심 아키텍처
→ docker exec kafka-1로 .log/.index/.timeindex 파일 직접 확인
→ kafka-dump-log로 세그먼트 파일 내용 분석
2주차 Chapter 2 전체 — 복제와 내구성 보장
→ kafka-3 컨테이너 강제 종료 → ISR 이탈 관찰
→ acks 설정별 처리량 측정 실험
3주차 Chapter 3 전체 — 전달 보장과 멱등성
→ 네트워크 지연 주입으로 중복 메시지 재현
→ 트랜잭션 Producer로 멀티 파티션 원자적 쓰기 실험
4주차 Chapter 4 전체 — Consumer Group과 리밸런싱
→ max.poll.interval.ms 초과로 리밸런싱 의도적 유발
→ Cooperative vs Eager 리밸런싱 다운타임 측정 비교
5주차 Chapter 5 전체 — 성능 튜닝과 모니터링
→ Grafana 대시보드 구성 → Consumer Lag 알림 설정
→ 핫스팟 파티션 재현 → Custom Partitioner 구현
6주차 Chapter 6 전체 — Kafka Streams와 이벤트 기반 설계
→ Topology 직접 구성 → RocksDB 상태 저장소 장애 복구 실험
→ Outbox Pattern + Debezium CDC 연동
7주차 Chapter 7 전체 — Spring과 Kafka 통합
→ DLT 라우팅 흐름 디버깅 → KafkaTransactionManager 설정
→ Spring Batch + Kafka Exactly-Once 검증
| 레포 | 주요 내용 | 연관 챕터 |
|---|---|---|
| spring-batch-deep-dive | Chunk 처리, Step 설계, ItemReader/Writer | Ch7-05(Kafka ItemReader/Writer + Exactly-Once) |
| spring-cloud-deep-dive | 이벤트 기반 마이크로서비스, Spring Cloud Stream | Ch6-05(Outbox Pattern), Ch7-04(Spring Cloud Stream Binder) |
| redis-deep-dive | Redis Stream, Pub/Sub vs Stream 비교 | Ch1-01(분산 로그 vs 메시지 큐 — Redis Stream과 비교) |
| spring-data-transaction | @Transactional 내부, 트랜잭션 경계 | Ch7-03(DB 트랜잭션과 Kafka 트랜잭션 경계) |
| mysql-deep-dive | InnoDB 트랜잭션, CDC 기반 Outbox | Ch6-05(Outbox Pattern — DB 트랜잭션과 Kafka 발행 원자성) |
💡 이 레포는 Kafka 내부 동작에 집중합니다. Spring을 모르더라도 Chapter 1~6을 순수 Kafka 관점으로 학습할 수 있습니다. Chapter 7은 Spring Kafka 실무 경험이 있을 때 더 깊이 연결됩니다.
- Kafka 공식 문서
- Kafka: The Definitive Guide, 2nd Edition — Neha Narkhede, Gwen Shapira, Todd Palino
- Designing Data-Intensive Applications — Martin Kleppmann — Kafka 관련 챕터
- Confluent Blog
- Kafka Improvement Proposals (KIPs)
- Spring Kafka Reference Documentation
⭐️ 도움이 되셨다면 Star를 눌러주세요!
Made with ❤️ by Dev Book Lab
"메시지를 발행하는 것과, Kafka가 어떻게 순서와 내구성을 보장하는지 아는 것은 다르다"