Event-driven APIs vs. Pub and Sub APIs

핵심 개념 요약

구분Pub/Sub APIsEvent-Driven APIs
정의토픽 기반 메시지 브로커 시스템상태 변화/이벤트 발생 시 신호 전달 시스템
주요 목적생산자-소비자 간 비동기 메시징실시간 이벤트 기반 시스템 반응성 향상
표준 구현 예시Google Cloud Pub/Sub, Apache KafkaAWS EventBridge, Webhook, MQTT

Pub/Sub API (발행-구독 API)

Pub/Sub 패턴은 메시지 발행자(Publisher)와 구독자(Subscriber) 사이의 느슨한 결합을 제공하는 메시징 패러다임이다. 발행자는 특정 주제(Topic)에 메시지를 발행하고, 해당 주제를 구독한 모든 구독자는 이 메시지를 수신한다. 이 과정에서 발행자와 구독자는 서로에 대해 직접적인 정보를 알 필요가 없다.

Event-Driven API (이벤트 기반 API)

이벤트 기반 API는 시스템 내에서 발생하는 상태 변화나 중요한 사건(이벤트)을 중심으로 설계된다. 클라이언트는 특정 이벤트에 대한 리스너를 등록하고, 해당 이벤트가 발생하면 서버가 알림을 보낸다. 이를 통해 실시간 반응형 시스템을 구축할 수 있다.

아키텍처 및 동작 방식 비교

항목Pub/Sub APIsEvent-Driven APIs
통신 모델토픽 중심 발행-구독이벤트 트리거 기반 푸시 알림
데이터 흐름메시지 단위 배치 처리상태 변화 시 즉각적 스트리밍
결합도완전 분리 (브로커 의존)부분 결합 (이벤트 소스와 직접 연결)
메시지 보관브로커에서 일정 기간 저장이벤트 발생 후 즉시 소멸 (옵션 저장)

아키텍처

Pub/Sub API 아키텍처

Pub/Sub 시스템은 일반적으로 세 가지 주요 구성 요소로 이루어진다:

  1. 발행자(Publisher): 메시지를 생성하여 특정 주제(Topic)에 발행한다.
  2. 브로커(Broker): 메시지를 수신하고 저장하며, 관련 구독자에게 전달한다.
  3. 구독자(Subscriber): 관심 있는 주제를 구독하고 해당 주제에 발행된 메시지를 수신한다.
1
2
3
4
5
[발행자1] ───┐                  ┌─── [구독자1]
             │                  │
[발행자2] ─── ├─→ [메시지 브로커] ─┼─→ [구독자2]
             │                  │
[발행자3] ───┘                  └─── [구독자3]
Event-Driven API 아키텍처

이벤트 기반 시스템의 주요 구성 요소는 다음과 같다:

  1. 이벤트 소스(Event Source): 이벤트를 발생시키는 주체이다.
  2. 이벤트 채널(Event Channel): 이벤트를 전달하는 매개체이다 (이벤트 버스 등).
  3. 이벤트 리스너(Event Listener): 특정 이벤트를 감지하고 처리하는 주체이다.
1
2
3
4
5
                                       ┌─→ [이벤트 처리기1]
[이벤트 소스] ─→ [이벤트 채널/버스] ───────┼─→ [이벤트 처리기2]
                                       └─→ [이벤트 처리기3]

주요 구현 기술 비교

Pub/Sub API 구현 기술
  1. Apache Kafka: 높은 처리량과 내구성을 제공하는 분산 이벤트 스트리밍 플랫폼
  2. RabbitMQ: AMQP 프로토콜을 지원하는 강력한 메시지 브로커
  3. Google Cloud Pub/Sub: 완전 관리형 실시간 메시징 서비스
  4. AWS SNS/SQS: 아마존의 알림 서비스와 메시지 큐 서비스
  5. Redis Pub/Sub: 인메모리 데이터 저장소를 활용한 경량 메시징 시스템
Event-Driven API 구현 기술
  1. WebSockets: 클라이언트와 서버 간 양방향 통신 채널을 제공하는 프로토콜
  2. Server-Sent Events (SSE): 서버에서 클라이언트로의 단방향 이벤트 스트림
  3. WebHooks: 특정 이벤트 발생 시 HTTP 콜백을 통한 알림
  4. Socket.IO: 실시간, 양방향, 이벤트 기반 통신을 위한 라이브러리
  5. Vert.x: 이벤트 루프 기반의 반응형 애플리케이션 프레임워크

기술적 특성

특성Pub/Sub APIsEvent-Driven APIs
확장성초당 100만 메시지 처리 가능장기 실행 연결 시 확장 제약
지연 시간100ms~1s (브로커 처리 시간 영향)10ms~100ms (직접 전달 최적화)
신뢰성At-least-once 전달 보장Best-effort (구현 방식에 따라 다름)
트랜잭션 지원제한적 (배치 단위 커밋)Saga 패턴을 통한 분산 트랜잭션 지원

주요 사용 사례

