
Kafka 사용하기
docker-compose.yml 생성
단일 브로커 환경 구성
IntelliJ 에서 docker-compose 를 통해 실행하거나 따로 생성하여
services:
kafka:
image: apache/kafka:3.7.0
container_name: kafka
ports:
- "9092:9092"
environment:
# KRaft 모드 설정 (Node ID 및 역할)
KAFKA_NODE_ID: 1
KAFKA_PROCESS_ROLES: broker,controller
KAFKA_CONTROLLER_QUORUM_VOTERS: 1@kafka:9093
# 리스너 설정
# CONTROLLER:9093 - KRaft 내부 컨트롤러 통신
# INTERNAL:29092 - Docker 내부 통신용 (Kafka-UI 연결)
# EXTERNAL:9092 - 호스트 PC 외부 접근용
KAFKA_LISTENERS: CONTROLLER://:9093,INTERNAL://:29092,EXTERNAL://:9092
KAFKA_ADVERTISED_LISTENERS: INTERNAL://kafka:29092,EXTERNAL://localhost:9092
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: CONTROLLER:PLAINTEXT,INTERNAL:PLAINTEXT,EXTERNAL:PLAINTEXT
KAFKA_CONTROLLER_LISTENER_NAMES: CONTROLLER
KAFKA_INTER_BROKER_LISTENER_NAME: INTERNAL
# 단일 브로커 환경을 위한 replication factor 설정
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1
KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1
# 리밸런싱 딜레이 제거 (테스트 속도 향상)
KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS: 0
# 로그 & 메타데이터 저장 경로
KAFKA_LOG_DIRS: /var/lib/kafka/data
volumes:
- kafka_data:/var/lib/kafka/data
networks:
- kafka-net
kafka-ui:
image: provectuslabs/kafka-ui:latest
container_name: kafka-ui
ports:
- "8088:8080"
environment:
KAFKA_CLUSTERS_0_NAME: local-kraft
KAFKA_CLUSTERS_0_BOOTSTRAPSERVERS: kafka:29092
DYNAMIC_CONFIG_ENABLED: 'true'
depends_on:
- kafka
networks:
- kafka-net
volumes:
kafka_data:
networks:
kafka-net:
driver: bridge
Kafka-UI 접속
localhost:8088
Topic 생성
좌측 탭의 Topics → 우측 상단 Add a Topic
Topic Name
토픽 이름을 결정
소문자 + - or . 을 사용하는 것이 관례
Number of Partitions
토픽을 몇 개의 파티션으로 나눌지 결정
줄일 수 없음에 유의
Cleanup Policy
오래된 메세지를 어떻게 처리할 것인지 정책 결정
Delete → 보존 시간/크기 초과 시 삭제 (주문 로그 등에 사용)
Compact → 같은 Key의 최신만 유지 (유저의 프로필 화면, 상품 정보 등에 사용)
Compact, Delete → 둘 다 적용
Min In Sync Replicas (ISR)
최소 몇개의 복제본이 동기화 되어 있어야 쓰기를 허용하는가 결정
Replication Factor 에 수에 따라 달라짐, 만약 3으로 지정했다면
Broker 1 → 원본 (Leader)
Broker 2 → 복제본 (Follower)
Broker 3 → 복제본 (Follower)
Min ISR 이 지정되었다면 지정된 수 만큼 브로커가 살아 있어야 쓰기 가능,
만약 2로 지정했다면최소 2개의 브로커가 살아 있어야 쓰기 가능
1개만 살아 있다면 쓰기 거부 (유실 방지)
Replication Factor
메세지를 몇 개의 브로커에 복제할지 결정
Replication Factor 를 3으로 지정했다면
2대까지 브로커가 죽어도 유지 가능 (1대의 Leader 가 남아 있으니)
보통 운영 환경은 3 이상, 개발 환경은 1로 지정하여 사용
Time to retain data
얼마나 메세지를 오래 보관하는가를 결정
기본은 7일
Consumer 가 아무리 늦어도 이 기간 내에는 메세지를 읽어야함
Max size on disk in GB
토픽이 디스크 내 차지할 최대 용량을 결정
Not Set → 보존 기간으로 제어
값 설정 → 용량 초과시 오래된 메세지부터 제거
Maximum message size in bytes
하나의 메세지가 지닐 수 있는 최대 크기를 결정
기본은 1MB
이미지 또는 대용량의 데이터의 경우는 S3 같은 스토리지 서비스를
이용하고 URL만 이용해야함
메세지 전송하기
토픽 내 우측 상단 Produce Message
Partition
어느 파티션에 메세지를 넣을지 선택
Key Serde, Value Serde
Serde → Serializer + Deserializer, 직렬화 방식을 지정함
String
문자열, 간단한 텍스트 또는 JSON 문자열 지원
숫자 계열
숫자 ID 사용시
Hex, Base64
바이너리 구조로 사용해야할 경우
UUIDBinary
서비스 간 완전히 독립된 UUID 로 식별이 필요시
Key
메세지의 분류 기준
null 지정시
파티션에 라운드 로빈 방식으로 차례차례 배분
파티션이 섞이므로 순서 보장이 안됨
키 지정시
같은 키는 항상 같은 파티션으로 이동
만약 userId:1 지정시
항상 같은 파티션으로 이동됨
유저 ID 1번의 이벤트는 항상 순서가 보장됨
Value
실제 전송할 메세지 본문
보통 JSON 형태 사용
// 예시
{
"userId": 101,
"category": "A",
"timestamp": "2026-03-31T14:00:00"
}Header
메세지의 헤더
HTTP 헤더와 비슷
실무에서는 추적용도로 지정하여 활용
// 예시
{
"source": "order-service",
"version": "v1",
"traceId": "abc-123"
}보낸 메세지 확인
Offset 이 늘어나고 있는 것을 확인
Kafka 의 동작
Kafka 는 보낸 메세지를 파일로 저장
docker volume 을 확인해보면 보낸 메세지를 파일로 저장하고 있음
Consumer 가 단지 메세지를 읽을 뿐 삭제하지 않음
Consumer 는 단지 어디까지 메세지를 읽었는지 Offset 을 기록함 Consumer 가 재기동되도 Offset 을 통해 어디까지 읽은건지 알고 작업을 이어나갈 수 있음
삭제는 오직 TTL로 제어됨 하지만 TTL이 만료되도 바로 삭제되지 않고 스케줄러가 삭제를 진행하므로 스케줄러가 삭제를 해야 진짜 메세지가 삭제된 것
Consumer Group 이 여러 개일 경우
각각이 독립적으로 Offset 을 활용하고 있으므로 A 그룹이 읽었어도
B도 동일한 메세지를 읽을 수 있음독립구조를 통한 내구성, 처리량이 좋음
[ Topic: order-created ]
offset 0: 주문#1
offset 1: 주문#2
offset 2: 주문#3
offset 3: 주문#4
Consumer Group A (결제 서비스)
└── offset 4까지 읽음 → "결제 처리 완료"
Consumer Group B (알림 서비스)
└── offset 2까지 읽음 → "이메일 발송 중..."
Consumer Group C (통계 서비스)
└── offset 0부터 다시 읽는 중 → "전체 집계 중..."