fos-blog/study
01 / 홈02 / 카테고리03 / 시리즈
01 / 홈02 / 카테고리03 / 시리즈

카테고리

  • AI 페이지로 이동
    • RAG 페이지로 이동
    • agent 페이지로 이동
    • langgraph 페이지로 이동
    • 사람용 CLI와 AI 에이전트용 CLI는 설계가 다르다
    • agents.md
    • Claude Code 메모리: CLAUDE.md와 .claude/rules를 규칙으로 쓰는 법
    • Claude Code의 Skill 시스템 - 개발자를 위한 AI 자동화의 새로운 차원
    • Claude Code를 5주 더 쓴 결과 — 스킬·CLAUDE.md를 키워가는 방식
    • Claude Code를 11일 동안 쓴 결과 — 데이터로 본 나의 사용 패턴
    • Claude Code 멀티 에이전트 — Teams
    • AI 에이전트와 디자인의 새 컨벤션 — DESIGN.md, Google Stitch, Claude Design
    • Docling — IBM Research 의 문서 파싱 toolkit 상세 정리
    • 하네스 엔지니어링 실전 — 4인 에이전트 팀으로 코딩 파이프라인 구축하기
    • 하네스 엔지니어링 — 오래 실행되는 AI 에이전트를 위한 설계
    • 멀티모달 LLM (Multimodal Large Language Model)
    • AI 에이전트와 함께 MVP 만들기 — dooray-cli 사례
    • 온톨로지에서 코딩 에이전트 컨텍스트까지 — 클래스·관계 설계와 그래프 평가
    • OpenClaw는 context와 memory를 어떻게 관리하나 — 나만의 에이전트를 구성하는 법
    • OpenClaw vs Hermes Agent — 갈아탈까 고민하며 정리한 비교
  • ai 페이지로 이동
    • agent 페이지로 이동
    • [초안] AI 제품 백엔드 안정성 — 지연·비용·권한·관측·도구 실패·폴백/재시도/사람 에스컬레이션
    • [초안] LLM 평가 프레임워크: 골든셋, 회귀 테스트, LLM-as-a-judge, 사람 피드백 루프
  • algorithm 페이지로 이동
    • live-coding 페이지로 이동
    • 분산 계산을 위한 알고리즘
  • architecture 페이지로 이동
    • 시니어 백엔드를 위한 API 설계 실전 스터디 팩
    • API 버저닝과 하위 호환성: 모바일·외부 컨슈머까지 안전하게 진화시키기
    • 캐시 설계 전략 총정리
    • [초안] 커머스 Spring 서비스에 Clean/Hexagonal Architecture를 실용적으로 적용하기
    • [초안] 커머스 도메인 모델링: 주문·재고·노출의 세 축을 분리해서 설계하기
    • 커머스 주문 상태와 데이터 정합성 기본기
    • [초안] 쿠폰/프로모션 동시성과 정합성 기본기 — 선착순·중복 사용 방지·발급/사용/복구
    • [초안] DDD와 도메인 모델링: 시니어 백엔드 관점의 전술/전략 패턴 실전 가이드
    • [초안] Decorator & Chain of Responsibility — 행동을 체인으로 조립하는 두 가지 방식
    • 디자인 패턴
    • [초안] 분산 아키텍처 완전 정복: Java 백엔드 시니어 인터뷰 대비 실전 가이드
    • [초안] 분산 트랜잭션과 Outbox 패턴 — 왜 2PC를 피하고 어떻게 대신할 것인가
    • 분산 트랜잭션
    • [초안] e-Commerce 주문·결제 도메인 모델링: 상태머신, 멱등성, Outbox/Saga 실전 정리
    • [초안] Event Sourcing과 CQRS — 상태가 아니라 변화를 저장한다는 발상
    • [학습중] 금융 거래 취소·정정·대사·일마감 운영
    • [학습중] 금융 거래 상태와 원장 설계
    • [초안] F&B 쿠폰·프로모션·멤버십·포인트 설계
    • [초안] F&B · e-Commerce 디지털 채널 도메인 한 장 정리
    • [초안] F&B 주문/매장/픽업 상태머신 설계
    • [초안] F&B 이커머스 결제·환불·정산 운영 가이드
    • [초안] Hexagonal / Clean Architecture를 Spring 백엔드에 적용하기
    • [초안] 대규모 커머스 트래픽 처리 패턴 — 대규모 회원과 메가 프로모션을 버티는 설계
    • [초안] 레거시 JSP/jQuery 화면과 신규 API가 공존하는 백엔드 운영 전략
    • [학습중] 모듈러 모놀리스에서 MSA로 점진 전환하는 실습
    • [초안] MSA 서비스 간 통신: Redis [Cache-Aside](../database/redis/cache-aside.md) × Kafka 이벤트 하이브리드 설계
    • [초안] Observability 입문: 시니어 백엔드가 장애를 탐지하고 대응하는 방식
    • [초안] Outbox / Inbox Pattern 심화 — 분산 메시징의 정합성 문제를 DB 트랜잭션으로 풀어내기
    • [초안] 결제 도메인 멱등성과 트랜잭션 재시도 기본기
    • [초안] 시니어 백엔드를 위한 Resilience 패턴 실전 가이드 — Timeout, Retry, Circuit Breaker, Bulkhead, Backpressure
    • [초안] Spring Batch vs Event-Driven — 같은 비동기처럼 보이지만 전혀 다른 두 패러다임
    • [초안] Strategy Pattern — 분기문을 없애는 설계, 시니어 백엔드 인터뷰 핵심 패턴
    • [초안] 시니어 백엔드를 위한 시스템 설계 입문 스터디 팩
    • [초안] 템플릿 메서드 패턴 - 백엔드 처리 골격을 강제하는 가장 오래되고 가장 위험한 패턴
    • [초안] 대규모 트래픽 중 무중단 마이그레이션 — Feature Flag + Shadow Mode 실전
  • database 페이지로 이동
    • milvus 페이지로 이동
    • mysql 페이지로 이동
    • opensearch 페이지로 이동
    • qdrant 페이지로 이동
    • redis 페이지로 이동
    • vespa 페이지로 이동
    • 김영한의-실전-데이터베이스-설계 페이지로 이동
    • [초안] DB Connection Pool Saturation과 Thread Pool 격리
    • 커넥션 풀 크기는 얼마나 조정해야 할까?
    • 인덱스 - DB 성능 최적화의 핵심
    • [초안] JPA N+1과 커머스 조회 모델: 주문/메뉴/쿠폰 도메인에서 살아남기
    • [초안] MyBatis 기본기 — XML Mapper, resultMap, 동적 SQL, 운영 패턴 정리
    • [초안] MyBatis와 JPA/Hibernate 트레이드오프 — 레거시 백엔드를 다루는 시니어 관점
    • 한국어 형태소 분석기 Nori vs Lindera — OpenSearch에서 Milvus로 갈 때 어휘 검색은 유지되나
    • 벡터 DB 5종, 아키텍처는 어떻게 다른가
    • 벡터 DB 5종을 실제로 벤치마크했다 — 같은 recall에서 QPS는 얼마나 갈리나
    • 벡터 DB 어떻게 고를까 — OpenSearch · Milvus · Qdrant · Vespa · pgvector 비교
    • 벡터 DB를 실제로 도입한 사례 — 빅테크 프로덕션
    • 역정규화 (Denormalization)
    • 데이터 베이스 정규화
  • devops 페이지로 이동
    • docker 페이지로 이동
    • k8s 페이지로 이동
    • k8s-in-action 페이지로 이동
    • observability 페이지로 이동
    • [초안] 커머스/F&B 채널 장애 첫 5분과 관측성 기본기
    • [초안] 운영 데이터 정합성 장애 대응 — 결제 취소 누락과 중복 적재 런북
    • Envoy Proxy
    • [초안] F&B / e-Commerce 운영 장애 대응과 모니터링 — 백엔드 관점 정리
    • Graceful Shutdown
    • [초안] 시니어 백엔드를 위한 SLO와 Error Budget 기반 장애 대응
  • http 페이지로 이동
    • HTTP Connection Pool
    • HTTPS는 어떻게 안전한가 — TLS, 인증서, 그리고 termination
  • interview 페이지로 이동
    • [초안] AI 서비스 팀 경험 기반 시니어 백엔드 면접 질문 뱅크 — Spring Batch RAG / gRPC graceful shutdown / 캐시 정합성 / 12일 AI 웹툰 MVP
    • Observability — 면접 답변 프레임
    • [초안] 시니어 Java 백엔드 면접 마스터 플레이북 — 김병태
    • [초안] NSC 슬롯팀 경험 기반 질문 은행 — 도메인 모델링·동시성·성능·AI 협업
  • java 페이지로 이동
    • concurrency 페이지로 이동
    • jdbc 페이지로 이동
    • opentelemetry 페이지로 이동
    • spring 페이지로 이동
    • spring-batch 페이지로 이동
    • testing 페이지로 이동
    • 더_자바_코드를_조작하는_다양한_방법 페이지로 이동
    • [초안] Java 동시성 락 정리 — 커머스 메뉴/프로모션 정책 캐시 갱신 관점
    • [초안] JVM 튜닝 실전: 메모리 구조부터 Virtual Threads, GC 튜닝, 프로파일링까지
    • Java의 로깅 환경
    • MDC (Mapped Diagnostic Context)
    • Java StampedLock — 읽기 폭주에도 쓰기가 밀리지 않는 락
    • Virtual Thread와 Project Loom
  • javascript 페이지로 이동
    • typescript 페이지로 이동
    • AbortController
    • Async Iterator와 제너레이터
    • CommonJS와 ECMAScript Modules
    • 제너레이터(Generator)
    • Http Client
    • Node 백엔드 운영 패턴 — Streams 백프레셔, pipe/pipeline, 멱등성 vs 분산 락
    • Node.js
    • npm vs pnpm — 어떤 기준으로 선택했나
    • `setImmediate()`
  • kafka 페이지로 이동
    • [초안] Kafka 기본 개념 — 토픽, 파티션, 오프셋, 복제
    • Kafka를 사용하여 **데이터 정합성**은 어떻게 유지해야 할까?
    • [초안] Kafka 실전 설계: 파티션 전략, 컨슈머 그룹, 전달 보장, 재시도, 순서 보장 트레이드오프
    • [학습중] Kafka 파티션·리밸런스·컨슈머 지연 운영
    • 메시지 전송 신뢰성
    • [초안] Spring Kafka 컨슈머 오프셋 커밋과 트랜잭션 정렬: AckMode, manual ack, 멱등 처리
  • linux 페이지로 이동
    • fsync — 리눅스 파일 동기화 시스템 콜
    • tmux — Terminal Multiplexer
  • mlops 페이지로 이동
    • llm-serving 페이지로 이동
    • serving-frameworks 페이지로 이동
    • Python CUDA 버전 생태계 — nvidia-smi, nvcc, pip, conda가 다 다른 버전을 말하는 이유
    • GPU 컨테이너의 CUDA 버전 호환성 — nvidia-smi부터 이미지 다이어트까지
    • Kubernetes GPU 노드에서 /run tmpfs가 꽉 차서 Pod가 안 뜰 때
    • GPU·CUDA·MPS 기초 — 자바 백엔드 개발자가 처음 만나는 그림
    • Multi-process GPU 워크로드 — 자바 ThreadPool 사용자가 만나는 모델 차이
    • ML 서비스 성능 분석 워크플로 — 자바 백엔드 트러블슈팅과 다른 점
    • 한 GPU 를 여러 프로세스가 나눠 쓰기 — Time-Slicing 과 MPS
  • network 페이지로 이동
    • Connection reset by peer는 누가 보낸 걸까 — 리버스 프록시 홉마다 TCP 연결은 따로 논다
    • L2(스위치)와 L3(라우터)의 역할 차이
    • L4와 VIP(Virtual IP Address)
    • IP Subnet
  • python 페이지로 이동
    • Python async/await — CompletableFuture·Reactor 와 다른 점, 그리고 blocking I/O 함정
    • Python 의존성 관리 — Java Maven/Gradle 사용자가 만나는 첫 충격
    • FastAPI 기초 — Spring Boot 사용자가 빠르게 익히는 법
    • Java 개발자를 위한 Python 심화 — OOP·데코레이터·컨텍스트 매니저
    • PyTorch 기초 — 텐서, 디바이스, 그리고 모델 로딩이 무거운 이유
    • Java 개발자를 위한 Python 문법 핵심
    • ThreadLocal 에서 contextvars 로 — Python 의 요청 컨텍스트 전파
    • OCR 동작 원리 — Layout · Text · Post-process 3단계
    • Python 서버의 RSS 가 안 줄어드는 이유 — gc.collect 의 한계와 malloc_trim
  • rabbitmq 페이지로 이동
    • [초안] RabbitMQ Basics — 실전 백엔드 관점에서 정리하는 메시지 브로커 기본기
    • [초안] RabbitMQ vs Kafka — 백엔드 메시징 선택 기준과 실전 운영 관점
  • resume 페이지로 이동
    • [초안] 김병태 경력기술서
    • [초안] 김병태 포트폴리오
  • security 페이지로 이동
    • [초안] 시니어 백엔드를 위한 보안 / 인증 스터디 팩 — Spring Security, JWT, OAuth2, OWASP Top 10
    • [초안] Spring Security 6.x OAuth2 + JWT 상용 인증 설계 — Grant 선택, Resource Server, Refresh Rotation, 로그아웃
  • task 페이지로 이동
    • ai-service-team 페이지로 이동
    • nsc-slot 페이지로 이동
    • sb-dev-team 페이지로 이동
    • the-future-company 페이지로 이동
  • testing 페이지로 이동
    • [초안] 시니어 Java 백엔드를 위한 테스트 전략 완전 정리 — 피라미드부터 TestContainers, 마이크로벤치, Contract까지
  • thinking 페이지로 이동
    • 좋은 일을 넘어 중요한 일을 하는 법