분야Pub/Sub APIs 적용 예시Event-Driven APIs 적용 예시
금융주문 집행 시스템실시간 사기 탐지
IoT디바이스 상태 일괄 수집센서 임계값 초과 즉시 경고
e-Commerce재고 동기화장바구니 포기 알림
미디어콘텐츠 배포 네트워크실시간 시청자 상호작용 처리

Pub/Sub API 적합 사례

  1. 대규모 데이터 스트리밍: 센서 데이터, 로그 데이터 등 대용량 데이터 처리
  2. 마이크로서비스 간 비동기 통신: 서비스 간 느슨한 결합 유지
  3. 분산 시스템에서의 이벤트 전파: 여러 시스템에 동일한 메시지 전달
  4. 백그라운드 작업 처리: 메인 애플리케이션 흐름에서 분리된 작업 처리
  5. 멀티테넌트 시스템: 여러 고객/사용자에게 맞춤형 메시지 전달

Event-Driven API 적합 사례

  1. 실시간 사용자 인터페이스: 채팅 애플리케이션, 협업 도구
  2. IoT 응용 프로그램: 장치 상태 변화 모니터링 및 제어
  3. 워크플로우 자동화: 특정 이벤트 발생 시 작업 트리거
  4. 반응형 시스템: 시스템 상태 변화에 즉각 대응하는 애플리케이션
  5. 실시간 분석 및 모니터링: 이상 탐지, 실시간 대시보드

장단점 비교

구분Pub/Sub APIs 장점Event-Driven APIs 장점
강점대량 메시지 처리 최적화초저지연 실시간 반응
한계복잡한 라우팅 규칙 구현 어려움이벤트 소실 가능성
최적 시나리오1:N 다중 구독자 환경1:1 또는 N:M 즉각적 반응 요구 시스템

Pub/Sub API

장점:

  1. 느슨한 결합: 발행자와 구독자는 서로에 대해 알 필요가 없음
  2. 확장성: 시스템 구성 요소를 독립적으로 확장 가능
  3. 안정성: 메시지 브로커가 임시 저장소 역할을 하여 시스템 장애 시에도 메시지 손실 방지
  4. 비동기 처리: 발행자는 메시지를 발행하고 즉시 다른 작업 수행 가능
  5. 멀티캐스팅: 하나의 메시지를 여러 구독자에게 효율적으로 전달

단점:

  1. 복잡성: 브로커 설정 및 관리에 따른 추가적인 복잡성
  2. 지연 시간: 브로커를 통한 메시지 전달로 인한 추가 지연 발생 가능
  3. 디버깅 어려움: 비동기 특성으로 인해 문제 추적이 어려울 수 있음
  4. 순서 보장의 어려움: 분산 환경에서 메시지 순서 보장이 도전적
  5. 리소스 사용: 메시지 저장 및 관리에 추가 리소스 필요

Event-Driven API

장점:

  1. 실시간성: 이벤트 발생 즉시 처리 가능
  2. 효율성: 필요한 경우에만 통신이 이루어져 리소스 효율적
  3. 반응형 설계: 상태 변화에 즉각적으로 반응하는 시스템 구축 가능
  4. 세밀한 제어: 이벤트 타입이나 속성에 따른 세밀한 처리 가능
  5. 유연성: 새로운 이벤트 타입이나 처리기를 쉽게 추가 가능

단점:

  1. 복잡한 오류 처리: 비동기 이벤트 처리의 오류 관리가 복잡
  2. 테스트 어려움: 이벤트 기반 시스템의 테스트가 도전적일 수 있음
  3. 이벤트 폭주: 대량의 이벤트 발생 시 시스템 과부하 가능성
  4. 이벤트 의존성: 이벤트 체인의 관리가 복잡해질 수 있음
  5. 상태 관리 어려움: 분산된 이벤트 처리에서 일관된 상태 유지가 어려울 수 있음

Event-driven APIs vs. Pub and Sub APIs 비교

특성Pub/Sub APIEvent-Driven API
정의메시지 발행자와 구독자 간의 중개자를 통한 비동기 통신 패턴이벤트 발생에 따라 시스템이 반응하는 패턴
통신 모델일반적으로 다대다(many-to-many) 통신일대다(one-to-many) 또는 다대다 통신 가능
중개자 역할메시지 브로커(broker)가 중앙에서 메시지 관리이벤트 버스나 이벤트 메시지 큐 등이 사용됨
메시지/이벤트 수명메시지가 일반적으로 구독자에게 전달될 때까지 저장됨이벤트는 주로 즉시 처리되거나 짧은 시간 동안만 유지
결합도매우 낮음 (발행자와 구독자는 서로를 알 필요 없음)낮음 (이벤트 소스와 리스너는 이벤트 타입만 알면 됨)
확장성매우 높음 (수천, 수만 개의 주제와 구독자 지원 가능)높음 (다양한 이벤트 유형 및 처리기 추가 가능)
데이터 흐름주로 메시지 중심 (데이터 전송에 초점)주로 상태 변화 중심 (상태 변화 알림에 초점)
사용 예시메시징 앱, 이메일 알림, 콘텐츠 배포 시스템실시간 대시보드, IoT 장치 모니터링, UI 상호작용
구현 기술Kafka, RabbitMQ, Google Cloud Pub/Sub, AWS SNS/SQSWebSockets, Server-Sent Events, WebHooks, Socket.IO
메시지 필터링주제(topic) 기반 필터링이 일반적이벤트 타입이나 속성 기반 필터링이 일반적
메시지 보증다양한 수준의 전달 보증 제공 가능 (최소 1회, 최대 1회, 정확히 1회)일반적으로 최선의 전달 노력 (best-effort delivery)
지연시간브로커를 통한 통신으로 약간의 지연 발생 가능직접적인 이벤트 처리로 지연시간이 상대적으로 낮을 수 있음
복잡성브로커 설정과 관리에 따른 복잡성 존재이벤트 처리 로직과 오류 처리에 따른 복잡성 존재
상태 관리주로 무상태(stateless) 설계이벤트 소싱 패턴과 결합 시 상태 관리 가능
메시지 순서주제 내에서 메시지 순서 보장이 도전적일 수 있음단일 소스에서의 이벤트 순서는 비교적 쉽게 보장 가능

통합 아키텍처에서의 상호보완적 활용

실제 시스템에서는 두 방식을 혼용하는 경우가 많다.

예를 들어 IoT 플랫폼에서:

  1. Pub/Sub을 통해 디바이스 → 클라우드 메시지 수집
  2. Event-Driven API로 실시간 이상 감지 시 사용자 알림
    이처럼 이벤트 발생(Event-Driven) → 대량 데이터 처리(Pub/Sub) → 결과 전달(Event-Driven)의 연계 구조가 빈번히 사용된다.

많은 현대적인 시스템에서는 Pub/Sub API와 Event-Driven API의 장점을 결합한 하이브리드 접근 방식을 채택하고 있다. 이러한 접근 방식은 다음과 같은 특징을 가진다:

  1. 이벤트 소싱 + Pub/Sub: 이벤트를 저장하고 발행하는 방식으로 결합
  2. CQRS 패턴 활용: Command(명령)와 Query(조회)를 분리하여 각각에 적합한 모델 적용
  3. 메시지 브로커 + WebSocket: 백엔드 시스템 간에는 메시지 브로커를, 클라이언트와는 WebSocket을 사용
  4. 계층적 이벤트 처리: 시스템 내부에서는 이벤트 기반 처리, 외부 통신에는 Pub/Sub 활용
  5. 도메인 이벤트 활용: 비즈니스 로직에서 발생한 이벤트를 Pub/Sub 시스템을 통해 전파

코드 예시 비교

Pub/Sub API 예시 (Node.js + Redis)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// Redis Pub/Sub 예시
const redis = require('redis');

// 발행자 생성
const publisher = redis.createClient();

// 구독자 생성
const subscriber = redis.createClient();

// 구독 설정
subscriber.subscribe('user-notifications');

// 메시지 수신 처리
subscriber.on('message', (channel, message) => {
  console.log(`채널 ${channel}에서 메시지 수신: ${message}`);
  // 메시지 처리 로직
});

// 메시지 발행
function publishUserNotification(userId, message) {
  const notification = JSON.stringify({
    userId,
    message,
    timestamp: Date.now()
  });
  publisher.publish('user-notifications', notification);
}

// 사용 예
publishUserNotification(12345, '새로운 친구 요청이 있습니다.');

Event-Driven API 예시 (Node.js + Express + Socket.IO)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
// Express와 Socket.IO를 사용한 이벤트 기반 서버
const express = require('express');
const http = require('http');
const socketIO = require('socket.io');

const app = express();
const server = http.createServer(app);
const io = socketIO(server);

// 이벤트 발생 및 처리
io.on('connection', (socket) => {
  console.log('새 클라이언트 연결됨');
  
  // 클라이언트에서 발생한 이벤트 처리
  socket.on('user-activity', (data) => {
    console.log(`사용자 ${data.userId}의 활동: ${data.action}`);
    
    // 특정 조건에 따라 새 이벤트 발생
    if (data.action === 'purchase') {
      // 특정 사용자에게만 이벤트 전송
      socket.emit('purchase-confirmation', {
        orderId: generateOrderId(),
        message: '주문이 완료되었습니다.',
        timestamp: Date.now()
      });
      
      // 관리자에게 알림 전송
      io.to('admin-room').emit('new-purchase', {
        userId: data.userId,
        details: data.details
      });
    }
  });
  
  // 관리자 사용자 처리
  if (isAdmin(socket.request)) {
    socket.join('admin-room');
  }
  
  socket.on('disconnect', () => {
    console.log('클라이언트 연결 종료');
  });
});

server.listen(3000, () => {
  console.log('서버가 3000 포트에서 실행 중입니다.');
});

// 헬퍼 함수
function generateOrderId() {
  return Math.random().toString(36).substring(2, 15);
}

function isAdmin(request) {
  // 관리자 확인 로직
  return request.headers.role === 'admin';
}

용어 정리

용어설명

참고 및 출처