TestForge | Aidevops | 📊 Plogger ✍️ Blog 📚 Docs
plogger

AI DevOps Korea

AI 서비스 개발, 운영, 성능개선을 하나의 루프로 연결합니다

aidevops.kr에서 LLMOps, RAG, AI Agent, 관측성, 평가, 비용-성능 최적화를 실전 운영 관점으로 정리합니다.

Apache Kafka로 이벤트 드리븐 아키텍처 구현하기

· 수정 4월 22일
Apache Kafka로 이벤트 드리븐 아키텍처 구현하기 다이어그램
이 그림은 이벤트 계약, Kafka 토픽, 컨슈머 복구, 운영 신호가 실전 이벤트 기반 시스템에서 어떻게 맞물리는지 보여 줍니다.
Kafka를 도입했다고 해서 좋은 이벤트 드리븐 아키텍처가 자동으로 생기지는 않습니다. 어려운 부분은 브로커를 띄우는 것이 아니라, **어떤 비즈니스 사실을 어떤 계약으로 발행하고, 어떤 순서를 보장하며, 중복과 재처리와 실패를 어떻게 운영할 것인지**를 정하는 데 있습니다.

그래서 Kafka의 본질은 브로커 기능보다 계약 설계와 운영 규율에 더 가깝습니다.

이벤트는 시스템 계약입니다

가장 중요한 판단은 이벤트를 내부 구현 디테일로 볼 것인지, 다른 시스템이 의존할 수 있는 계약으로 볼 것인지입니다.

실전 시스템에서는 대개 이벤트를 계약으로 다뤄야 합니다.

  • 이름은 비즈니스 사실을 반영해야 함
  • payload는 downstream이 이해할 수 있는 의미를 담아야 함
  • 소비자가 늘기 전에 버전 전략을 정해야 함
  • 스키마 변경은 점진적으로 배포해야 함

UserUpdated, RowChanged 같은 모호한 이벤트는 소비자에게 의미 추론 책임을 떠넘기게 됩니다.

Topic과 Partition은 비즈니스 의미를 가져야 합니다

Kafka topic은 단순 전송 채널이 아니라 이벤트 종류와 보관 정책의 경계입니다. Partition은 단순 확장성 단위가 아니라 순서 보장의 단위입니다.

그래서 partition key를 정할 때는 최소한 다음을 답해야 합니다.

  • 어떤 엔터티가 순서를 필요로 하는가
  • 어느 정도 병렬성이 필요한가
  • hot key로 인한 skew 위험은 없는가

좋은 partition 설계는 비즈니스가 필요한 순서는 지키고, 시스템이 활용할 수 있는 병렬성은 열어둡니다.

DB 변경과 이벤트 발행 경계는 명시적으로 풀어야 합니다

Kafka 시스템에서 가장 위험한 가정 중 하나는 “DB 쓰기와 Kafka 발행은 보통 같이 성공하겠지”입니다.

실무에서는 보통 Outbox 패턴이 가장 현실적인 해법입니다.

  • 비즈니스 상태와 outbox 이벤트를 한 DB 트랜잭션으로 저장
  • 별도 relay가 Kafka로 비동기 발행
  • relay 지연과 실패를 모니터링

이 경계를 풀지 않으면, 서비스 내부 상태와 외부에 공개된 이벤트 사이의 침묵하는 불일치가 결국 발생합니다.

Consumer는 멱등해야 합니다

at-least-once 전달을 쓰는 이상, 중복 메시지는 정상 상황입니다.

그래서 소비자는 다음을 만족해야 합니다.

  • 메시지 식별자를 기준으로 중복을 감지
  • side effect를 한 번만 안전하게 적용
  • 일시 오류와 비즈니스 거절을 구분
  • replay 상황에서도 상태를 오염시키지 않음

중복을 견디지 못하는 consumer는 브로커 구성이 아무리 깔끔해도 운영 준비가 된 것이 아닙니다.

Replay는 비상 수단이 아니라 기능입니다

Kafka의 강점 중 하나는 retained event를 다시 읽어 상태를 재구축할 수 있다는 점입니다. 하지만 replay는 미리 준비된 시스템에서만 진짜 기능이 됩니다.

replay 친화적인 시스템은 보통 다음 특징을 가집니다.

  • deterministic한 consumer 로직
  • 멱등한 side effect
  • 명확한 버전 전략
  • backfill과 offset 관리 도구
  • replay 중 lag와 오류 급증을 볼 수 있는 관측 체계

replay를 “정말 큰일 날 때만 하는 작업”으로 보면, 정작 필요할 때 실패하기 쉽습니다.

DLT는 쓰레기통이 아니라 운영 제어 장치입니다

Dead-letter topic은 반복 실패 메시지를 격리하는 데 유용하지만, 해결되지 않은 문제를 쌓아두는 장소가 되어서는 안 됩니다.

건강한 DLT 운영에는 보통 다음이 필요합니다.

  • 실패 원인 분류
  • malformed payload와 transient infrastructure issue 구분
  • 누가 조사하고 어떻게 replay할지 절차 정의
  • correlation ID와 원본 메타데이터 보존

이 규율이 없으면 DLT는 그냥 방치된 정합성 적체가 됩니다.

실제로 봐야 하는 지표

Kafka 운영 성공을 클러스터 상태만으로 판단하면 위험합니다. 브로커가 살아 있다고 해서 애플리케이션 정합성이 건강한 것은 아닙니다.

최소한 다음은 봐야 합니다.

  • consumer lag
  • rebalance 빈도
  • producer error rate
  • retry와 dead-letter 유입량
  • consumer group별 처리 지연
  • hot partition skew

이 지표가 실제 이벤트 흐름이 건강한지 보여줍니다.

흔한 아키텍처 실수

다음 패턴은 실제 운영에서 자주 문제를 만듭니다.

  • 테이블 변경을 그대로 노출한 이벤트 발행
  • topic 전체 또는 모든 partition에 글로벌 순서가 있다고 가정
  • DB/event atomicity를 풀지 않은 채 Kafka만 붙임
  • 선언되지 않은 필드 의미에 consumer가 의존
  • replay와 DLT 처리를 수동 영웅 작업에 맡김

이것들은 브로커 실패가 아니라 설계 실패입니다.

체크리스트

설계가 성숙했다고 말하려면 최소한 다음이 확인되어야 합니다.

  • 이벤트 이름이 도메인 사실을 반영하는가
  • partition key가 순서 요구를 반영하는가
  • outbox 또는 동등한 경계 해법이 있는가
  • consumer가 멱등한가
  • replay 절차가 검증되었는가
  • lag, rebalance, DLT, skew를 관측하는가

마무리

좋은 Kafka 설계는 브로커 자체보다, 이벤트를 계약으로 다루고 순서를 의도적으로 선택하며 replay와 중복을 정상 운영 조건으로 받아들이는 규율에서 갈립니다.

그것이 비동기 시스템을 단순한 메시지 전송이 아니라 신뢰 가능한 이벤트 아키텍처로 만드는 기준입니다.

Continue Reading

다음으로 읽기 좋은 글

다음 탐색

이 주제를 시스템 관점으로 더 이어서 보기