FOS-BLOG · FOOTERall systems normal·v0.1 · 2026.04.27·seoul, kr
Ffos-blog/study

개발 학습 기록을 정리하는 블로그입니다. 공부하면서 기록하고, 기록하면서 다시 배웁니다.

visitors
01site
  • Home↗
  • Posts↗
  • Categories↗
  • Glossary↗
  • About↗
02policy
  • 소개/about
  • 개인정보처리방침/privacy
  • 연락처/contact
03categories
  • AI↗
  • Algorithm↗
  • DB↗
  • DevOps↗
  • Java/Spring↗
  • JS/TS↗
  • React↗
  • Next.js↗
  • System↗
04connect
  • GitHub@jon890↗
  • Source repositoryjon890/fos-study↗
  • RSS feed/rss.xml↗
  • Newsletter매주 1 회 · 한 편의 글→
© 2026 FOS Study. All posts MIT-licensed.
built with·Next.js·Tailwind v4·Geist·Pretendard·oklch
fos-blog/kafka/[학습중] Kafka 파티션·리밸런스·컨슈머…
kafkadevops

[학습중] Kafka 파티션·리밸런스·컨슈머 지연 운영

이 문서는 Kafka를 사용했다는 수준에서 벗어나 파티션 키, 리밸런스, 컨슈머 지연을 운영 판단으로 설명하기 위해 만들었다. 학습 목표는 순서·처리량·복구 시간의 관계를 이해하고, 장애를 재현해 지표와 로그로 원인을 구분하는 것이다. 완료 기준은 로컬 클러스터에서 파티션 쏠림, 느린 컨슈머, 리밸런스를 각각 재현하고 대응 전후를 측정하는 것이다. > Ka...

2026.07.21·7 min read·16 views

이 문서는 Kafka를 사용했다는 수준에서 벗어나 파티션 키, 리밸런스, 컨슈머 지연을 운영 판단으로 설명하기 위해 만들었다. 학습 목표는 순서·처리량·복구 시간의 관계를 이해하고, 장애를 재현해 지표와 로그로 원인을 구분하는 것이다. 완료 기준은 로컬 클러스터에서 파티션 쏠림, 느린 컨슈머, 리밸런스를 각각 재현하고 대응 전후를 측정하는 것이다.

Kafka 실전 설계: 파티션 전략, 컨슈머 그룹, 전달 보장, 재시도, 순서 보장 트레이드오프를 먼저 읽으면 좋다. 이 글은 개념을 반복하지 않고 운영 진단과 장애 실습에 집중한다.

운영의 출발점은 메시지가 아니라 업무 키다

Kafka의 파티션은 병렬 처리 단위이면서 순서 보장 경계다. 프로듀서가 같은 키를 사용하면 같은 토픽 안에서 같은 파티션으로 라우팅된다. 따라서 키를 잘못 고르면 순서가 깨지거나 특정 파티션에 부하가 몰린다.

금액 이동 이벤트라면 거래 식별자보다 계좌 식별자가 순서 보장에 더 적합할 수 있다. 같은 계좌의 출금과 취소가 서로 다른 파티션에 들어가면 여러 컨슈머가 역순으로 처리할 수 있기 때문이다. 반대로 전체 고객 식별자를 키로 쓰면 활동량이 큰 고객 하나가 핫 파티션을 만들 수 있다.

