백엔드 데이터 인프라 97편 — Kafka Consumer 설정 25가지 (Group · Commit · Fetch)

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

백엔드 데이터 인프라 97편. Kafka Consumer 설정 25가지 — group.id·auto.offset.reset·enable.auto.commit·session.timeout·max.poll.interval·fetch.min.bytes·max.poll.records·partition.assignment.strategy·isolation.level 등 카테고리별 튜닝 핵심을 풀어쓴 학습 노트.

📚 백엔드 데이터 인프라 · 97편 — Kafka Consumer 설정 25가지 (Group · Commit · Fetch)

이 글은 백엔드 데이터 인프라 시리즈 130편 중 97편이에요. 96편 에서 Producer 설정을 잡았다면, 이번 97편은 그 반대편으로 Consumer 설정 25가지 를 짚어요. 87편 Design·92편 API 와 직결되는 튜닝 핵심이에요.

Consumer Config가 어렵게 느껴지는 이유

Consumer 설정(메시지 받는 쪽 설정)은 Producer 보다 훨씬 까다로워요. 다음 4가지 영역이 서로 맞물리거든요.

  1. Group membership (session·heartbeat·rebalance)
  2. Offset commit (auto·sync·async)
  3. Fetch tuning (min·max·timeout)
  4. Partition assignment (strategy)

설정 하나가 다른 설정에 영향을 줘요. session.timeout 을 늘리면 heartbeat 도 같이 늘려야 하는 식의 조정 규칙이 곳곳에 깔려 있어요.

이 글에서는 25가지 설정을 카테고리로 묶어서 살펴보고, 권장 조합과 함정도 함께 정리해요.

1. 필수 (4개)

bootstrap.servers=kafka1:9092,kafka2:9092
group.id=order-workers
key.deserializer=org.apache.kafka.common.serialization.StringDeserializer
value.deserializer=org.springframework.kafka.support.serializer.JsonDeserializer

group.id 는 consumer group 식별자예요. 같은 group 안의 consumer 들이 partition 을 나눠 가져요.

2. Offset (5개)

auto.offset.reset

auto.offset.reset=latest          # 기본, 새 group = 현재 시점부터
# 또는
auto.offset.reset=earliest         # 새 group = 처음부터
# 또는
auto.offset.reset=none             # offset 없으면 에러 (수동 처리)

개발 환경에서는 earliest 로 모든 메시지를 보고, 운영 환경에서는 상황에 맞춰 골라요.

enable.auto.commit

enable.auto.commit=true            # 기본 (위험)
# 운영 권장
enable.auto.commit=false

true 면 자동 commit 이어서 메시지 손실 위험이 있어요(92편). 운영 환경에서는 false 가 안전해요.

auto.commit.interval.ms

auto.commit.interval.ms=5000       # 5초 (기본)

enable.auto.commit=true 일 때만 의미가 있어요.

isolation.level

isolation.level=read_uncommitted    # 기본
# 또는 EOS 환경
isolation.level=read_committed

EOS(exactly-once semantics, 정확히 한 번 처리) 깊이는 88편에서 다뤄요. Transactional 메시지를 처리하는 자리예요.

exclude.internal.topics

exclude.internal.topics=true        # 기본

__consumer_offsets 같은 internal topic 을 제외해요.

3. Group Membership (8개)

session.timeout.ms

session.timeout.ms=45000           # 45초 (기본)

Broker 가 이 consumer 의 죽음을 판단하는 시간이에요. 처리가 길면 늘려 잡아요.

권장:

  • 짧은 처리 (수 ms) = 30~60초
  • 긴 처리 (수 분) = 120~180초

heartbeat.interval.ms

heartbeat.interval.ms=3000          # 3초 (기본)

Consumer 가 broker 에 heartbeat 를 보내는 주기예요. 보통 session.timeout.ms / 3 으로 잡아요.

max.poll.interval.ms

max.poll.interval.ms=300000          # 5분 (기본)

두 poll() 사이의 최대 간격이에요. 이걸 넘기면 consumer 가 죽은 걸로 보고 rebalance 가 일어나요.

처리 시간이 긴 환경에서는 10분·30분·1시간으로 늘려 잡기도 해요.

max.poll.records

max.poll.records=500                 # 기본

한 poll() 의 최대 메시지 수예요. 이 메시지들을 max.poll.interval.ms 안에 다 처리해야 해요.

처리 시간이 길면 50~100 으로 적게, 짧으면 1000+ 으로 많이 잡아요.

group.instance.id

group.instance.id=worker-1

87편에서 다룬 Static Membership(고정 멤버십, rebalance 회피)이에요. 재시작이나 rolling deploy 때 불필요한 rebalance 를 피해요.

대규모거나 상태가 큰 환경에서는 반드시 설정해요.

