백엔드 데이터 인프라 87편 — Kafka Design: Consumer (Pull · Consumer Group · Offset)

2026-05-17백엔드 데이터 인프라

백엔드 데이터 인프라 87편. Kafka Consumer 설계 — Push vs Pull 의 선택 이유, Long Polling 최적화, Consumer Position (offset) 관리, Consumer Group 분담 모델, Static Membership 으로 rebalance 회피까지 풀어쓴 학습 노트.

📚 백엔드 데이터 인프라 · 87편 — Kafka Design: Consumer (Pull · Consumer Group · Offset)

이 글은 백엔드 데이터 인프라 시리즈 130편 중 87편이에요. 86편 까지 Producer 설계를 풀었다면, 이번 87편은 Consumer 측의 핵심 설계 결정. Push vs Pull·Consumer Group·Offset 관리·Rebalance·Static Membership 다섯 가지 영역.

Kafka Consumer 설계가 어렵게 느껴지는 이유

Consumer 가 겉으로는 단순 — "메시지 받기". 하지만 4가지 결정 이 숨어 있어요.

첫째, 왜 Pull 모델인가. 다른 메시지 시스템들의 Push 와 다르게 Kafka 는 Consumer 가 broker(메시지 중계 서버) 에 fetch 요청. 이 결정의 정확한 trade-off.

둘째, Offset 관리의 미묘함. offset(파티션 안 메시지 위치 번호) 으로 "어디까지 처리했는지" 추적하는 방식이 전통 메시지 큐와 완전히 다름. Consumer 가 자기 offset 관리.

셋째, Consumer Group 의 rebalancing 함정. 같은 그룹 안 consumer 가 추가·제거되면 partition(토픽을 쪼갠 단위) 재분배. rebalance 가 자주 일어나면 운영 사고.

Push vs Pull — Kafka 의 선택

Push 모델 (Flume·Scribe 등)

Broker → Consumer (즉시 push)

문제:

  • Broker 가 전송 속도 결정 → consumer 가 따라가지 못하면 DoS(서비스 거부 공격) 비슷한 상태
  • 다양한 consumer (빠른·느린) 처리가 어려움

Pull 모델 (Kafka 의 선택)

Consumer → "fetch from offset N" → Broker → 데이터 chunk 반환

장점:

  • Consumer 가 자기 속도대로 처리 — 느려져도 그냥 뒤처짐
  • aggressive batching(한 번에 묶어 가져오기) 가능현재 offset 부터 가능한 모든 데이터 한 번에
  • Multiple consumer 가 각자 자기 속도

Pull 모델의 함정 — Busy Polling

데이터 없을 때 tight loop 으로 polling?

해법 — Long Polling:

Consumer → "fetch from offset N, max wait 500ms, min bytes 1024"
   → Broker 가 데이터 들어올 때까지 대기 (최대 500ms)
   → 데이터 들어오면 응답, 없으면 timeout 응답

설정:

  • fetch.max.wait.ms=500 — 최대 대기
  • fetch.min.bytes=1024 — 최소 데이터 양 (작으면 더 기다림)

tight loop 없고 즉시 응답성도 보장.

왜 producer 는 push 인가?

여기서 시험 함정 — Kafka 가 pull 만 쓰는 건 아님. Producer → Broker 는 push, Broker → Consumer 만 pull.

이유 — producer 가 수천 개 일 수 있어 각각이 로컬 디스크 운영 은 비현실적. 직접 broker 에 push 가 자연스러움.

한 줄 정리 — Producer push (단순) + Consumer pull (속도 조절·batching) = Kafka 의 비대칭 모델.

Consumer Position — Offset 만 추적

전통 메시지 큐의 함정

대부분 메시지 시스템에서는 broker 가 각 메시지의 ACK(수신 확인 응답) 상태 추적 하고, 메시지마다 sent / acknowledged / consumed 같은 다양한 상태 + 별도 메타데이터 를 들고 있어요.

문제는 두 가지. ACK 전 consumer 가 죽으면 처리 두 번 이 되고, broker 가 수많은 상태 메타데이터 를 관리해야 해서 성능·확장 한계 에 부딪힘.

Kafka 의 단순 모델

핵심 — 각 partition 은 한 consumer 만 (consumer group 안에서) 처리.

따라서:

  • Consumer position = 단일 정수 (offset)
  • partition 하나당 숫자 하나
  • 주기적으로 checkpoint (commit) 만 하면 됨

장점 — 완전 가벼움, broker 부담 0.

Rewind 가능 — 부가 이점

Consumer 가 임의의 offset 으로 되감기 가능. 버그 수정 후 재처리·새 시스템 학습용 과거 데이터 등.

전통 큐는 consume 시 메시지 삭제재처리 불가능. Kafka 는 retention(메시지 보존 기간) 안에서 언제든 재처리.

Consumer Group — 분담 모델

모델

Topic "orders" (10 partitions)
       │
       ↓
Group "order-workers" (3 consumers)
  Worker A → partitions 0, 1, 2, 3
  Worker B → partitions 4, 5, 6
  Worker C → partitions 7, 8, 9

같은 그룹 안 consumer = partition 을 나눠 받음. partition 한 개는 한 consumer 에게만.

여러 그룹 = 독립

Topic "orders" 
  ├─ Group "real-time-analytics" → 모든 메시지 처리
  ├─ Group "ml-training" → 모든 메시지 처리
  └─ Group "data-warehouse" → 모든 메시지 처리

각 그룹이 독립적. 같은 메시지를 여러 시스템이 다른 목적으로 처리.

핵심 규칙

  • partition ≥ consumer — partition 보다 많은 consumer 는 놀고 있음
  • partition < consumer — 일부 consumer 가 여러 partition
  • partition 안 메시지는 한 consumer 가 순차 처리 → 순서 보장 (partition 안에서만)