키 선택 전에 다음 질문을 적는다.

  • 어떤 이벤트끼리 순서가 반드시 보장돼야 하는가.
  • 그 순서를 보장하는 최소 업무 범위는 무엇인가.
  • 키 분포가 장기적으로 균등한가.
  • 한 키의 최대 처리량이 한 파티션 처리량을 넘을 수 있는가.
  • 파티션 수를 늘렸을 때 키의 파티션 매핑 변화가 허용되는가.

파티션 수는 컨슈머 수만 보고 정하지 않는다

한 컨슈머 그룹에서 한 파티션은 한 시점에 한 컨슈머에게만 배정된다. 파티션이 12개인데 컨슈머가 20개면 최소 8개는 놀게 된다. 하지만 파티션 수를 무작정 늘리면 브로커 메타데이터, 파일 핸들, 복제 트래픽, 장애 복구 비용도 증가한다.

처리량 기반의 거친 계산은 다음과 같다.

text
필요 파티션 수 = ceil(목표 초당 메시지 수 / 단일 파티션에서 검증한 초당 처리량)

이 계산에는 다음 여유를 더한다.

  • 장애 시 일부 브로커에 리더가 몰리는 상황
  • 피크 트래픽과 메시지 크기 변화
  • 컨슈머 외부 IO 지연
  • 향후 병렬 처리 확장

