Developer Log
Backend

RabbitMQ 도입으로 서비스 간 결합도 낮추기

서비스를 여러 개로 나누었다고 해서 자동으로 느슨하게 결합되는 것은 아닙니다. Vantage에서도 크롤링, 요약, 논문 처리, 브리핑 생성 기능이 늘어나면서 각 서비스가 상대방의 처리 순서와 작업 상태를 더 많이 알아야 했습니다.

메시지 큐를 도입한 핵심 이유는 장애 복구 자체보다 서비스 간 결합도가 높아진 문제를 해결하기 위해서였습니다.

이전 구조에서 불편했던 점

처리 단계가 늘어날수록 어느 서비스가 다음 작업을 호출하고, 누가 완료 여부를 확인하며, 실패 상태는 어디에서 관리할지가 복잡해졌습니다.

  • 요청자가 다음 서비스의 실행 세부 사항을 알아야 했습니다.
  • 새로운 단계를 추가할 때 여러 서비스의 흐름을 함께 수정해야 했습니다.
  • 작업 이력과 실제 실행 책임이 뒤섞여 변경 범위를 예측하기 어려웠습니다.

문제의 본질은 단순한 처리 속도가 아니라 서비스의 책임 경계가 흐려진 것이었습니다.

Command와 Event를 구분했습니다

Command는 특정 서비스에 작업 수행을 요청합니다. 예를 들어 브리핑을 생성하라는 명령은 담당 소비자가 처리합니다.

Event는 이미 발생한 사실을 알립니다. 처리가 끝났다면 완료 이벤트를 발행하고, 이를 필요로 하는 서비스가 자신의 읽기 모델이나 상태를 갱신합니다.

이렇게 분리하면 요청 서비스가 완료 이후의 모든 후속 작업까지 직접 호출하지 않아도 됩니다.

Outbox와 Inbox를 둔 이유

DB 상태를 변경하는 작업과 메시지 발행을 별개로 처리하면 어느 한쪽만 성공하는 상황이 생길 수 있습니다.

트랜잭셔널 Outbox를 사용해 비즈니스 데이터 변경과 발행할 메시지 기록을 같은 트랜잭션 경계 안에 둡니다. 이후 발행기가 메시지를 전송합니다.

소비자 쪽에서는 Inbox와 멱등성 키로 중복 전달을 처리합니다. 메시지 시스템은 재전송이 발생할 수 있기 때문에 정확히 한 번 전달된다고 가정하지 않는 것이 중요합니다.

실패 흐름도 계약으로 다뤘습니다

지연 재시도 큐와 DLQ를 두고, 작업별 상태와 오류 이력을 관찰할 수 있게 했습니다. 핵심은 큐가 있다는 사실보다 실패했을 때 어떤 상태로 남는지, 누가 재시도를 담당하는지 일관되게 정의하는 것입니다.

모든 파이프라인을 한 번에 메시지 기반으로 바꾸지는 않았습니다. Vantage의 기사 처리에는 DB 작업 상태와 코디네이터가 담당하는 구간도 있습니다. 업무 성격에 맞춰 기존 상태 관리와 메시지 기반 작업 전달을 구분했습니다.

배운 점

메시지 큐는 단순히 HTTP 호출을 다른 전송 수단으로 바꾸는 도구가 아닙니다. 서비스가 무엇을 책임지는지, 어떤 사실을 외부에 알리는지, 중복과 실패를 어떻게 처리할지 다시 설계하게 만듭니다.

Vantage에서는 RabbitMQ 도입을 통해 기능 간 연결을 명령과 이벤트 계약으로 표현하는 방향을 선택했습니다. 앞으로는 메시지 흐름의 관측 가능성과 계약 변경 관리도 계속 다듬을 계획입니다.