Rebalance — 운영의 까다로운 영역

트리거

다음 이벤트가 생기면 모든 consumer 가 잠시 멈춤 + 다시 분배 가 발동돼요. consumer 가 그룹에 추가 / 제거 되거나, consumer 가 죽었을 때 (heartbeat(생존 신호) timeout), 토픽의 partition 수 변경, consumer 가 지정 topic 변경 같은 경우.

비용

여기서 시험 함정이 하나 있어요 — Rebalance 동안 모든 consumer 가 멈춤 ("stop-the-world"). 수 초~수십 초 가능. 빈번하면 consumer lag(처리 지연 누적) 폭증.

큰 환경에서 rebalance storm — 한 consumer 죽음 → rebalance → 또 다른 consumer 타임아웃 → 또 rebalance → 무한 루프.

완화

  • session.timeout.ms 충분히 크게 (기본 45초, 운영 환경 60~90초)
  • heartbeat.interval.ms 적당히 (session timeout 의 1/3)
  • max.poll.interval.ms — poll 사이 최대 간격 (기본 5분, 처리 길면 늘림)
  • Static Membership (아래 깊이)

Static Membership — Rebalance 회피

Kafka 2.3+ 도입. consumer instance 가 persistent ID 가짐.

spring:
  kafka:
    consumer:
      properties:
        group.instance.id: "worker-1"   # 고정 ID

장점:

  • 재시작·rolling deploy 시 같은 ID 로 재참가rebalance 안 일어남
  • partition 이 그대로 재할당 → 상태 큰 application 의 복구 시간 0
  • 큰 Kafka Streams 앱 에서 매우 유리

설정 시 유의 — 모든 consumer 에 unique group.instance.id 를 부여하고, 중복되면 FencedInstanceIdException 으로 강제 종료돼요.

대규모·상태 큰 환경 = 반드시 Static Membership.

Long Polling 동작

Consumer.poll(Duration.ofMillis(1000))
  • 데이터 있으면 즉시 반환
  • 데이터 없으면 1초까지 대기 후 빈 결과 반환

내부 동작 = fetch.max.wait.ms (broker 측 long poll) + poll() (consumer 측 wait).

자주 헷갈리는 자리 — poll(timeout) 의 timeout 은 consumer 측 최대 대기. 실제 데이터 가져오기는 broker 측 long poll 도 거침.

Offline Data Load — Batch Consumer

Kafka 의 영구 저장 덕분에 batch consumer 가 자연스러워요. Hadoop(분산 처리 프레임워크) 의 map task 가 각 partition 을 병렬 처리 하고, 실패해도 원래 offset 부터 재시작 이라 중복이 없음.

Real-time + Batch 동일 Kafka 에서 처리.

한계·실무 함정

1. partition 수 = consumer 한계

partition 10개 = consumer 최대 10명. 더 늘려도 효과 없음. 처음 partition 수 설계 가 중요.

2. Rebalance Storm

위에서 강조. session.timeout 크게 + Static Membership.

3. Offset commit 타이밍

84편의 at-least-once vs at-most-once — process 전 commit (loss) vs process 후 commit (duplicate). at-least-once + 멱등성 이 일반적.

4. partition 안 순서만 보장

전체 topic 순서가 필요하면 partition 1개 — 처리량 X. 비즈니스 key 별 순서 만 필요하면 같은 key 사용.

5. Consumer 가 slow processing 으로 lag 폭증

처리 시간 > 메시지 들어오는 속도 → lag 무한 증가. consumer 수 늘리기 + partition 늘리기 또는 batch 처리 효율화.

시험 직전 한 번 더 — Kafka Consumer 함정 압축 노트

  • Kafka 는 Producer push + Consumer pull 비대칭 모델
  • Pull 장점 = consumer 자기 속도, aggressive batching
  • Pull 함정 = busy polling → Long Polling 으로 해결
  • fetch.max.wait.ms (broker) + poll(timeout) (consumer) 조합
  • 전통 메시지 큐 = broker 가 각 메시지 ACK 상태 관리 → 무거움
  • Kafka = consumer position = 단일 offset 정수 per partition
  • 주기적 checkpoint (commit) 만으로 충분
  • Rewind 가능 = 임의 offset 으로 되감기, 재처리·과거 학습
  • Consumer Group = 같은 그룹 안 partition 나눠 받음
  • 다른 그룹 = 독립적으로 모든 메시지
  • partition ≥ consumer 가 원칙 (partition < consumer = 놀고 있음)
  • partition 안 순서만 보장
  • Rebalance 트리거 = consumer 추가/제거/사망, partition 변경
  • Rebalance 동안 = stop-the-world (수 초~수십 초)
  • Rebalance Storm = 잦은 rebalance 무한 루프
  • 완화 = session.timeout.ms·heartbeat.interval.ms·max.poll.interval.ms 적절히
  • Static Membership (Kafka 2.3+) = group.instance.id 고정
  • 재시작·rolling deploy 시 = rebalance 안 일어남
  • 대규모·상태 큰 환경 = Static Membership 필수
  • Batch consumer 도 자연스러움 (Hadoop·DW)
  • 함정 — partition 수가 consumer 한계
  • 함정 — Rebalance Storm
  • 함정 — Offset commit 타이밍 (process 전 vs 후)
  • 함정 — slow processing 으로 lag 폭증

공식 문서: Kafka Design — The Consumer 에서 자세한 사양을 확인할 수 있어요.

시리즈 다른 편 (앞뒤 글 모음)

이전 글:

다음 글:

※ 이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.

답글 남기기

error: Content is protected !!