계산값은 정답이 아니라 부하 시험의 시작점이다.

리밸런스가 일어나는 이유

컨슈머 그룹 구성이나 구독 대상이 바뀌면 파티션을 다시 배정한다. 프로세스 배포, 컨슈머 장애, 세션 타임아웃, poll() 지연, 파티션 추가가 대표 원인이다.

리밸런스 중에는 파티션 소유권이 이동한다. 처리 중이던 레코드와 커밋된 오프셋의 경계가 어긋나면 중복 처리가 발생할 수 있다. 그래서 리밸런스는 단순한 로그 소음이 아니라 처리 정지 시간과 중복 가능성을 함께 봐야 하는 사건이다.

Apache Kafka 4.0부터 제공되는 새 consumer rebalance protocol은 완전 증분 방식으로 전역 동기화 장벽을 줄인다. 다만 클라이언트에서 자동 활성화되는 것이 아니며 group.protocol=consumer 설정과 호환성 검토가 필요하다. 기존 classic protocol을 사용하는 환경에서는 협력적 할당과 static membership이 배포 중 파티션 이동을 줄이는 선택지가 될 수 있다.

poll 간격과 처리 시간의 관계

classic consumer에서 max.poll.interval.ms 안에 다음 poll()이 호출되지 않으면 컨슈머가 정상 처리 중이어도 실패한 것으로 판단될 수 있다. 레코드 한 묶음 처리 시간이 이 값을 넘으면 리밸런스가 반복된다.

대응은 타임아웃만 크게 늘리는 것이 아니다.

  • max.poll.records를 줄여 한 번에 가져오는 작업량을 제한한다.
  • 오래 걸리는 외부 IO를 별도 워커로 넘기되 오프셋 커밋 경계를 명확히 한다.
  • 처리 시간을 분포로 측정하고 최악의 입력을 찾는다.
  • 긴 배치 작업이면 Kafka consumer가 적합한 실행 모델인지 다시 검토한다.
yaml
spring:
  kafka:
    consumer:
      enable-auto-commit: false
      max-poll-records: 100
      properties:
        max.poll.interval.ms: 300000

