EDA로 이해하는 서비스 간 이벤트 흐름
EDA는 이벤트 발행과 구독을 통해 서비스 간 직접 의존을 줄이는 아키텍처다. 브로커 선택, 비동기 처리의 경계, 중복 처리와 유실 방지 같은 실무 판단 기준까지 함께 이해해야 한다.
Script Companion
오디오와 함께 스크립트 보기
- 01
EDA는 Event-Driven Architecture, 즉 이벤트 기반 아키텍처다. 핵심은 어떤 서비스에서 이벤트가 발생하면 다른 서비스들이 각자의 방식으로 반응한다는 구조다. 프론트엔드에서 React의 useEffect가 상태 변화에 반응하고, Redux 미들웨어가 액션 발생 시 부수 효과를 처리하는 것과 같은 반응형 패턴이 서비스 간 통신 수준으로 확장된 형태라고 볼 수 있다. DOM의 element.addEventListener가 NestJS의 @OnEvent가 되고, 다시 SQS Consumer로 확장되는 흐름이다.
- 02
EDA의 기본 구성요소는 Publisher, Event Broker, Subscriber 세 가지다. Publisher는 이벤트를 발행하고 바로 다음 작업을 진행하며 누가 구독하는지는 신경 쓰지 않는다. Event Broker는 이벤트를 받아 저장하고 구독자에게 전달하는 중계자다. Subscriber는 자신이 관심 있는 이벤트만 받아 처리하고, 이벤트를 누가 발행했는지 몰라도 된다. 식당 주문으로 보면 손님이 주문서를 창구에 꽂고, 조리팀과 음료팀과 서빙팀이 각자 필요한 주문을 가져가 처리하는 구조와 비슷하다.
- 03
브로커가 필요한 이유는 느슨한 결합과 내구성이다. Publisher가 Subscriber를 직접 호출하면 모든 Subscriber의 주소나 큐 이름을 알아야 해서 결합도가 올라간다. 또 Subscriber가 다운된 순간 이벤트가 유실될 수 있다. 브로커를 두면 Publisher는 브로커에만 발행하면 되고, 브로커는 Subscriber가 복구될 때까지 이벤트를 보관할 수 있다. SNS와 SQS 조합은 한 이벤트에 여러 반응을 만들면서, 느린 소비자를 응답 경로에서 분리하는 데 적합하다.
- 04
Queue와 Pub/Sub는 비슷해 보여도 목적이 다르다. Queue는 하나의 메시지를 하나의 Consumer가 처리하는 1:1 구조라서 작업 분산과 비동기 Worker 패턴에 잘 맞는다. Pub/Sub는 같은 이벤트를 여러 Subscriber가 각자 처리하는 1:N 구조라서 이벤트 브로드캐스트에 맞다. AWS 기준으로 SQS는 pull 모델과 메시지 보존, SNS는 push 기반 팬아웃, EventBridge는 규칙 매칭과 콘텐츠 기반 필터링이 핵심 차이다.
- 05
EventBridge는 이벤트 내용으로 조건별 라우팅을 할 때 강점이 있다. SNS나 SQS가 모든 메시지를 전달하는 쪽에 가깝다면, EventBridge는 조건에 맞는 메시지만 특정 타깃으로 보낼 수 있다. 그래서 새 처리 조건이 생겨도 Publisher 코드를 바꾸지 않고 규칙을 추가하는 방식으로 대응할 수 있다. 문서에서는 2025년에 EventBridge의 향상된 로깅과 Cross-Account 직접 전달이 추가되어, 성공과 실패와 재시도를 더 자세히 추적하고 다른 AWS 계정의 SQS Queue로 직접 이벤트를 보낼 수 있다고 설명한다.
- 06
AWS 관리형 브로커만 선택지가 되는 것은 아니다. Kafka는 고처리량 이벤트 스트리밍과 로그 기반 저장에 강하고, 로그 보존으로 오프셋 리셋을 통한 메시지 재처리가 가능하다. RabbitMQ는 유연한 메시지 라우팅과 낮은 지연이 중요할 때 고려된다. SQS와 SNS는 AWS 환경에서 운영 부담을 줄이고 싶을 때 기본 선택지가 되기 쉽다. 다만 SQS FIFO는 기본 batching 기준 초당 3,000 messages per API method라는 기준이 있으므로, 무제한 고처리량 스트림으로 오해하면 안 된다.
- 07
EDA를 선택하지 말아야 할 때도 분명하다. 판단 기준은 이 작업이 사용자 응답이나 원자적 결정에 필요한가이다. 결제 승인 여부를 화면에 즉시 보여줘야 한다면 payment.approved 이벤트를 기다리는 구조보다 결제 API를 동기 호출하고, 성공 후 order.paid 이벤트를 발행하는 편이 단순하다. 강한 ACID 트랜잭션이 필수이거나, 소규모 모놀리스라서 브로커 운영 복잡성이 더 크거나, 서브밀리초 저지연이 필요한 워크로드라면 EDA가 과할 수 있다.
- 08
EDA에서 중요한 실패 지점은 DB 저장과 이벤트 발행 사이의 틈이다. 주문은 DB에 저장됐지만 이벤트 발행이 실패하면 데이터 불일치가 생기고, 반대로 이벤트는 발행됐는데 DB 트랜잭션이 롤백되면 구독자만 이미 처리한 상태가 된다. Outbox Pattern은 DB 트랜잭션 안에서 이벤트 레코드를 함께 저장하고, 별도 프로세스가 그 레코드를 읽어 발행하는 방식으로 이 문제를 줄인다. L6 수준에서는 DB 저장과 이벤트 발행을 원자적으로 묶어야 유실이 줄어든다는 점이 핵심이다.
- 09
Event Sourcing은 EDA의 다음 단계로 볼 수 있다. 현재 상태만 저장하는 대신 order.created, order.paid, order.shipped, order.delivered 같은 이벤트 시퀀스를 저장하고, 상태를 이벤트 재생으로 복원하는 방식이다. 이벤트 자체가 감사 로그가 되고 특정 시점 상태로 되돌리는 시간여행 디버깅도 가능하다. 하지만 이벤트 스키마 버전 관리, Projection 지연에 따른 최종 일관성, CQRS와의 결합 설계가 따라오므로 금융, 의료, 법률처럼 감사 이력이 필수인 도메인이 아니라면 단순 CRUD에 도입하기에는 과한 엔지니어링이 될 수 있다.
- 10
실무 트러블슈팅에서는 증상과 원인을 분리해서 봐야 한다. @OnEvent 리스너가 실행되지 않으면 EventEmitterModule.forRoot 등록, Service providers 등록, 이벤트 이름 대소문자와 오타를 확인한다. SQS에서 동일 이벤트가 중복 처리되면 at-least-once 전달 특성 때문에 같은 메시지가 다시 올 수 있으므로 orderId나 이벤트 ID 기준으로 멱등성을 구현한다. EventBridge에서 규칙 지표는 증가하는데 타깃이 실행되지 않으면 권한, 이벤트 패턴, FailedInvocations, 패턴 테스트를 함께 확인해야 한다.
- 11
정리하면 EDA는 이벤트 발행과 구독으로 서비스 간 느슨한 결합을 만드는 구조다. Publisher는 누가 구독하는지 몰라도 되고, Subscriber는 누가 발행했는지 몰라도 된다. Queue는 작업 처리, Pub/Sub는 이벤트 브로드캐스트에 어울리며, SQS와 SNS와 EventBridge, Kafka와 RabbitMQ는 각각 다른 선택 기준을 가진다. 다만 비동기 구조는 중복 처리, 최종 일관성, 이벤트 유실 가능성을 함께 가져오므로 Outbox Pattern과 멱등성 같은 보완책까지 같이 봐야 한다.
같은 레이어
L6에서 이어 듣기
- 오디오 파일
- /podcasts/l6-eda-basics.mp3