백엔드 데이터 인프라 82편. Kafka Quickstart — 로컬에 KRaft 모드 단일 노드 띄우고 topic 만들고 producer/consumer 직접 실행하는 5분 hands-on. Docker·다운로드 방식 둘 다, 시리즈 1 Spring 환경에서 바로 이어 갈 수 있게 풀어쓴 학습 노트.
이 글은 백엔드 데이터 인프라 시리즈 130편 중 82편이에요. 80~81편 으로 Kafka 의 정체성과 활용 영역을 잡았다면, 82편은 직접 손으로 짚는 차례 — 로컬에 Kafka 한 노드 띄우고 topic·producer·consumer 까지 5분 hands-on 으로 끝낸다.
왜 hands-on부터 짚어야 하나
83~89편 Design 영역으로 들어가기 전에 몸으로 한 번 익혀 두면 추상 개념이 훨씬 잘 박힌다. Producer·Consumer 가 어떤 흐름인지 터미널에서 직접 보고, Topic·Partition 의 실제 모양을 확인하고, Consumer Group(같은 그룹 안 consumer 들이 partition 을 나눠 받는 단위) 동작을 직접 관찰한 뒤, Java 클라이언트 코드 마지막 단계만 추가하면 시리즈 1 Spring 환경으로 자연스럽게 이어진다.
이 글에서 두 가지 방법(다운로드·Docker) + 기본 명령어 4개 + Java 클라이언트 미리보기까지 한 번에 짚는다.
사전 준비
- Java 17+ 필요 (Kafka 4.0 기준)
- 또는 Docker (Java 설치 없이)
- 디스크 약 1GB
- 메모리 약 2GB
방법 1: 다운로드 방식
Step 1: Kafka 다운로드
$ wget https://downloads.apache.org/kafka/4.0.0/kafka_2.13-4.0.0.tgz
$ tar -xzf kafka_2.13-4.0.0.tgz
$ cd kafka_2.13-4.0.0
폴더 구조:
bin/ - 모든 CLI 도구 (.sh 파일)
config/ - 설정 파일
libs/ - JAR 의존성
Step 2: KRaft 모드 시작
Kafka 4.0+ 는 Zookeeper 없이 KRaft(Kafka 자체 합의 프로토콜) 모드로 돈다. 80편에서 본 Zookeeper 의 후속.
# Cluster UUID 생성 (한 번만)
$ KAFKA_CLUSTER_ID="$(bin/kafka-storage.sh random-uuid)"
# 로그 디렉토리 포맷
$ bin/kafka-storage.sh format --standalone -t $KAFKA_CLUSTER_ID -c config/server.properties
# Kafka 서버 시작 (foreground)
$ bin/kafka-server-start.sh config/server.properties
성공하면 수십 줄 로그 후 다음 메시지가 뜬다.
[KafkaServer id=1] started (kafka.server.KafkaServer)
이제 로컬 localhost:9092 에서 Kafka 가 듣고 있어요.
Step 3: 새 터미널 — Topic 만들기
$ bin/kafka-topics.sh --create \
--topic quickstart-events \
--bootstrap-server localhost:9092 \
--partitions 3 \
--replication-factor 1
Created topic quickstart-events.
--partitions 3= 3개 partition--replication-factor 1= 단일 노드라 1 (운영 환경은 3)
확인:
$ bin/kafka-topics.sh --describe --topic quickstart-events --bootstrap-server localhost:9092
Topic: quickstart-events TopicId: ... PartitionCount: 3 ReplicationFactor: 1
Topic: quickstart-events Partition: 0 Leader: 1 Replicas: 1 Isr: 1
Topic: quickstart-events Partition: 1 Leader: 1 Replicas: 1 Isr: 1
Topic: quickstart-events Partition: 2 Leader: 1 Replicas: 1 Isr: 1
방법 2: Docker — 가장 빠른 시작
Java 설치 없이 한 줄로 띄운다.
$ docker run -d --name kafka \
-p 9092:9092 \
apache/kafka:4.0.0
Topic 만들기:
$ docker exec -it kafka \
/opt/kafka/bin/kafka-topics.sh --create \
--topic quickstart-events \
--bootstrap-server localhost:9092 \
--partitions 3
이후 모든 명령은 docker exec -it kafka /opt/kafka/bin/<command> 형태로 들어간다.
Step 4: Producer — 이벤트 보내기
$ bin/kafka-console-producer.sh \
--topic quickstart-events \
--bootstrap-server localhost:9092
>Hello
>World
>This is event 3
> 프롬프트에서 한 줄이 한 이벤트. Ctrl+C 로 종료한다.
시험 함정 하나 — kafka-console-producer.sh 의 기본 동작은 key 없이 value 만 박히고 partition 은 라운드 로빈으로 돈다. key 까지 박으려면 --property "parse.key=true" 와 --property "key.separator=:" 를 붙인다.
$ bin/kafka-console-producer.sh \
--topic quickstart-events \
--bootstrap-server localhost:9092 \
--property "parse.key=true" \
--property "key.separator=:"
>user42:Made a payment
>user99:Logged in
이제 user42 의 이벤트는 같은 partition 으로 떨어진다 (key hash 기반).
Step 5: Consumer — 이벤트 받기
$ bin/kafka-console-consumer.sh \
--topic quickstart-events \
--from-beginning \
--bootstrap-server localhost:9092
Hello
World
This is event 3
--from-beginning 은 topic 의 모든 메시지를 처음부터 끌어온다. 생략하면 현재 시점 이후 메시지만 본다.
key 까지 보기:
$ bin/kafka-console-consumer.sh \
--topic quickstart-events \
--from-beginning \
--bootstrap-server localhost:9092 \
--property "print.key=true" \
--property "key.separator=:"
user42:Made a payment
user99:Logged in
Step 6: Consumer Group 관찰
새 터미널 두 개 열고 같은 그룹으로 붙인다.
# Terminal A
$ bin/kafka-console-consumer.sh --topic quickstart-events --bootstrap-server localhost:9092 --group group-1
# Terminal B (같은 그룹)
$ bin/kafka-console-consumer.sh --topic quickstart-events --bootstrap-server localhost:9092 --group group-1
같은 --group group-1 인 두 consumer 는 partition 을 나눠 받는다. Partition 3개에 consumer 2명이면 한 명이 2개 partition, 다른 한 명이 1개를 맡는 식.
다른 그룹은 독립적으로 모든 메시지를 받는다.
# Terminal C (다른 그룹)
$ bin/kafka-console-consumer.sh --topic quickstart-events --bootstrap-server localhost:9092 --group group-2
group-2 는 처음부터 (또는 현재부터) 모든 메시지를 다시 받는다.
Consumer group 상태 확인:
$ bin/kafka-consumer-groups.sh --bootstrap-server localhost:9092 --describe --group group-1
GROUP TOPIC PARTITION CURRENT-OFFSET LOG-END-OFFSET LAG
group-1 quickstart-events 0 5 5 0
group-1 quickstart-events 1 3 3 0
group-1 quickstart-events 2 2 2 0
- CURRENT-OFFSET = consumer 가 처리한 마지막 offset
- LOG-END-OFFSET = topic 의 마지막 offset
- LAG = 남은 미처리 =
LOG-END-OFFSET - CURRENT-OFFSET
운영 모니터링에서 가장 핵심 메트릭이 이 lag.
Step 7: Topic 정리·삭제
# 모든 topic 목록
$ bin/kafka-topics.sh --list --bootstrap-server localhost:9092
# topic 삭제
$ bin/kafka-topics.sh --delete --topic quickstart-events --bootstrap-server localhost:9092
Java 클라이언트 미리보기 — Spring Boot
// 의존성
// implementation 'org.springframework.kafka:spring-kafka'
// Producer
@Autowired
private KafkaTemplate<String, String> kafkaTemplate;
public void sendEvent(String key, String value) {
kafkaTemplate.send("quickstart-events", key, value);
}
// Consumer
@KafkaListener(topics = "quickstart-events", groupId = "spring-group")
public void listen(String message) {
System.out.println("Received: " + message);
}
spring:
kafka:
bootstrap-servers: localhost:9092
consumer:
group-id: spring-group
auto-offset-reset: earliest
key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
value-deserializer: org.apache.kafka.common.serialization.StringDeserializer
producer:
key-serializer: org.apache.kafka.common.serialization.StringSerializer
value-serializer: org.apache.kafka.common.serialization.StringSerializer
이것만으로 시리즈 1 Spring Boot 환경에서 Kafka 통합이 시작된다. 130편 Spring Kafka 에서 더 깊이 들어간다.
자주 막히는 자리
1. "Connection refused" 에러
Kafka 서버가 안 떠 있다. kafka-server-start.sh 로그를 확인한다.
2. "Topic does not exist" 에러
Auto-create 가 꺼진 환경이거나 topic 을 안 만들었다. 명시적으로 create 하거나 auto.create.topics.enable=true 로 켠다 (개발용).
3. "Marking the coordinator dead" 에러
Consumer group coordinator 통신 문제. bootstrap server 주소·방화벽·hostname 해상도를 확인한다.
4. Consumer 가 메시지를 못 받음
기본은 consumer 시작 이후 메시지만 받는다. --from-beginning 또는 auto.offset.reset=earliest 로 바꾼다.
5. partition 보다 많은 consumer
여기가 정말 중요한 자리 — partition 3개에 consumer 5명이면 2명은 놀고 있다. partition 수 ≥ consumer 수 가 일반 원칙.
다음 단계 — 무엇을 더 해볼까
Quickstart 끝났다면:
- Java 클라이언트 로 producer/consumer 직접 작성 (90~93편)
- 여러 broker 띄우고 replication 관찰 (107편 replication 깊이)
- Kafka Streams 로 간단한 처리 파이프라인 (121~129편)
- Confluent Cloud 같은 Managed Kafka 무료 plan 체험
시험 직전 한 번 더 — Kafka Quickstart 함정 압축 노트
- 사전 준비 = Java 17+ 또는 Docker
- Kafka 4.0+ = KRaft 모드 (Zookeeper 없음)
- 시작 =
kafka-storage.sh format→kafka-server-start.sh - Cluster UUID =
kafka-storage.sh random-uuid(한 번만) - Topic 생성 =
kafka-topics.sh --create --topic <name> --partitions N --replication-factor M - Topic 정보 =
kafka-topics.sh --describe --topic <name> - Producer =
kafka-console-producer.sh --topic <name> - 기본 producer = key 없음, value 만, partition 라운드 로빈
- key 박기 =
--property "parse.key=true"+--property "key.separator=:" - Consumer =
kafka-console-consumer.sh --topic <name> --from-beginning= 처음부터 모든 메시지- key 보기 =
--property "print.key=true" - Consumer Group = 같은
--group= partition 나눠 받음 - 다른 group = 독립적 모든 메시지 받음
kafka-consumer-groups.sh --describe= lag 모니터링- LAG = 미처리 메시지 수 (가장 중요한 메트릭)
- 일반 원칙 — partition 수 ≥ consumer 수
- partition > consumer = 일부 consumer 가 여러 partition 처리
- partition < consumer = 일부 consumer 가 놀고 있음
- Docker =
apache/kafka:4.0.0이미지 - Spring Boot 통합 =
spring-kafka의존성 +@KafkaListener·KafkaTemplate - 자주 에러 — connection refused, topic does not exist, coordinator dead, from-beginning 누락
공식 문서: Apache Kafka Quickstart 에서 자세한 사양과 다른 시나리오를 확인할 수 있어요.
시리즈 다른 편 (앞뒤 글 모음)
이전 글:
- 77편 — Redis Vector Database (HNSW · 코사인 유사도 · RAG)
- 78편 — Redis Client 라이브러리 종합
- 79편 — Java Redis Client (Jedis vs Lettuce 깊이)
- 80편 — Apache Kafka란 + 이벤트 스트리밍의 표준
- 81편 — Kafka 활용 영역 7가지 (메시징·활동 추적·로그·이벤트 소싱)
다음 글: