핵심 인사이트 (3줄 요약)

  1. 본질: 이벤트 버스와 퍼블리시-서브스크라이브(Publish-Subscribe) 패턴은 발행자(Publisher)가 구독자(Subscriber)를 직접 알지 않고 이벤트 채널이나 브로커를 통해 알림을 전달하는 느슨한 결합의 구조다.
  2. 가치: 기능을 추가할 때 발행자 코드를 수정하지 않고 새로운 구독자를 붙일 수 있어, 모듈 확장성과 비동기 반응 처리에 강하다.
  3. 판단 포인트: 그러나 제어 흐름이 숨어버리기 쉬워 요청-응답, 강한 순서 보장, 즉시 실패 전파가 필요한 업무에는 남용하면 안 되며, 계약(schema)·관측성·멱등성 설계가 함께 필요하다.

Ⅰ. 개요 및 필요성

이벤트 버스 패턴은 "누가 반응할지 몰라도 어떤 일이 발생했다는 사실만 알리자"는 생각에서 출발한다. 주문 서비스가 주문 생성 후 재고 차감, 알림 발송, 분석 적재, 포인트 적립까지 모두 직접 호출하면, 처음에는 단순해 보여도 시간이 갈수록 호출 대상이 늘고 장애 전파 범위도 커진다. 결국 발행자는 비즈니스 핵심 행위보다 후속 연쇄 호출을 더 많이 알아야 하는 상태가 된다.

퍼블리시-서브스크라이브 패턴은 이 문제를 완화한다. 발행자는 OrderPlaced 같은 이벤트만 내보내고, 누가 그 이벤트를 소비할지는 이벤트 버스나 메시지 브로커가 중개한다. 그래서 새로운 후속 기능이 생겨도 발행자 코드를 자주 건드리지 않아도 된다. 이 점 때문에 플러그인 구조, 화면 이벤트, 마이크로서비스 간 통합, 비동기 알림에 널리 쓰인다.

다만 여기서 바로 조심해야 할 점도 있다. 이벤트를 쓰는 순간 제어 흐름이 코드 한 줄에서 보이지 않게 숨어 들어간다. 그래서 설계자는 "직접 호출을 줄였다"는 장점만 보지 말고, "이제 누가 언제 반응하는지 추적하기 더 어려워졌다"는 새로운 비용도 함께 봐야 한다.

  • 📢 섹션 요약 비유: 이벤트 버스는 발표자가 참석자 전원을 직접 부르지 않고 방송만 내보내는 방식이라 편하지만, 누가 실제로 그 방송을 듣고 움직였는지는 따로 확인 체계가 필요하다.

Ⅱ. 아키텍처 및 핵심 원리

이벤트 버스는 크게 인프로세스(In-Process) 형태와 브로커 기반 분산 형태로 나뉜다. 전자는 하나의 애플리케이션 내부 모듈 간 이벤트 전달이고, 후자는 Kafka, RabbitMQ 같은 브로커를 사이에 둔 서비스 간 이벤트 전달이다. 둘 다 발행자와 구독자를 분리하지만, 전달 보장, 지연시간, 재처리, 장애 복구 방식은 크게 다르다.

아래 그림은 대표적인 이벤트 흐름을 요약한다.

┌──────────────────────────────────────────────────────────────────────┐
│ Event Bus reference flow                                            │
├──────────────────────────────────────────────────────────────────────┤
│ OrderService publishes OrderPlaced(v1)                              │
│        │                                                            │
│        ▼                                                            │
│ [Event Bus / Broker / Topic: order.events]                          │
│        │ schema + routing key                                       │
│        ├─ InventorySubscriber   -> reserve stock                    │
│        ├─ NotificationSubscriber -> send message                    │
│        └─ AnalyticsSubscriber   -> update dashboard                 │
│                                                                      │
│ failure path: retry -> DLQ (Dead Letter Queue) -> alert / replay    │
└──────────────────────────────────────────────────────────────────────┘

핵심 구성 요소는 이벤트 계약(Event Contract), 채널(Topic/Channel), 전달 메커니즘, 구독자 처리기, 실패 처리다. 특히 분산 환경에서는 "이벤트 이름"보다 이벤트 스키마와 버전이 더 중요하다. 이벤트는 보통 과거형 사실(OrderPlaced, PaymentFailed)로 정의하고, 구독자는 이를 읽어 자기 책임 범위의 상태를 갱신한다. 이때 재시도와 중복 소비가 흔하므로, 구독 로직은 멱등성(Idempotency)을 확보해야 한다.

구성 요소역할설계 포인트
Publisher이벤트 발생 사실을 발행구독자 존재를 몰라야 결합도가 낮아짐
Event Contract이벤트 이름, 필드, 버전 정의스키마 진화와 하위 호환성 확보
Event Bus / Broker라우팅과 전달 중개인프로세스 vs 분산 브로커 구분 필요
Subscriber이벤트를 소비해 후속 작업 수행멱등성, 재시도, 장애 격리 필수
Topic / Channel관심사별 분류 통로너무 거칠면 잡음, 너무 세밀하면 운영 복잡
DLQ (Dead Letter Queue)실패 이벤트 격리무한 재시도 방지와 수동 재처리 근거