partition.assignment.strategy

partition.assignment.strategy=\
    org.apache.kafka.clients.consumer.CooperativeStickyAssignor    # Kafka 2.4+ 권장
# 또는
partition.assignment.strategy=\
    org.apache.kafka.clients.consumer.StickyAssignor                # 2.4 이전
# 또는
partition.assignment.strategy=\
    org.apache.kafka.clients.consumer.RangeAssignor                  # 기본
Strategy 특징
Range (기본) partition 범위 분배, 한 consumer 가 많이 가질 수 있음
RoundRobin 균등 분배
Sticky 균등 + rebalance 시 이전 할당 유지 시도
CooperativeSticky Sticky + 점진적 rebalance (멈춤 시간 ↓)

대부분의 환경에서는 CooperativeSticky 를 골라요. Kafka 2.4+ 에서 권장되고, rebalance 영향이 가장 적어요.

group.protocol

group.protocol=consumer              # 기본
# 또는
group.protocol=classic               # 옛 protocol

Kafka 4.0+ 의 새 Consumer Group Protocol 인 KIP-848(차세대 consumer 프로토콜) 이에요. 아직 덜 알려졌지만 점진적으로 도입되는 중이에요.

group.remote.assignor

group.remote.assignor=range

새 KIP-848 protocol 의 broker-side assignor 예요.

4. Fetch (6개)

fetch.min.bytes

fetch.min.bytes=1                    # 1 (기본)
# 처리량 우선
fetch.min.bytes=10240                # 10KB

Broker 가 최소 N 바이트가 모일 때까지 기다려요. 처리량은 늘고 지연은 늘어나요.

fetch.max.wait.ms

fetch.max.wait.ms=500                 # 500ms (기본)

fetch.min.bytes 가 안 채워졌어도 max.wait 이 지나면 반환해요. Long polling 의 한계예요.

fetch.max.bytes

fetch.max.bytes=52428800              # 50MB (기본)

한 fetch 의 최대 응답 크기예요.

max.partition.fetch.bytes

max.partition.fetch.bytes=1048576     # 1MB (기본)

Partition 한 개에서 fetch 하는 최대 크기예요. 한 메시지가 이보다 크면 consumer 가 진행을 못 해요.

여기서 시험 함정이 하나 있어요. 이 값이 broker max.message.bytes 와 topic max.message.bytes 보다 작으면 큰 메시지를 받을 수 없어요. 세 값의 일관성을 늘 챙겨야 해요.

receive.buffer.bytes

receive.buffer.bytes=65536            # 64KB (기본)

TCP receive buffer 예요.

send.buffer.bytes

send.buffer.bytes=131072              # 128KB (기본)

TCP send buffer 예요.

5. Connection·Network (3개)

request.timeout.ms

request.timeout.ms=30000              # 30초 (기본)

Broker 응답을 기다리는 최대 시간이에요.

connections.max.idle.ms

connections.max.idle.ms=540000         # 9분 (기본)

Idle 상태가 길어지면 connection 을 닫아요.

reconnect.backoff.ms · reconnect.backoff.max.ms

reconnect.backoff.ms=50
reconnect.backoff.max.ms=1000

Reconnect 사이의 exponential backoff 를 잡아요.

6. Identity·Monitoring (2개)

client.id

client.id=order-worker-1

운영 모니터링에서 가독성을 높여요. 같은 group 안에서 각 consumer 의 고유 id 역할이에요.

metrics.recording.level

metrics.recording.level=INFO          # 기본
# DEBUG = 더 자세한 메트릭

운영 환경 권장 조합

일반 백엔드 (균형)

bootstrap.servers=kafka1:9092,kafka2:9092,kafka3:9092
group.id=order-workers
key.deserializer=org.apache.kafka.common.serialization.StringDeserializer
value.deserializer=org.springframework.kafka.support.serializer.JsonDeserializer

# Offset
auto.offset.reset=earliest
enable.auto.commit=false

# Group
session.timeout.ms=60000
heartbeat.interval.ms=20000
max.poll.interval.ms=300000
max.poll.records=500
partition.assignment.strategy=org.apache.kafka.clients.consumer.CooperativeStickyAssignor

# Fetch
fetch.min.bytes=1024
fetch.max.wait.ms=500

# Identity
client.id=order-worker-prod-1
group.instance.id=order-worker-prod-1

처리 시간 긴 환경 (예: ML 추론)

session.timeout.ms=120000
heartbeat.interval.ms=40000
max.poll.interval.ms=1800000          # 30분
max.poll.records=10                    # 적게

매우 빠른 처리량 (메트릭·로그)

fetch.min.bytes=102400                 # 100KB
fetch.max.wait.ms=100
max.poll.records=5000                  # 많이
enable.auto.commit=true                # 손실 OK
auto.commit.interval.ms=1000

