서비스가 서로를 직접 호출하는 구조에서는 한 서비스가 느려지면 호출한 쪽도 함께 멈춘다. Event Driven Architecture는 이 결합을 끊기 위해 서비스 간 협력의 매개체를 "이벤트"로 바꾼다. 발생한 사실을 기록하고 흘려보내면, 관심 있는 서비스가 알아서 반응하는 방식이다.
Event는 시스템에서 발생한 사실을 나타내는 불변 메시지다. OrderPlaced, PaymentCompleted처럼 과거형 동사로 이름을 짓는다.
Producer는 이벤트를 생성해 Event Bus로 전달한다. 누가 이 이벤트를 받는지 알지 못한다.
Consumer는 관심 있는 이벤트를 구독해 처리한다. Producer와 직접 통신하지 않는다.
Event Bus는 이벤트를 수신하고 Consumer에게 전달하는 중간 인프라다. Kafka, RabbitMQ, AWS SNS/SQS 등이 이 역할을 맡는다.
[Order Service]
|
이벤트 발행 (OrderPlaced)
|
v
[ Event Bus ]
|
┌──┴──────────────┐
v v
[Inventory Service] [Notification Service]
재고 차감 이메일 발송
Message Queue는 일반적으로 1:1 통신이다. 메시지를 하나의 Consumer가 소비하면 사라진다. Pub/Sub 패턴은 1:N이다. 하나의 이벤트가 구독 중인 모든 Consumer에게 전달된다.
| 항목 | Message Queue | Pub/Sub |
|---|---|---|
| 수신자 | 1개 | 다수 |
| 소비 후 메시지 | 삭제 | 유지 가능 |
| 대표 도구 | RabbitMQ, SQS | Kafka, SNS |
| 주 용도 | 작업 분산 | 이벤트 브로드캐스트 |
Topic에 이벤트를 발행하면, 해당 Topic을 구독한 모든 서비스가 이벤트를 수신한다. Consumer는 독립적으로 처리하기 때문에 서로 영향을 주지 않는다.
// Producer — 이벤트 발행
await eventBus.publish("order.placed", {
orderId: "abc-123",
userId: "user-456",
amount: 39000,
});
// Consumer — 이벤트 구독
eventBus.subscribe("order.placed", async (event) => {
await inventoryService.decrease(event.orderId);
});
Topic: 이벤트를 분류하는 채널. Producer는 Topic에 발행하고 Consumer는 Topic을 구독한다.
장점
단점
Idempotency: 같은 작업을 여러 번 실행해도 결과가 동일한 성질. 중복 이벤트 처리 시 부작용을 막기 위해 필수다.
이커머스에서 주문이 완료되면 하나의 OrderPlaced 이벤트가 발행된다. 재고 서비스는 재고를 차감하고, 알림 서비스는 이메일을 보내고, 정산 서비스는 매출을 기록한다. 세 서비스는 서로를 모르고, Order Service도 이들을 직접 호출하지 않는다. 새로운 서비스가 필요하면 같은 Topic을 구독하는 Consumer를 추가하는 것으로 끝난다.