설정값은 예시일 뿐이다. 처리 시간의 p99와 장애 복구 목표를 측정한 뒤 정해야 한다.

lag는 결과이지 원인이 아니다

컨슈머 지연(lag)은 파티션의 최신 오프셋과 컨슈머 그룹의 커밋 오프셋 차이다. lag가 늘었다는 사실만으로 원인을 알 수 없다.

원인 후보는 다음과 같다.

  • 유입량이 처리량보다 많다.
  • 특정 키가 한 파티션에 몰렸다.
  • 외부 API나 DB가 느리다.
  • 역직렬화 실패와 재시도가 반복된다.
  • 리밸런스가 자주 발생한다.
  • 컨슈머 스레드가 GC나 CPU 포화로 멈춘다.
  • 오프셋 커밋이 실패하지만 실제 처리는 진행 중이다.

lag를 볼 때는 합계만 보지 않는다. 파티션별 lag, 증가 속도, 가장 오래된 메시지의 나이, 처리 성공률을 함께 본다.

bash
kafka-consumer-groups.sh \
  --bootstrap-server localhost:9092 \
  --group transfer-worker \
  --describe

한 파티션만 lag가 증가하면 컨슈머 수 증설보다 키 분포와 해당 파티션의 메시지 특성을 먼저 확인한다.

재시도와 DLQ의 운영 기준

재시도는 일시적 실패에만 의미가 있다. 검증 오류나 지원하지 않는 스키마를 같은 입력으로 반복 처리하면 lag만 키운다.

오류를 세 종류로 분류한다.

  • 일시적 오류: 네트워크 단절, 일시적 DB 타임아웃
  • 영구적 데이터 오류: 필수 필드 누락, 해석 불가능한 값
  • 코드 또는 계약 오류: 새로운 스키마를 구버전 컨슈머가 읽음

짧은 재시도는 컨슈머 안에서 수행할 수 있다. 긴 재시도는 retry topic으로 보내 원본 파티션을 막지 않는다. DLQ에는 원본 토픽, 파티션, 오프셋, 예외 유형, 재시도 횟수, 추적 식별자를 보존한다.

DLQ는 쓰레기통이 아니다. 유입률, 체류 시간, 재처리 결과, 반복 실패 원인을 운영 지표로 관리한다.

장애 대응 순서를 미리 고정한다

lag 경보를 받았을 때 운영자가 즉흥적으로 오프셋을 이동하면 원인과 복구 범위가 더 불명확해진다. 먼저 영향 범위를 고정하고, 원인을 분류하고, 처리량을 복구한 뒤, 누락과 중복을 검증하는 순서를 따른다.

첫 단계에서는 토픽 전체가 아니라 어느 컨슈머 그룹과 파티션이 밀리는지 확인한다. 프로듀서 유입률이 평소와 같은지, 최근 배포와 컨슈머 수 변화가 있었는지 함께 본다. 특정 파티션만 밀리면 키 쏠림이나 독성 메시지를 의심한다. 모든 파티션이 비슷하게 밀리면 DB와 외부 API, CPU, GC처럼 공통 의존성을 확인한다.

두 번째 단계에서는 처리량 복구 조치가 데이터 의미를 바꾸는지 검토한다. 컨슈머 증설은 파티션 수 이내에서만 병렬성을 높인다. 문제 메시지를 건너뛰면 서비스는 살아나지만 해당 거래의 처리가 누락될 수 있다. 오프셋을 앞으로 옮기는 조치는 대상 범위와 재처리 계획을 승인받은 뒤 수행해야 한다.

세 번째 단계에서는 장애 구간의 시작 오프셋과 종료 오프셋을 기록한다. 복구 뒤 업무 DB의 처리 건수와 Kafka 소비 결과를 비교해 중복과 누락을 찾는다. 금융 이벤트라면 단순 소비 성공률뿐 아니라 원장 반영과 대사 결과까지 확인한다.

운영 기록에는 다음 근거를 남긴다.

  • 경보가 시작된 시각과 최초로 증가한 파티션
  • 유입률과 처리율의 차이
  • 리밸런스와 배포 이력
  • 적용한 설정 또는 스케일 변경
  • 재처리한 오프셋 범위
  • 업무 데이터 대사 결과