EOS (Kafka Streams 등)

isolation.level=read_committed
enable.auto.commit=false

Spring Boot 적용

spring:
  kafka:
    bootstrap-servers: kafka1:9092,kafka2:9092
    consumer:
      group-id: order-workers
      auto-offset-reset: earliest
      enable-auto-commit: false
      key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
      value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer
      max-poll-records: 500
      properties:
        session.timeout.ms: 60000
        heartbeat.interval.ms: 20000
        max.poll.interval.ms: 300000
        partition.assignment.strategy: org.apache.kafka.clients.consumer.CooperativeStickyAssignor
        group.instance.id: ${HOSTNAME}
        client.id: order-worker
    listener:
      ack-mode: MANUAL
      concurrency: 3                   # 한 application 에 3 consumer

설정 상호작용 — 함정 모음

1. heartbeat.interval.mssession.timeout.ms 비율

heartbeat = session / 3 이 권장 비율이에요. 두 값이 같으면 heartbeat 한 번만 놓쳐도 죽은 걸로 처리돼요.

2. max.poll.interval.ms < 실제 처리 시간

처리 도중에 consumer 가 죽은 걸로 판정되면 rebalance 가 일어나요. 처리는 안 끝났는데 다른 consumer 가 같은 메시지를 다시 받아 중복 처리가 발생해요.

3. max.partition.fetch.bytes < 큰 메시지

해당 partition 이 아예 진행을 못 해요. broker·topic 의 max.message.bytes 와 일관되게 잡아야 해요.

4. enable.auto.commit=true + 메시지 처리 실패

commit 은 됐는데 처리는 못 한 상태가 되면 메시지가 영구 손실돼요. 운영 환경에서는 false 로 두고 manual commit 을 써요.

5. group.instance.id 중복

FencedInstanceIdException 으로 종료돼요. 각 instance 가 고유한 id 를 가져야 해요.

6. Range Assignor 의 불균등

partition 7개에 consumer 3명이면 [3, 3, 1] 로 갈려요(Range 는 균등하지 않아요). CooperativeSticky 를 권장해요.

모니터링 메트릭

JMX(Java Management Extensions, 자바 모니터링 표준):

  • records-consumed-rate — 초당 메시지 수
  • records-lag-max — 최대 lag
  • records-lag-avg — 평균 lag
  • fetch-rate — fetch 횟수
  • commit-rate — commit 횟수
  • heartbeat-rate — heartbeat

가장 중요한 건 records-lag-max 예요. consumer 가 따라가지 못하고 있다는 신호거든요.

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

  • 필수 = bootstrap.servers·group.id·key/value.deserializer
  • Offsetauto.offset.reset (latest/earliest/none)·enable.auto.commit·isolation.level (read_uncommitted/read_committed)
  • 운영 환경 = enable.auto.commit=false
  • EOS = isolation.level=read_committed
  • Group Membershipsession.timeout.ms (45초)·heartbeat.interval.ms (3초)·max.poll.interval.ms (5분)·max.poll.records (500)
  • 권장 비율 = heartbeat = session / 3
  • 처리 시간 긴 환경 = max.poll.interval.ms 늘림
  • group.instance.id = Static Membership, rebalance 회피
  • 대규모·상태 큰 = 필수
  • partition.assignment.strategy — Range·RoundRobin·Sticky·CooperativeSticky (권장 2.4+)
  • CooperativeSticky = 점진적 rebalance, 멈춤 시간 ↓
  • KIP-848 = group.protocol=consumer (Kafka 4.0+)
  • Fetchfetch.min.bytes·fetch.max.wait.ms·fetch.max.bytes·max.partition.fetch.bytes
  • max.partition.fetch.bytes 와 broker·topic max.message.bytes 일관성
  • client.id = 운영 모니터링 가독성
  • 운영 환경 권장 조합 — 일반·긴 처리·고처리량·EOS
  • Spring = spring.kafka.consumer.* + listener.ack-mode=MANUAL + concurrency
  • 함정 — heartbeat = session 같으면 한 번 놓침에 죽음
  • 함정 — max.poll.interval.ms < 실제 처리 → 중복 폭증
  • 함정 — max.partition.fetch.bytes < 큰 메시지 → 진행 못 함
  • 함정 — enable.auto.commit=true + 처리 실패 = 영구 손실
  • 함정 — group.instance.id 중복 = FencedInstanceIdException
  • 함정 — Range Assignor 불균등 분배
  • 모니터링 = JMX 의 records-lag-max (가장 중요)

공식 문서: Kafka Consumer Configs 에서 모든 설정의 자세한 사양을 확인할 수 있어요.

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

이전 글:

다음 글:

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

답글 남기기

error: Content is protected !!