소프트웨어 아키텍처

이벤트 기반 아키텍처

meellon 2026. 6. 25. 05:20

학습 목표

이벤트 기반 아키텍처(EDA)느슨한 결합·비동기를 만드는 방식임을 설명할 수 있다.

pub/sub이벤트 스트리밍의 차이를 설명할 수 있다.

event notificationevent-carried state transfer를 구분할 수 있다.

CQRS가 명령·조회 분리로 읽기·쓰기를 다르게 모델링함을 설명할 수 있다.

문제 상황

  • 주문 생성 API가 재고·결제·알림·포인트를 순서대로 REST 호출한다
    • 한 곳 503이면 전체 실패, latency는 합산
  • 재고 서비스가 주문 API 스키마 변경에 매번 끌려간다
    • 동기 결합 + 공유 DTO
  • "알림만 추가"인데 주문 팀이 배포해야 한다
    • 새 consumer마다 producer 수정
  • 조회 API가 쓰기 DB에 무거운 JOIN을 건다
    • 읽기 트래픽이 주문 처리를 느리게
  • 이벤트를 도입했는데 순서·중복 처리가 없어 데이터가 어긋난다
    • at-least-once 전제 설계 없음

MSA에서 동기 체인·async queue를 봤다. 이번 편은 이벤트를 1급 시민으로 두는 EDA다.

1. EDA란

이벤트 기반 아키텍처 — 상태 변화를 이벤트로 발행하고, 관심 있는 consumer비동기로 반응한다.

개념 의미
Event 과거에 일어난 사실 (OrderPlaced, PaymentCaptured)
Producer 이벤트 발행 (publisher)
Consumer 이벤트 구독·처리 (subscriber)
Broker 이벤트 중개 (Kafka, RabbitMQ, SNS/SQS)
  • Smart endpoints, dumb pipes — MSA와 맞물림: pipe는 전달만, 비즈니스는 endpoint
  • Temporal decoupling — producer·consumer가 동시에 떠 있을 필요 없음

2. 동기 체인 vs 이벤트

  동기 REST 체인 이벤트 EDA
결합 호출 순서·응답 강결합 broker 경유 약결합
장애 중간 실패 → 전파 consumer 독립 재시도
확장 새 기능 = caller 수정 subscriber 추가
일관성 즉시 강한 일관성 쉬움 ** eventual consistency**
디버깅 호출 스택 추적 correlation ID·트레이스 필요
# 동기 (앞서 Order service 예)
Order → Payment → Inventory → Notification  (4 hops)

# 이벤트
Order → publish OrderPlaced
          ├→ Inventory (reserve)
          ├→ Notification (email)
          └→ Analytics (report)
  • 핵심 경로는 동기 + 부가 기능은 이벤트 — 하이브리드가 흔함

3. Pub/Sub과 이벤트 스트리밍

Message queue (pub/sub)

  • Topic/Queue에 publish → 한 consumer 또는 fan-out (SQS+SNS, RabbitMQ)
  • 메시지 처리 후 삭제 (또는 TTL)
  • 작업 분배·알림·백그라운드 job에 적합

Event streaming (log)

  • Append-only log — Kafka, Pulsar (OrderPlaced offset 1024…)
  • 보존 기간 동안 재생(replay) 가능
  • 여러 consumer group독립 offset으로 읽음
  • CDC·분석·CQRS projection에 강함
  Queue Stream (log)
소비 처리하면 사라짐 offset으로 재읽기
순서 보통 파티션/단일 consumer partition key 순서
용도 task queue, notify event sourcing, stream processing
  • Kafka = 스트리밍 플랫폼 + pub/sub semantics

4. 이벤트 설계

Event notification vs event-carried state

