카프카 마스터 노트 시리즈 2편. Topic이 단순 분류가 아닌 분산 로그인 이유, Partition이 병렬 처리·수평 확장의 핵심인 원리, Offset의 불변성과 컨슈머 진행 추적, Segment 파일 단위 보관과 롤링, 파티셔닝 전략 3종(Key·Round-Robin·Custom), Retention Policy(시간·크기·압축)까지 — Kafka 데이터 구조의 4계층.
이 글은 카프카 마스터 노트 시리즈의 두 번째 편입니다. 1편(기초)에서 Kafka가 분산 로그라는 개념을 잡았다면, 이번엔 그 분산 로그가 어떻게 구성되는가 — Topic·Partition·Offset·Segment 4계층.
Kafka의 모든 성능·확장성·신뢰성이 이 4계층에서 나옵니다. Partition을 모르면 컨슈머 그룹도, 리밸런싱도, 순서 보장도 다 안 잡힙니다.
처음 4계층이 어렵게 느껴지는 이유
처음 이 단원이 어렵게 느껴지는 이유는 두 가지예요. 첫째, Topic·Partition·Segment가 한 번에 등장합니다. 누가 누구를 포함하는지 머리에 안 들어옵니다. 둘째, Offset이 어디 단위인지 헷갈립니다. 토픽 단위? 파티션 단위?
해결법은 한 가지예요. "신문사 + 인쇄기 + 일자별 파일" 비유로 묶는 것. Topic = 신문 종류, Partition = 같은 신문의 인쇄기들, Offset = 그 인쇄기에서 N번째 인쇄물, Segment = 인쇄물을 일자별로 묶은 파일. 4계층이 하나로 보입니다.
Topic — 분산 로그
Kafka Topic = 이벤트의 분류 단위 + 분산 영속 로그.
order-events (Topic)
├── partition 0
├── partition 1
└── partition 2
↓ 각 파티션은 디스크 파일
토픽명 규칙 — 영문·숫자·.·_·- 사용. 길이 ≤ 249자. 권장 — 비즈니스 도메인 명시 (order-events, payment-events).
여기서 시험 함정이 하나 있어요. .과 _는 함께 쓰면 안 됨. Kafka 내부 메트릭 이름 충돌. order.events나 order_events만, 섞지 X.
Partition — 병렬 처리·수평 확장의 핵심
각 파티션 = 순서 있는 불변 로그:
partition 0:
[msg_0] [msg_1] [msg_2] [msg_3] ...
off=0 off=1 off=2 off=3
새 메시지는 끝에 append만. 중간 수정·삭제 X.
파티션이 왜 중요한가
- 병렬 처리 — N 파티션 = N 컨슈머 동시 처리
- 수평 확장 — 파티션을 여러 브로커에 분산
- 순서 보장 — 단일 파티션 안에서만 순서 보장 (전역 순서 X)
여기서 정말 중요한 시험 함정 — Kafka 순서 보장은 파티션 단위. 같은 토픽 안 다른 파티션 사이엔 순서 X. 같은 user_id 메시지의 순서를 보장하려면 같은 파티션으로 가게 해야 (Key 사용).
파티션 수 결정
- 너무 적음 — 컨슈머 병렬도 한계
- 너무 많음 — 파일 핸들·메모리·재할당 비용 ↑
- 권장 — 컨슈머 수 ≥ 파티션 수가 되도록 (조금 여유 있게)
Offset — 파티션 안 위치
각 파티션 안 메시지의 순차 번호 (0부터 시작).
partition 0:
[msg_0] [msg_1] [msg_2] [msg_3]
off=0 off=1 off=2 off=3
특성:
- 불변 — 한 번 부여된 offset은 안 바뀜
- 단조 증가 — 항상 +1 증가
- 파티션 단위 — 같은 토픽이라도 파티션마다 별도
Consumer Offset
컨슈머가 어디까지 읽었는지 추적하는 별도 offset. __consumer_offsets 라는 내부 토픽에 저장.
파티션 latest offset = 100
컨슈머가 읽은 마지막 offset = 80
컨슈머 Lag = 100 - 80 = 20
여기서 시험 함정이 하나 있어요. Lag이 크면 컨슈머가 따라가지 못한다는 신호. 모니터링 핵심 지표.
Segment — 파일 단위 보관
각 파티션은 디스크에 여러 세그먼트 파일로 저장:
/var/lib/kafka/data/order-events-0/
├── 00000000000000000000.log (offset 0~999)
├── 00000000000000001000.log (offset 1000~1999)
├── 00000000000000002000.log (active segment, 쓰기 중)
└── *.index, *.timeindex
새 데이터 = 마지막(active) 세그먼트 끝에 append. 일정 크기 도달 시 새 세그먼트 생성 (rolling).
기본 설정:
log.segment.bytes— 1GB (이 크기 넘으면 롤링)log.segment.ms— 7일 (이 시간 넘으면 롤링)
여기서 정말 중요한 시험 함정 — Segment 단위로 삭제·압축. Retention 만료 시 active 아닌 세그먼트만 통째 삭제. 개별 메시지 삭제 X (DB 아님).
파티셔닝 전략 — 메시지가 어느 파티션으로?
Producer가 메시지 보낼 때 파티션 결정:
1. Key 기반 (가장 일반적)
producer.send(new ProducerRecord<>("order-events", "user-123", orderJson));
partition = hash(key) % numPartitions
같은 key → 같은 파티션 → 순서 보장.
2. Round-Robin (Key 없을 때)
producer.send(new ProducerRecord<>("order-events", null, orderJson));
파티션 사이 균등 분산. 순서 보장 X.
3. Custom Partitioner
Partitioner 인터페이스 구현. 비즈니스 규칙으로 분배.
public class GeoPartitioner implements Partitioner {
public int partition(...) {
// 지역 기반 분배
}
}
여기서 시험 함정이 하나 있어요. Key가 같아도 파티션 수가 바뀌면 매핑 깨짐. hash % numPartitions이라 numPartitions 변경 시 거의 모든 메시지의 위치 변경. 파티션 수는 신중히 결정 + 가급적 변경 X.
Retention Policy — 메시지를 얼마나 보관
1. 시간 기반 (기본)
log.retention.hours = 168 # 7일
7일 지난 세그먼트는 자동 삭제.
2. 크기 기반
log.retention.bytes = 1073741824 # 1GB
파티션이 1GB 넘으면 옛 세그먼트부터 삭제.
3. Log Compaction — Key별 최신만
같은 Key의 옛 메시지는 삭제, 최신만 보존.
Before:
Key=user-1, Value=age:30
Key=user-2, Value=age:25
Key=user-1, Value=age:31 (최신)
Key=user-1, Value=age:32 (최신)
After Compaction:
Key=user-2, Value=age:25
Key=user-1, Value=age:32
용도 — 상태 스냅샷. 사용자 프로필·재고 수량 같은 "최신 상태" 데이터.
여기서 정말 중요한 시험 함정 — Compaction은 시간 기반 retention과 함께 쓸 수 있음. cleanup.policy=compact,delete — 압축 + 시간 만료 둘 다.
토픽별 설정 가능
kafka-configs --bootstrap-server localhost:9092 \
--entity-type topics --entity-name order-events \
--alter --add-config retention.ms=259200000 # 3일
토픽마다 다른 보존 정책 가능.
Replication Factor — 데이터 복제
각 파티션은 여러 브로커에 복제:
파티션 0:
Leader (Broker 1) ← 읽기·쓰기
Follower (Broker 2)
Follower (Broker 3)
Replication Factor = 3
장점 — 한 브로커 다운에도 데이터 안 사라짐. 자세한 건 6편에서.
기본 권장 = 3. 운영 환경 표준.
토픽 만들기 실전
# CLI로 토픽 생성
kafka-topics --bootstrap-server localhost:9092 \
--create \
--topic order-events \
--partitions 6 \
--replication-factor 3 \
--config retention.ms=604800000 # 7일
# 토픽 확인
kafka-topics --bootstrap-server localhost:9092 --describe --topic order-events
# 결과:
# Topic: order-events PartitionCount: 6 ReplicationFactor: 3
# Partition: 0 Leader: 1 Replicas: 1,2,3 Isr: 1,2,3
# Partition: 1 Leader: 2 Replicas: 2,3,1 Isr: 2,3,1
# ...
메시지 구조 (1편 보충)
Record:
Key (선택) ─→ 파티션 결정
Value (필수) ─→ 실제 데이터
Headers (선택) ─→ 메타데이터 (typeId·trace 등)
Timestamp ─→ 자동 부여 또는 명시
Topic
Partition ─→ 자동 결정
Offset ─→ 자동 부여
파티션 수 — 얼마나?
가이드:
- 컨슈머 최대 수 = 파티션 수
- 시작 — 6
12 (소규모), 3050 (중규모) - 절대 100 이하로 시작 (재할당 비용)
- 파티션 수는 늘리기만 가능 (줄이기 X)
여기서 시험 함정이 하나 있어요. 파티션 수 늘리면 Key 매핑 깨짐. 데이터 라우팅 변경. 신중히 결정.
시험 직전 한 번 더 — 자주 헷갈리는 함정 모음
여기까지가 2편의 핵심입니다. 시험 직전 또는 실무에서 헷갈릴 때 다시 펼쳐 볼 수 있게 압축 노트로 마무리할게요.
- 4 계층 — Topic / Partition / Offset / Segment
- Topic = 이벤트 분류 + 분산 영속 로그
- 토픽명 — 영문·숫자·
.·_·-(.과_섞지 X) - Partition = 순서 있는 불변 로그, 새 메시지는 끝에 append만
- 파티션 = 병렬 처리·수평 확장의 핵심
- 순서 보장은 파티션 단위 (전역 X)
- 같은 user 순서 보장 = 같은 Key
- Offset = 파티션 안 메시지 순차 번호 (0부터)
- 불변·단조 증가·파티션 단위
- Consumer Offset =
__consumer_offsets내부 토픽에 - Lag = 최신 - 컨슈머 위치
- Segment = 파일 단위 (1GB 기본)
- Rolling — 크기·시간 도달 시 새 세그먼트
- 삭제·압축은 세그먼트 단위 (개별 메시지 X)
- 파티셔닝 3 전략 — Key (
hash % N) / Round-Robin / Custom - 가장 일반적 = Key 기반
- 파티션 수 변경 시 Key 매핑 깨짐
- Retention — 시간(
hours) / 크기(bytes) / Compaction - Compaction = Key별 최신만 보존 (상태 스냅샷)
cleanup.policy=compact,delete결합 가능- Replication Factor 권장 = 3 (운영 표준)
- 파티션 수 — 늘리기만 가능, 줄이기 X
- 시작 6
12, 중규모 3050
시리즈 다른 편
- 1편 — EDA·Kafka 기초·KRaft
- 2편 — Topic·Partition·Offset (현재 글)
- 3편 — Producer·Consumer 동작
- 4편 — Consumer Group·리밸런싱
- 5편 — Reactor Kafka
- 6편 — Cluster·HA·Best Practices
- 7편 — 배치·에러·트랜잭션
- 8편 — Spring Kafka·테스트·보안
- 9편 — Spring Cloud Stream 기초
- 10편 — StreamBridge 동적 라우팅
- 11편 — Fan-Out / Fan-In
- 12편 — SCS Tips & Tricks
- 13편 — Saga 코레오그래피
- 14편 — Saga 오케스트레이터
- 15편 — Transactional Outbox
공식 문서: Kafka Topic Configurations 에서 더 깊이.
다음 글(3편)에서는 Producer·Consumer 동작 원리 — acks·직렬화·auto.offset.reset까지 풀어 갑니다.