따라서 이벤트 버스의 핵심 원리는 "한 번의 사실을 여러 반응으로 확장하되, 각 반응의 책임은 분리한다"는 데 있다. 다만 이 구조가 유효하려면 이벤트 계약이 안정적이어야 하고, 소비 실패가 전체 트랜잭션 실패와 동일하지 않다는 점을 팀이 이해해야 한다.

  • 📢 섹션 요약 비유: 이벤트 버스는 방송국이 같은 소식을 여러 채널 청취자에게 뿌리는 구조와 같아서, 방송 규격과 재송출 규칙이 없으면 오히려 더 큰 혼란이 난다.

Ⅲ. 비교 및 연결

이벤트 버스는 Observer 패턴, 메시지 큐, 이벤트 소싱(Event Sourcing)과 자주 혼동된다. 하지만 이들은 비슷해 보여도 해결하는 문제가 다르다. Observer는 보통 같은 프로세스 안에서 객체 간 알림을 다루고, 이벤트 버스/브로커형 Pub/Sub은 더 느슨한 결합과 비동기 확장을 목표로 한다. 메시지 큐는 주로 작업 분산과 경쟁 소비에 초점이 있고, 이벤트 소싱은 상태 변경 자체를 이벤트로 영속 저장하는 저장 모델이다.

비교 항목ObserverEvent Bus / Pub-SubWork QueueEvent Sourcing
주 목적객체 간 알림느슨한 결합의 사실 전파작업 분배상태 변경의 영속 기록
결합도상대적으로 높음낮음중간높을 수도 낮을 수도 있음
전달 대상등록된 리스너다수 구독자보통 한 작업자 그룹이벤트 저장소와 재생 로직
처리 방식동기/로컬 중심비동기 확장에 강함경쟁 소비이벤트 재생으로 상태 복원
대표 위험직접 참조 증가숨은 의존성재시도/중복 처리모델 복잡도 증가

설계감리에서 자주 나오는 질문은 "이벤트 버스가 만능인가"다. 답은 아니다. 사용자가 버튼을 눌렀을 때 즉시 결제 승인 결과를 받아야 하는 요청-응답 업무는 RPC (Remote Procedure Call)나 명시적 API 호출이 더 적합하다. 반면 주문 완료 후 알림, 로그 적재, 통계 집계, 검색 인덱스 갱신처럼 후행 반응은 이벤트 기반으로 분리할 때 효과가 크다.

즉 이벤트 버스는 직접 호출을 없애는 패턴이 아니라, 직접 호출이 꼭 필요하지 않은 반응을 분리하는 패턴으로 이해해야 한다. 이 경계를 흐리면 시스템은 겉보기엔 느슨하게 연결됐지만, 실제로는 숨은 의존성으로 가득 찬 복잡한 구조가 된다.

  • 📢 섹션 요약 비유: Observer가 같은 교실 안 손들기라면, Event Bus는 학교 방송이고, Work Queue는 청소 당번표이며, Event Sourcing은 학교에서 일어난 일을 전부 적는 생활기록부에 가깝다.

Ⅳ. 실무 적용 및 기술사 판단

이벤트 버스는 특히 "핵심 업무 한 번에 후속 반응이 여러 개 붙는 상황"에서 좋다. 예를 들어 주문 생성 후 재고, 포인트, 알림, 데이터 웨어하우스 적재가 각각 독립 책임이라면, 주문 서비스는 주문 완료 이벤트만 발행하고 나머지 서비스가 자율적으로 반응하게 할 수 있다. 이런 구조는 신규 구독자 추가가 쉬워 마이크로서비스와 플러그인 아키텍처에서 자주 선택된다.

그러나 설계자는 두 가지를 항상 확인해야 한다. 첫째, 그 반응이 정말 비동기여도 되는가? 둘째, 구독 실패 시 발행자의 성공을 어떻게 해석할 것인가? 예를 들어 "주문 생성은 성공했지만 알림 서비스는 나중에 재시도"가 허용되는 업무라면 적합하다. 반대로 "모든 후속 처리가 같은 트랜잭션 안에 반드시 끝나야 한다"면 이벤트 버스보다 명시적 오케스트레이션이 더 낫다.

실무 상황권장 판단이유
주문 완료 후 알림·분석·검색 갱신적극 적용후속 반응이 독립적이고 확장 가능
플러그인/모듈 훅 구조적극 적용발행자 수정 없이 기능 확장 가능
결제 승인처럼 즉시 결과 필요신중 적용 또는 직접 호출요청-응답이 더 명확함
강한 순서·단일 트랜잭션 요구대안 검토결국 숨은 동기 의존성으로 변질될 수 있음

