topic과 partition — 메시지를 어디에, 어떻게 쌓을 것인가
쇼핑몰에서 주문이 들어온다. 이 주문 데이터를 어디에 쌓을까? Kafka에선 topic이라는 곳에 넣는다. "주문" topic, "클릭 로그" topic, "결제" topic처럼 용도별로 topic을 나눠 메시지를 분류한다. 그런데 주문이 초당 수만 건씩 쏟아지면 topic 하나로는 감당이 안 된다 — 그래서 topic을 여러 조각으로 쪼개는데, 그 조각이 partition이다.
이 글은 Kafka를 처음 접하는 사람이 topic과 partition을 "확" 이해하도록, 기초부터 차근차근 쌓아 올린다. topic이 뭔지, 어떻게 쓰이는지부터 시작해 partition이 왜 필요한지, key가 뭔 역할을 하는지, 그리고 "partition 수를 나중에 바꾸면 생기는 일"까지. 어려운 구현 디테일(murmur2 해시 등)은 마지막 심화로 미뤘다.
topic이란 — 메시지를 담는 이름 붙인 상자
topic은 가장 단순하게 말하면 "메시지를 담는, 이름 붙인 상자(또는 카테고리)"다. 데이터베이스의 테이블이나 파일시스템의 폴더와 비슷하다 — 용도별로 나눠 놓는 구분선.
| 비유 | topic에 대응 |
|---|---|
| 우체국의 수신처란(주문/청구/문의) | topic 이름(orders / clicks / payments) |
| 회사의 사서함 번호 | topic 이름 — 메시지가 여기로 배달됨 |
| 데이터베이스의 테이블 | topic — 한 종류의 데이터를 담는 그릇 |
topic의 구체 예
실제 서비스에선 이렇게 topic을 나눈다:
orders— 주문 이벤트 (주문 생성, 취소, 상태 변경)user-clicks— 사용자 클릭 로그 (UX 분석용)payments— 결제 요청/완료shipping-events— 배송 상태__consumer_offsets— (내부용) consumer가 어디까지 읽었나
topic 이름은 용도를 드러내게 짓는 게 관례다. t1, test 같은 이름은 운영에서 혼란을 만든다.
topic의 핵심 성질 (장 01 복습)
topic은 로그(log)다 — 메시지가 들어온 순서대로 계속 append되고, consumer가 읽어도 지워지지 않는다(retention 정책이 닿기 전까지). 그래서 여러 consumer가 같은 topic을 각자 따로 읽을 수 있고, 나중에 다시 읽을(replay) 수도 있다. 이게 일반 "큐"(소비하면 사라짐)와 topic의 결정적 차이다.
topic은 어떻게 쓰이나 — worked scenario
추상 정의만으론 감이 안 온다. 쇼핑몰의 orders topic이 실제로 어떻게 쓰이는지 보자.
flowchart LR
P1[주문 API 서버] -->|주문 이벤트 produce| T["orders topic"]
P2[모바일 앱] -->|주문 이벤트 produce| T
T -->|consume| C1[주문 처리 서비스<br/>재고 차감·결제]
T -->|consume| C2[분석 파이프라인<br/>일별 매출 집계]
T -->|consume| C3[알림 서비스<br/>주문 확인 이메일]
이 시나리오가 topic의 가치를 보여준다:
- 여러 곳에서 쓴다(multi-producer): 주문 API 서버와 모바일 앱이 모두
orderstopic에 주문 이벤트를 쓴다. 어디서 왔든 같은 topic에 쌓인다. - 여러 곳에서 읽는다(multi-consumer): 주문 처리 서비스(재고 차감), 분석 파이프라인(매출 집계), 알림 서비스(이메일)가 각자 독립적으로 같은
orderstopic을 읽는다. 한 consumer가 느려져도 다른 consumer는 영향 안 받는다. - 나중에 다시 읽을 수 있다(replay): 분석에서 버그를 발견하면, 지난주 주문 데이터를
orderstopic에서 다시 읽어 재처리할 수 있다(메시지가 남아있으므로).
이게 topic을 단순 "메시지 큐"가 아니라 이벤트의 영속 저장소로 쓰는 방식이다. 서비스들이 topic을 사이에 두고 느슨하게 연결되는 구조(이벤트 기반 아키텍처)의 기반이 된다.
topic을 만들 때:
bin/kafka-topics.sh --create --topic orders --partitions 3 --replication-factor 1 --bootstrap-server localhost:9092. 여기서--partitions 3이 바로 다음에 볼 partition이다.
topic 하나로 충분한가 — 왜 partition이 필요한가
주문이 초당 10건이면 topic 하나(partition 1개)로 충분하다. 하지만 주문이 초당 수만 건으로 늘면? topic을 하나의 줄(로그)로만 처리하면:
- 쓰기가 한 곳에 몰림 — 한 broker만 바쁘고, 다른 broker는 한가함.
- 읽기도 한 줄 — consumer를 여럿 띄워도 한 줄을 순서대로 읽어야 하니 병렬 처리가 안 됨.
해법: topic을 여러 조각(partition)으로 쪼개 각 조각을 다른 broker에 두고, consumer도 각 조각을 나눠 읽는다. 이게 partition의 탄생 동기다. "topic은 논리적 분류, partition은 물리적 분할"이라고 한 줄로 요약할 수 있다.
partition이란 — topic을 쪼갠 물리적 조각
partition은 topic을 물리적으로 나눈 조각이다. topic orders에 partition 3개를 두면:
- partition 0, partition 1, partition 2 — 각각 독립된 append-only 로그.
- 메시지는 (key 유무에 따라) 어느 partition으로 갈지 정해져 들어간다.
- 각 partition은 (복제본 제외) 단일 broker에 저장되어 부하가 분산된다.
flowchart TB
T["topic: orders<br/>(논리적 분류)"] --> P0["partition 0<br/>(물리적 로그)"]
T --> P1["partition 1"]
T --> P2["partition 2"]
P0 --> B0["broker 0"]
P1 --> B1["broker 1"]
P2 --> B2["broker 2"]
partition은 세 가지의 최소 단위다
partition은 Kafka에서 세 가지 의미의 최소 단위를 동시에 뜻한다:
- 데이터 분할 단위 — 각 partition은 단일 broker에 저장. topic 전체 데이터가 partition 수만큼 분산.
- 병렬성 단위 — consumer group 내에서 partition 1개당 최대 consumer 1개가 병렬 소비. 즉 partition 수 = consumer 병렬성의 상한.
- 순서 보장 단위 — partition 내에서만 쓴 순서대로 읽힌다. topic 전체 순서 보장은 없다.
(basic-kafka-operations.md): partition 수가 "데이터를 몇 개 로그로 샤딩할지"와 "consumer 최대 병렬성"을 결정한다.
이 셋이 한 몸이라는 게 핵심이다. 병렬성을 늘리려 partition을 늘리면 순서 보장 범위가 쪼개진다 — partition 수 선택은 병렬성 vs 순서 보장 범위의 트레이드오프다.
key가 partition을 고정한다 — 같은 key는 같은 partition으로
producer가 메시지에 key를 주면, 같은 key는 항상 같은 partition으로 간다. 왜 필요한가 — 같은 사용자의 주문이 여러 partition에 흩어지면 순서가 꼬인다. "사용자 A의 주문"이 항상 같은 partition에 있어야 그 사용자 이벤트를 순서대로 처리할 수 있다.
구체 예:
key = "userA"인 메시지 → 항상 partition 1로 감(예). "userA"의 모든 주문이 partition 1에 순서대로 쌓임.key = "userB"→ 항상 partition 0으로 감(예).- key 없음(
null) → sticky: 한 batch를 같은 partition에 몰아넣어 효율을 높임. key 기반 순서가 필요 없는 로그에 적합.
어떻게 partition이 정해지나 — key를 해시해서 partition 수로 나눈다(자세한 공식은 심화 절에서). 핵심은 같은 key → 항상 같은 partition이라는 보장이다.
partition이 정해지는 방식을 모르면 "key를 줬는데 왜 어떤 사용자는 빨리 처리되고 어떤 사용자는 늦지?" 같은 현상의 원인(partition 쏠림)을 못 찾는다.
partition 수를 늘리면 같은 key가 다른 partition으로 간다
이제 위에서 쌓은 기초가 필요한 부분이다. topic을 만들 때 partition 3개로 잡았다. 처리량이 부족해 5로 늘리기로 했다. "빈 공간이 두 개 더 생기겠지"라고 생각한다면 — 위험하다.
partition을 늘리는 순간, 기존에 특정 partition에 가던 key가 다른 partition으로 갈 수 있다. key가 "userA"라 partition 1에 가던 메시지가, partition이 5개가 되면 partition 3으로 갈 수 있다. 같은 key의 메시지가 partition 경계에서 순서가 끊기는 것이다.
flowchart LR
A["partition 3개<br/>key 'userA' → partition 1"] -->|alter --partitions 5| B["partition 5개"]
B --> C["key 'userA' → partition 3<br/>(매핑 변화)"]
C --> D["key 기반 순서 보장 파손<br/>(기존 메시지는 옮겨지지 않음)"]
왜? partition 결정이 hash(key) % partition수라서, partition수가 바뀌면 결과가 달라지기 때문이다. 공식 문서가 명시하는 부작용 (basic-kafka-operations.md):
- key 분포 변화: Kafka Streams 등 key 기반 상태를 쓰는 앱의 순서·상태 가정이 깨짐.
- consumer 데이터 누락 위험:
auto.offset.reset=latest인 consumer가 새 partition을 인지 못하는 찰나에 메시지를 놓칠 수 있음. - 메타데이터 전파 지연: 클러스터 전체에 새 partition 정보가 퍼지는 데 시간차.
주의: partition을 늘려도 기존 메시지는 옮겨지지 않는다. userA의 과거 메시지는 partition 1에 그대로 있고, 새 메시지는 partition 3으로 간다.
절대 금지: 내부 topic(
__consumer_offsets,__transaction_state,__cluster_metadata)의 partition을 수동으로 늘리면 시스템 장애. 이들은 Kafka가 스스로 관리한다.
partition은 왜 줄일 수 없는가
"그럼 모자라면 늘리고, 너무 많으면 줄이면 되겠지?" — 줄일 수 없다. Kafka는 partition 감소를 지원하지 않는다. 이유:
- 메시지는 이미 특정 partition에 영속됐다. 합치려면 offset을 다시 매겨야 하는데, 이미 commit된 consumer offset과 어긋난다.
- 두 partition의 메시지 사이엔 정의된 전체 순서가 없었으므로(그게 partition의 전제), 합쳐서 시간순으로 다시 엮는 게 불가능하다.
- ISR 복제본들도 같은 재구성을 동시에 해야 한다.
그래서 줄이려면 새 topic을 만들고 데이터를 이주(migrate)하는 수밖에 없다. 설계 단계에서 넉넉히 잡되, key 매핑 깨짐을 감수할 수 없다면 partition 수를 사실상 고정으로 설계해야 한다.
partition 수는 어떻게 정하나
공식 문서는 절대 권장값을 명시하지 않는다. 다음 기준으로 산정한다:
| 기준 | 관계 |
|---|---|
| 처리량 | partition ↑ → 병렬 쓰기/읽기 ↑ (broker 한계까지) |
| consumer 병렬성 | consumer 수 ≤ partition 수 (초과분은 idle) |
| broker 부하 | partition ↑ → 파일 수·메모리 ↑ |
| 장애 복구 | partition ↑ → 복구 시간 ↑ |
| 순서 보장 | partition ↑ → key 순서 보장 범위 세분화 |
사이징 예(수치는 가정): 초당 100MB 목표, partition당 ~ 10MB 측정 → 최소 10 partition. consumer 병렬성 상한 10. 확장 여유 1.5 ~ 2배 → 15 ~ 20으로 시작. (partition당 처리량은 실측 필수 — 공식 문서는 절대 수치를 안 준다.)
"많을수록 좋다"가 아니다 — partition당 파일 핸들·메모리가 들고, broker 장애 시 복구가 느려진다.
Kafka 4.3 실습 — 직접 확인하기
사전: KRaft 단일 노드 기동.
topic 만들고 메시지 넣기
# Kafka 4.3, KRaft 단일 노드
bin/kafka-topics.sh --create --topic part-demo --partitions 3 --replication-factor 1 --bootstrap-server localhost:9092
# key:value 형태로 produce (key가 partition 결정)
bin/kafka-console-producer.sh --topic part-demo --property "parse.key=true" --property "key.separator=:" --bootstrap-server localhost:9092
# 입력 예:
# userA:evt1
# userA:evt2
# userB:evt3
확인할 것: 같은 key(userA)는 항상 같은 partition에 기록되는가.
bin/kafka-console-consumer.sh --topic part-demo --property "print.key=true" --property "print.partition=true" --from-beginning --bootstrap-server localhost:9092
partition: 1, key: userA, value: evt1
partition: 1, key: userA, value: evt2
partition: 2, key: userB, value: evt3
실제 partition 번호는 해시 결과에 따라 달라진다. 핵심은 같은 key → 같은 partition.
partition 추가와 key 매핑 변화 관찰
bin/kafka-topics.sh --alter --topic part-demo --partitions 5 --bootstrap-server localhost:9092
bin/kafka-console-producer.sh --topic part-demo --property "parse.key=true" --property "key.separator=:" --bootstrap-server localhost:9092
# userA:evt4 (새로 입력)
bin/kafka-console-consumer.sh --topic part-demo --property "print.key=true" --property "print.partition=true" --from-beginning --bootstrap-server localhost:9092
확인할 것: userA의 evt1/evt2는 partition 1에 있었지만, evt4는 partition이 바뀔 수 있다 → 같은 key의 순서가 partition 경계에서 끊긴다.
describe로 partition 상태 보기
bin/kafka-topics.sh --describe --topic part-demo --bootstrap-server localhost:9092
PartitionCount/ReplicationFactor: topic 전체 설정.Leader/Replicas/Isr: 각 partition의 복제 상태(단일 노드에선 모두 같음; 복제는 장 06).
흔히 묻는 것, 흔히 틀리는 것
| 오해 | 정정 |
|---|---|
| "topic은 메시지 큐라서 소비하면 사라진다" | 아니다. topic은 로그. 소비해도 retention 전엔 남음(장 01) |
| "partition 많을수록 항상 빠르다" | broker당 파일 핸들·메모리 한계. 과도하면 장애 복구 지연 |
| "partition을 줄일 수 있다" | 불가. 늘리기만. 줄이려면 새 topic으로 이주 |
| "key 순서는 영원히 보장된다" | partition 수가 변하지 않는 한 보장. partition 증가 순간 깨짐 |
| "partition 수 = consumer 수로 맞추면 된다" | consumer 병렬성 상한일 뿐. 처리량·broker 부하도 함께 고려 |
| "topic 생성 후 기본값(partition 1)으로 둬도 된다" | 프로덕션에선 거의 항상 부족. 명시적 --partitions 필수 |
| "partition 늘리면 기존 메시지도 옮겨진다" | 아니다. 과거는 그대로, 새 메시지만 새 매핑 → key 순서 끊김 |
더 깊이 — partition은 어떻게 정해지나 (murmur2)
위에서 "key를 해시해서 partition 수로 나눈다"고만 했다. 정확한 공식이 궁금한 사람을 위해:
key != null:toPositive(murmur2(key)) % numPartitions. 같은 key는 항상 같은 partition(결정적).- BuiltInPartitioner.java를 보면
murmur2해시를 쓰고, 그걸 partition 수로 나눈다(모듈로).
왜 consistent hash가 아닌가 — partition 확장에 취약한 이유
Kafka 파티셔너가 consistent hash를 쓴다고 생각하는 경우가 많다. 아니다. consistent hash라면 partition 수가 변해도 대부분 key-매핑이 유지되지만, 모듈로 방식은 partition 수가 변하면 거의 모든 key의 매핑이 바뀐다. 바로 이것이 위 "partition 늘리면 key 매핑이 깨진다"의 원인이다.
murmur2는 비암호학적 해시다 — 속도가 빠르고 분산이 고른 것이 목적이지, 보안이 아니다. toPositive는 murmur2의 signed(음수 가능) 결과를 양수로 바꾸는 래퍼(음수 해시에 모듈로를 쓰면 partition 번호가 음수가 되므로).
key 기반 상태(changelog, state store)를 쓰는 Kafka Streams 앱은 "key X는 항상 partition P에 있다"를 전제로 동작한다. partition 수가 변하면 이 전제가 깨져 상태 재구성이 일어난다.
요약 — 이 글의 결론
- topic = 메시지를 담는 이름 붙은 상자(카테고리). 용도별로 나눔(orders, clicks...). 로그라 소비해도 남고, 여러 consumer가 따로 읽고, replay 가능.
- topic 사용: multi-producer(여러 곳에서 씀) + multi-consumer(여러 서비스가 따로 읽음). 이벤트 기반 아키텍처의 기반.
- partition = topic을 물리적으로 쪼갠 조각. topic 하나의 부하를 여러 broker로 분산.
- partition 세 의미: 데이터 분할 + 병렬성(=consumer 병렬성 상한) + 순서 보장(partition 내만).
- key → 같은 partition: 같은 key는 항상 같은 partition. 같은 사용자 이벤트 순서 처리에 필수.
- partition은 늘리기만 가능(줄이기 불가). 늘리면 key 매핑이 깨져 순서 가정 파손.
- 내부 topic partition 변경 절대 금지 → 시스템 장애.
- partition 수 산정은 처리량·컨슈머 병렬성·broker 부하·복구의 균형. "많을수록 좋다"가 아니다.
생각해 볼 문제
- topic을 "메시지 큐"가 아니라 "로그"로 부르는 것이 어떤 차이를 만드는가? (소비 후 삭제 vs 남음)
- 쇼핑몰에서
orderstopic 하나를 주문 처리·분석·알림 서비스가 따로 읽는 것이 가능한 이유는? - topic 하나(partition 1개)로 초당 수만 건을 처리하기 어려운 이유는? partition이 어떻게 해결하는가?
- partition 6개인 topic에 consumer 4개인 그룹이 있다. 각 consumer는 몇 partition을 소유하는가? consumer를 8개로 늘리면?
- partition을 3→5로 늘리면 같은 key의 메시지 순서가 왜 끊기는가?
- partition을 줄일 수 없는 이유는? 줄여야 한다면 어떻게 해야 하는가?
- key 없이 produce하는데 cleanup.policy=compact인 topic에서 무슨 일이 벌어지는가?
참고
- Basic Operations — Adding/Modifying topics - 접근 2026-07-09 (partition 증가 부작용, 내부 topic 금지)
- Topic configurations - 접근 2026-07-09
- Introduction — Partitioning - 접근 2026-07-09
- BuiltInPartitioner (murmur2) - 접근 2026-07-09
'Tech Artifacts > Kafka' 카테고리의 다른 글
| Kafka - 06. replication (0) | 2026.07.09 |
|---|---|
| Kafka - 05. consumer group (0) | 2026.07.09 |
| Kafka - 04. consumer (0) | 2026.07.09 |
| Kafka - 03. producer (0) | 2026.07.09 |
| Kafka - 01. architecture (0) | 2026.07.09 |