정확히 한 번의 범위를 과장하지 않는다

Kafka의 idempotent producer는 프로듀서 재시도로 같은 레코드가 브로커 로그에 중복 기록되는 문제를 줄인다. Kafka transaction은 Kafka 안의 읽기·처리·쓰기와 오프셋 커밋을 하나의 트랜잭션으로 묶을 수 있다.

하지만 컨슈머가 MySQL을 갱신하고 외부 API를 호출하는 순간 Kafka transaction만으로 전체 작업이 정확히 한 번 처리되지는 않는다. DB 고유 제약, Inbox, 멱등한 상태 전이, Outbox 같은 애플리케이션 설계가 여전히 필요하다.

관련 경계는 Spring Kafka 리스너의 오프셋 커밋과 트랜잭션 경계에서 확인한다.

나쁜 대응과 개선된 대응

lag가 늘면 컨슈머부터 늘린다

핫 파티션이나 느린 DB가 원인이면 컨슈머를 늘려도 효과가 없다. 파티션별 lag와 처리 시간 분해를 먼저 수행한다.

리밸런스를 없애려고 타임아웃만 늘린다

장애 감지 시간까지 같이 늘어나 복구가 늦어진다. 처리 묶음 크기와 외부 IO 경계를 먼저 줄이고 타임아웃을 조정한다.

실패 메시지를 무한 재시도한다

영구 오류 한 건이 파티션 전체를 막는다. 오류 분류, 제한된 재시도, 격리, 승인된 재처리 흐름을 만든다.

오프셋을 먼저 커밋한다

처리 실패 뒤 메시지를 다시 읽지 못해 유실로 이어진다. 업무 반영 완료 뒤 커밋하되 중복 가능성을 멱등성으로 흡수한다.

장애 실습

기존 Kafka 로컬 환경을 띄운 뒤 payments 토픽을 파티션 6개로 만든다.

bash
kafka-topics.sh \
  --bootstrap-server localhost:9092 \
  --create \
  --topic payments \
  --partitions 6 \
  --replication-factor 1

세 가지 실험을 수행한다.

핫 파티션 실험

전체 메시지의 대부분에 같은 키를 사용한다. 파티션별 레코드 수와 lag를 비교한다. 그다음 분산 가능한 업무 키로 바꾸고 분포를 다시 측정한다.

느린 컨슈머 실험

특정 메시지에서 DB 호출 대신 의도적인 지연을 넣는다. max.poll.records와 처리 시간을 바꾸며 리밸런스 발생 횟수를 기록한다.

배포 리밸런스 실험

컨슈머 세 개를 실행한 뒤 하나씩 종료하고 재시작한다. 파티션 이동, 처리 중단 시간, 중복 처리 건수를 로그로 남긴다. 가능하면 classic과 새 consumer protocol을 각각 실험하되 같은 클러스터에서 지원 여부를 먼저 확인한다.

설명할 때의 답변 구조

파티션 키는 같은 업무 개체의 순서를 보장하는 최소 범위로 정하고, 분포 쏠림을 함께 검증합니다. lag는 원인이 아니라 결과이므로 파티션별 lag, 메시지 나이, 처리 시간, 리밸런스 빈도를 같이 봅니다. 리밸런스가 반복되면 poll 주기와 처리 묶음, 외부 IO 지연을 확인하고 설정만 늘리지 않습니다. 전달 보장은 보통 at-least-once를 전제로 두고 DB 고유 제약과 멱등한 컨슈머로 중복을 흡수합니다. Kafka transaction의 exactly-once 범위가 외부 DB나 API까지 자동으로 확장되지는 않는다고 구분합니다.

학습 완료 체크리스트

  • 업무 순서 경계와 파티션 키를 연결해 설명할 수 있다.
  • 파티션 수 산정 가정을 부하 시험으로 검증했다.
  • 리밸런스가 발생하는 조건을 재현했다.
  • max.poll.interval.ms와 처리 시간의 관계를 설명할 수 있다.
  • 파티션별 lag와 메시지 나이를 함께 관찰했다.
  • 핫 파티션과 느린 컨슈머를 구분할 수 있다.
  • 일시적 오류와 영구적 오류의 재시도 정책을 분리했다.
  • DLQ 재처리 절차와 지표를 정의했다.
  • Kafka exactly-once의 적용 범위를 과장하지 않는다.

