도커 환경에서의 Kafka UI 설정
알림 기능이 제대로 동작하는 지 검증하기 앞서 Kafka 브로커가 설계한 대로 작동하는 지 Kafka UI를 통해 확인해보자.
Kafka UI는 Kafka 브로커에 저장된 메시지와 메타데이터를 조회해 시각적으로 표현하는 HTTP 기반의 외부 웹 UI 도구이다. 일반적으로 Docker 컨테이너로 배포되며 브라우저를 통해 Kafka 브로커의 운영 및 모니터링 환경을 제공한다. 다음의 Docker 설정을 통해 브라우저 접속 환경을 구성할 수 있다.
kafka-ui:
image: provectuslabs/kafka-ui:latest
container_name: kafka-ui
ports:
- "8085:8080" # 호스트의 브라우저는 8085로 접속 가능
environment:
KAFKA_CLUSTERS_0_NAME: local
KAFKA_CLUSTERS_0_BOOTSTRAPSERVERS: kafka:9092
depends_on:
- kafka
restart: unless-stopped
Kafka UI 접속
이제 도커에서 Kafka를 실행하고 localhost:8085로 접속하면 다음 화면을 볼 수 있다.

서비스 규모가 크지 않아 단일 브로커 구조로 설정했으므로, 현재 클러스터는 1개의 브로커와 7개의 토픽으로 구성되어 있다. approval-show-topic만 3개의 파티션을 가지고 있고, 나머지 토픽은 각각 1개의 파티션으로 설정했는데도 대시보드에 전체 파티션 수가 58개로 표시되어 다소 이상하게 보인다. 우선 토픽별로 파티션 구성을 확인해보자.

대시보드에서 확인한 7개의 토픽을 확인할 수 있다. 하위 6개는 직접 생성한 토픽이지만, 가장 위에는 생성한 적이 없는 __consumer_offsets 토픽이 존재한다. 이 토픽에 대해 알아보자.
__consumer_offsets
Kafka는 내부적으로 50개의 파티션을 사용하여 __consumer_offsets 토픽을 운영하며, 각 컨슈머 그룹의 커밋 내역과 상태 메타데이터를 기록한다.
이 토픽에 생성된 메시지를 살펴보면 {컨슈머 그룹, 토픽, 파티션}을 Key로 하는 메시지와 {컨슈머 그룹}을 Key로 하는 메시지가 있다.
전자는 오프셋 커밋 로그, 후자는 그룹 메타데이터 로그이다.




offset commit log
컨슈머 그룹, 토픽, 파티션을 Key로 하는 메시지는 일전에 서버 재시작 상황에서 언급했던 컨슈머 오프셋의 커밋 로그이다. 이는 특정 파티션의 작업 진행 상태를 나타내는 체크포인트 역할을 하며 애플리케이션의 갑작스러운 종료나 재시작 상황에서 유실 없이 마지막 처리 지점부터 작업을 재개할 수 있도록 보장한다.
서버 재시작 상황에서 메모리가 날아간 컨슈머는 자신이 어디서부터 다시 읽어야 하는지에 대한 정보(=마지막으로 커밋된 오프셋=컨슈머 오프셋)를 브로커(Group Coodrinator)에게 요청한다. 컨슈머가 언제 꺼질지 모르는 Kafka 브로커는 각 컨슈머가 파티션의 어디까지 처리했는지 오프셋을 커밋할 때마다 내부 토픽에 로그로 기록해둔다. 이때 커밋 로그가 저장되는 내부 토픽이 __consumer_offsets이다.
group-A / approval-show-topic / partition-0 -> committed offset 100
group-B / approval-show-topic / partition-1 -> committed offset 98
group-C / approval-show-topic / partition-2 -> committed offset 97
컨슈머 오프셋은 개별 컨슈머 인스턴스 단위가 아니라 consumer group + topic + partition 단위로 저장된다. 따라서 재시작이나 리밸런싱으로 기존과 다른 컨슈머 인스턴스가 해당 파티션을 맡더라도, 같은 컨슈머 그룹에 속해 있다면 마지막으로 커밋된 오프셋부터 이어서 메시지를 가져올 수 있다. 즉, 커밋 로그에서 컨슈머 오프셋은 컨슈머 그룹의 파티션별 처리 위치를 의미하게 된다.
Group metadata log
__consumer_offsets 토픽에서 컨슈머 그룹만을 Key로 하는 메시지는 해당 그룹의 구성원 정보, 각 파티션 구독 상태, 파티션 할당 상태를 관리하는 그룹 메타데이터 로그이다. 컨슈머 그룹의 상태와 관련된 메타데이터를 기록하며 컨슈머 그룹의 존재 여부, 각 파티션 구독 상태, 현재 오프셋 위치, 리밸런스 발생 여부와 같은 이벤트를 상태 변경으로 간주하여 저장한다.
이 로그는 컨슈머 그룹 전체의 생명주기와 리밸런싱 현황 파악에 사용되며, 컨슈머 그룹 내 구성원 변경이나 세션 유지 상태 관리에 있어서 근거가 되는 정보이다.