실무 체크리스트

  1. 이벤트 이름이 과거형 사실로 정의되어 있는가? 예: OrderPlaced, UserRegistered.
  2. 이벤트 계약 버전 관리와 하위 호환성 전략이 있는가?
  3. 구독자는 중복 소비에도 안전한 멱등 처리를 하는가?
  4. 이벤트 추적을 위해 Correlation ID와 모니터링 대시보드가 있는가?
  5. 실패 이벤트를 격리할 DLQ와 재처리 절차가 있는가?

자주 발생하는 안티패턴

  • 모든 서비스 호출을 무조건 이벤트로 바꾸는 "보이지 않는 RPC" 설계
  • system.updated 같은 지나치게 포괄적인 이벤트 이름으로 의미를 흐리는 경우
  • 이벤트 계약 소유자가 없어 필드가 제멋대로 바뀌는 경우
  • 구독자 멱등성 없이 재시도를 걸어 중복 적립·중복 발송이 발생하는 경우
  • 관측성 없이 이벤트 흐름을 숨겨 장애 분석이 불가능해지는 경우

기술사 답안에서는 이벤트 버스의 장점만 쓰면 부족하다. 느슨한 결합, 확장성, 비동기화와 함께 숨은 제어 흐름, 스키마 관리, 장애 추적 난이도를 같이 적어야 현실적인 설계 판단으로 보인다. 특히 "이벤트는 사실 전달에 쓰고, 명령과 즉시 응답은 별도 경로로 분리한다"는 문장을 남기면 좋다.

  • 📢 섹션 요약 비유: 이벤트 버스를 잘 쓰는 조직은 방송과 전화의 용도를 구분하는 조직이고, 못 쓰는 조직은 긴급 지시도 방송으로만 해서 누가 들었는지 모르는 조직과 같다.

Ⅴ. 기대효과 및 결론

이벤트 버스와 퍼블리시-서브스크라이브 패턴의 가장 큰 효과는 기능 확장 비용을 낮춘다는 점이다. 발행자는 핵심 행위를 수행한 뒤 사실만 알리고, 구독자는 자기 책임 범위에서 필요한 후속 처리를 붙인다. 이 구조는 서비스 독립 배포, 플러그인 확장, 비동기 처리량 향상, 장애 격리 측면에서 장점이 크다.

하지만 이 장점은 계약과 관측성이 뒷받침될 때만 유지된다. 이벤트가 많아질수록 누가 어떤 이벤트를 소비하는지, 순서는 어떤지, 실패 시 재처리를 어떻게 하는지, 데이터 일관성은 어느 수준까지 허용하는지 명확해야 한다. 그렇지 않으면 이벤트 버스는 느슨한 결합의 도구가 아니라 "이유를 찾기 어려운 시스템"이 되기 쉽다.

결론적으로 기억할 문장은 간단하다. 이벤트 버스는 관심 있는 사실을 널리 알리는 패턴이지, 모든 협업을 숨겨서 해결하는 만능 통로가 아니다. 설계감리에서는 발행자와 구독자의 분리 수준만이 아니라, 이벤트 계약·멱등성·관측성까지 함께 보아야 한다.

  • 📢 섹션 요약 비유: 이벤트 버스는 잘 설계된 방송국이면 도시를 효율적으로 움직이게 하지만, 방송표와 기록이 없으면 누가 무엇을 들었는지 모르는 소음원이 된다.

📌 관련 개념 맵

개념연결 포인트
EDA (Event-Driven Architecture)이벤트 버스 패턴이 넓게 포함되는 상위 아키텍처 스타일이다.
Observer Pattern같은 프로세스 안의 알림 구조로, Event Bus와 경계 비교가 중요하다.
Message Broker분산 Pub/Sub에서 이벤트 라우팅과 전달을 담당한다.
Schema Registry이벤트 계약 버전과 호환성을 관리하는 운영 기반이다.
Idempotency재시도와 중복 소비를 안전하게 처리하는 핵심 성질이다.
CQRS (Command Query Responsibility Segregation) / Event Sourcing이벤트 기반 읽기 모델 분리와 영속 이벤트 저장으로 확장되는 연관 개념이다.

📈 관련 키워드 및 발전 흐름도

직접 호출 기반 결합
    │
    ▼
Observer / local event notification
    │
    ▼
Event Bus / Publish-Subscribe
    │
    ├─ schema versioning
    ├─ retry / DLQ
    └─ observability / correlation ID
    │
    ▼
Event-Driven Architecture · CQRS 확장

이 흐름은 알림 구조가 단순 객체 간 통지에서 시작해, 계약과 운영 체계를 갖춘 분산 이벤트 아키텍처로 발전하는 과정을 보여 준다.

👶 어린이를 위한 3줄 비유 설명

  1. 이벤트 버스는 반장이 친구들 이름을 하나씩 부르지 않고 교실 방송으로 "숙제 나왔어"라고 알려 주는 거예요.
  2. 숙제가 필요한 친구만 듣고 움직이니까 반장은 모두를 직접 챙기지 않아도 돼요.
  3. 대신 누가 방송을 들었는지 적어 두지 않으면 나중에 왜 숙제가 안 됐는지 찾기 어려워져요.