참고 자료

  • Apache Kafka — Consumer Rebalance Protocol
  • Apache Kafka — Consumer Configuration
  • Apache Kafka — Design
  • Spring for Apache Kafka Reference Documentation
on this page
  • 01운영의 출발점은 메시지가 아니라 업무 키다
  • 02파티션 수는 컨슈머 수만 보고 정하지 않는다
  • 03리밸런스가 일어나는 이유
  • 04poll 간격과 처리 시간의 관계
  • 05lag는 결과이지 원인이 아니다
  • 06재시도와 DLQ의 운영 기준
  • 07장애 대응 순서를 미리 고정한다
  • 08정확히 한 번의 범위를 과장하지 않는다
  • 09나쁜 대응과 개선된 대응
  • lag가 늘면 컨슈머부터 늘린다
  • 리밸런스를 없애려고 타임아웃만 늘린다
  • 실패 메시지를 무한 재시도한다
  • 오프셋을 먼저 커밋한다
  • 10장애 실습
  • 핫 파티션 실험
  • 느린 컨슈머 실험
  • 배포 리밸런스 실험
  • 11설명할 때의 답변 구조
  • 12학습 완료 체크리스트
  • 13참고 자료
tags
#학습중#카카오뱅크#금융도메인#Kafka#파티션#리밸런스#컨슈머지연#장애복구

이런 글도

  • [초안] Spring Kafka 컨슈머 오프셋 커밋과 트랜잭션 정렬: AckMode, manual ack, 멱등 처리
    이 문서의 결론을 먼저 적으면 하나다. > "DB 커밋"과 "Kafka 오프셋 커밋"은 원자적으로 묶을 수 없다. 그래서 순서를 DB 커밋 → 오프셋 커밋으로 고정해 at-least-once로 만들고, 중복 재처리는 컨슈머 멱등성으로 흡수한다. 이 한 줄을 코드와 실패 시나리오로 풀어내는 것이 목표다. 다루는 질문은 다음과 같다. - 리스너가 정상 종료하면...
    📨 kafka
    kafka
    2026.06.13
  • [초안] Kafka 실전 설계: 파티션 전략, 컨슈머 그룹, 전달 보장, 재시도, 순서 보장 트레이드오프
    Kafka를 "메시지 큐로 쓴다"는 말은 맞지만, 그것만으로는 시니어 면접을 통과할 수 없다. 면접관이 묻고 싶은 것은 "파티션을 몇 개로 잡았고 왜 그랬나", "컨슈머가 죽었을 때 리밸런싱은 어떻게 되나", "결제 이벤트인데 순서가 바뀌면 어떻게 처리했나", "메시지 유실은 허용 가능한 도메인인가" 같은 설계 판단이다. 이 문서는 Kafka의 내부 동작을...
    📨 kafka
    kafka
    2026.04.16
  • [초안] Kafka 기본 개념 — 토픽, 파티션, 오프셋, 복제
    Kafka 글을 여러 편 정리하다 보니 "기본 개념을 한 번 모아서 짚는 문서"가 빠져 있었다. 이 글은 토픽·파티션·오프셋·복제(Leader/Follower/ISR)에 한해서 입문 수준으로만 정리한다. 파티션 키 전략, 컨슈머 그룹 리밸런싱, 메시지 전달 보장(at-least-once 등) 같은 운영·설계 영역은 별도 문서에서 다룬다. - 파티션 수 결정...
    📨 kafka
    kafka
    2026.01.30
  • Kafka를 사용하여 **데이터 정합성**은 어떻게 유지해야 할까?
    - 메시징 시스템에서 '정확히 한 번 (Exactly-once)'을 보장하기 위한 전략들과 설정들을 살펴보자. 데이터 유실은 보통 Producer가 메시지를 보냈으나 Broker에 안전하게 저장되지 않았을 떄 발생한다. - acks=all (또는 -1) : 리더 파티션뿐만 아니라 min.insync.replicas에 설정된 모든 복제본이 메시지를 받았는지...
    📨 kafka
    kafka
    2026.01.30

댓글 (0)