따라서 메시지를 아직 소비하지 않아 커밋 기록이 없더라도, 컨슈머가 토픽을 구독하거나 그룹에 참여하는 순간 이런 상태 변경 정보는 컨슈머 그룹을 Key로 하는 메시지로 __consumer_offsets에 저장된다. 실제로 hot-board-notice-group은 아직 소비 작업을 수행한 적이 없음에도 불구하고, 리더 컨슈머 선출 및 파티션 할당이 성공적으로 완료되었다는 그룹 메타데이터 로그가 기록되어 있다. 이러한 기록들은 단순히 오프셋 추적을 넘어, 컨슈머와 브로커 간의 멤버십 유지, 파티션 재할당, 업무 재분배 등에 활용된다.
알림 기능 이벤트 전달 검증
등록 승인 알림


내내 예로 들었던 approval-show-topic을 살펴보자. 11개의 메시지가 파티션 3개에 골고루 저장되어 있는 것을 볼 수 있다.
3개의 컨슈머 그룹 모두 concurrency=3으로 설정되어있다. 따라서 10,000개의 이벤트가 생성되더라도, 3개의 워커 스레드가 각 파티션을 하나씩 담당하여 승인 알림, 구독 알림, 추천 알림 로직을 총 9개의 스레드에서 메인 흐름과 독립적으로 처리할 수 있다. 설령 애플리케이션이 재시작되더라도, 브로커에 커밋된 오프셋을 기준으로 중단된 위치부터 재처리가 이루어져 시스템의 내구성과 처리 안정성을 보장한다.
등록 반려 알림



등록 반려 이벤트의 경우, 메인 로직으로부터 반려 사유를 전달받아야 하므로 최소 정보만 전달하는 다른 메시지에 비해 용량이 크다. 추후 반려 사유 탬플릿이 고정되면 ENUM으로 매핑시켜서 최소한의 파라미터만 전달하는 방안도 가능해보인다.
게시글 댓글 알림


댓글과 대댓글 이벤트도 잘 전달된다.
이로써 Kafka를 이용한 비동기 이벤트 처리 구조를 적용한 알림 기능이 잘 작동함을 확인했다.
사실 Kafka를 공부하면서 줄곧 느낀 점은, Kafka의 진정한 가치는 수십, 수백만 단위의 row를 처리해야하는 대규모 서비스 환경에서 비로소 드러난다는 것이다. 시스템이 available한 최대 capacity를 균일하게 유지하며 수많은 read, write 트래픽을 안정적으로 처리하고, 필요에 따라 수평 확장되는 구조에서 Kafka와 같은 이벤트 기반 통신이 없다면 어떻게 서비스를 안정적으로 설계하고 상태를 일관되게 관리할 지 잘 상상이 가지 않는다.
앞으로 MSA쪽도 공부해보며 이해를 넓히고, 실제 마이크로서비스 개발 과정에서 Kafka를 직접 적용해 보며 그 가치를 검증해 보고 싶다.
'dev > infra' 카테고리의 다른 글
| Kafka - 이벤트 기반의 비동기 작업 처리 구조로 알림 기능 구현하기 (3) (0) | 2026.01.22 |
|---|---|
| Kafka - 이벤트 기반의 비동기 작업 처리 구조로 알림 기능 구현하기 (2) (1) | 2026.01.21 |
| Kafka - 이벤트 기반의 비동기 작업 처리 구조로 알림 기능 구현하기 (1) (0) | 2026.01.19 |