유형 payload consumer 동작
Notification ID·타입만 (orderId: 9001) API/DB 조회로 상세 확보
Carried state 필요한 필드 포함 (items, amount) 조회 없이 처리 가능
{
  "type": "OrderPlaced",
  "eventId": "evt-8f2a",
  "occurredAt": "2026-06-25T06:00:00Z",
  "correlationId": "abc-123",
  "data": {
    "orderId": "9001",
    "userId": "42",
    "totalAmount": 12900,
    "items": [{ "sku": "A1", "qty": 2 }]
  }
}
  • 전체 예시는 code/order-placed-event.json 참고
  • 과거 사실OrderPlaced수정하지 않음, 취소는 OrderCancelled 새 이벤트
  • 스키마 버전·호환 (필드 추가는 관대하게, 삭제는 신중)

Idempotency·순서

이슈 대응
중복 전달 eventId 또는 business key로 멱등 처리
순서 같은 orderId같은 partition key
지연 consumer 시간 무관 설계 (재생 대비)

5. CQRS 개요

CQRS (Command Query Responsibility Segregation) — 쓰기 모델읽기 모델을 분리한다.

역할
Command 상태 변경 PlaceOrder, CancelOrder
Query 조회 GetOrderSummary, 검색·대시보드
Client ──command──> Order service (write DB)
                      │
                      └──event──> Projection updater ──> Read store (denormalized)
Client ──query───> Order read API ──> Read store
장점 비용
읽기 스케일·모델 최적화 ** eventual consistency**
쓰기 단순 트랜잭션 projection·동기화 운영
이벤트 log와 자연스럽게 연결 디버깅 복잡도
  • 모든 API에 CQRS는 과함 — 읽기·쓰기 부하·형태가 다를 때
  • Event sourcing(상태=이벤트 재생)은 CQRS와 자주 짝지만 필수는 아님

6. EDA + MSA 실무

패턴 용도
Outbox DB commit + 이벤트 publish 원자성
Saga 분산 트랜잭션 → 로컬 TX + 이벤트 + 보상
Dead letter queue poison message 격리
Schema registry Avro/JSON schema 버전 관리
Order service:
  1. INSERT order (local TX)
  2. INSERT outbox row (same TX)
  3. Relay publishes OrderPlaced to Kafka
Inventory consumer:
  4. consume · idempotent reserve · commit offset
  • Observability — correlationId로 publish~consume 추적
  • Platform — K8s에서 consumer도 Deployment로 독립 스케일

7. 트레이드오프·안티패턴

EDA 장점 EDA 비용
결합↓ · 탄력적 확장 분산 디버깅
피크 흡수 (buffer) ** eventual consistency**
새 기능 구독만 broker 운영·ordering
안티패턴 증상
Chatty events 1 API → 이벤트 50개
God topic 모든 이벤트 한 topic — schema 지옥
동기 disguised as async consumer가 blocking RPC 연쇄
이벤트 없이 CDC만 의미·버전 없는 row copy

8. 정리

  • EDA = 사실(event) 발행 + 관심 consumer의 비동기 반응
  • Queue vs stream — 재생·fan-out 요구에 따라 선택
  • notification vs carried state, 멱등·partition key 설계 필수
  • CQRS — command/query 분리, projection으로 읽기 최적화
  • MSA와 하이브리드 — 핵심은 동기, 부가·통합은 이벤트

다음에 다룰 것

  • 스타일 비교와 선택
  • 모놀리스 vs MSA vs EDA, 언제 무엇을 쓸지

해당 내용은 Fundamentals of Software Architecture (Mark Richards, Neal Ford), Designing Data-Intensive Applications (Martin Kleppmann) 의 내용을 기반으로 합니다.

'소프트웨어 아키텍처' 카테고리의 다른 글

SOLID 원칙  (0) 2026.06.27
아키텍처 스타일 비교와 선택  (0) 2026.06.26
마이크로서비스 아키텍처  (0) 2026.06.24
헥사고날과 클린 아키텍처  (3) 2026.06.23
레이어드 아키텍처  (0) 2026.06.22