본문으로 건너뛰기
yutils

Webhook · Polling · SSE · WebSocket — 실시간 패턴 선택 가이드

지연·비용·복잡도·실패 모드로 4 가지 실시간 패턴 비교. Webhook·Polling·SSE·WebSocket 의 적절한 선택 기준과 fallback 전략.

약 9분 읽기

"Stripe 결제 완료를 우리 시스템에 어떻게 알릴까?" — 답에 따라 아키텍처가 갈린다. Webhook, polling, Server-Sent Events (SSE), WebSocket — 모두 "한쪽의 변화를 다른 쪽이 알게 하는" 패턴이지만 지연 시간·비용·복잡도가 매우 다르다. 이 가이드는 네 가지를 비교하고 어떤 상황에 어느 패턴을 골라야 하는지 정리한다.

네 가지 패턴 한 줄 요약

패턴방향지연비용전형 사례
PollingClient → Server (반복 pull)interval (수 초~분)요청 빈도 × nSlack 알림 전 단계, 후행 작업 status
WebhookServer → Server (event push)거의 즉시이벤트당 1 회Stripe 결제, GitHub push, Slack 슬래시
SSEServer → Client (스트림)즉시connection 1 개 유지chat·dashboard 단방향 알림
WebSocket양방향 (full-duplex)즉시connection 1 개 양방향실시간 협업, 게임, 거래소

1. Polling — 가장 단순, 가장 둔감

클라이언트가 일정 간격으로 서버에 "변경됐어?" 묻는다.

setInterval(async () => {
  const res = await fetch("/api/job/123/status");
  const {status} = await res.json();
  if (status === "done") {
    showResult();
    return;
  }
}, 5000);  // 5 초 간격

장점:

  • 인프라 0 — 표준 HTTP 만. 방화벽·프록시 모두 통과.
  • 실패 시 복구 = 다음 polling. 무상태.
  • 구현 5 분.

단점:

  • 지연 = polling interval. 1 초마다 polling 하면 origin 부하 60× 분당.
  • 요청 대부분이 "변경 없음" → 낭비. 9 / 10 응답이 304 또는 같은 데이터.

Long polling — polling 변형

클라이언트 요청을 서버가 즉시 응답 안 하고 변경 발생까지 hold (최대 N 초). 변경 발생 시 응답, 아니면 timeout 후 클라이언트 재요청.

  • 장점: 지연 즉시 (변경 시).
  • 단점: connection 점유. 일부 프록시 timeout 짧음. SSE/WebSocket 이 등장 후 거의 폐기.

2. Webhook — Server-to-Server Push

제공자가 이벤트 발생 시 HTTP POST 로 우리 서버에 직접 알림. Stripe·GitHub·Slack 등 모든 SaaS API 의 표준.

// Webhook 수신 엔드포인트
POST /webhooks/stripe
Stripe-Signature: t=...,v1=...
Content-Type: application/json

{ "type": "charge.succeeded", "data": {...} }

장점:

  • 지연 거의 즉시.
  • 요청 = 이벤트 수 (낭비 0).
  • 클라이언트 connection 점유 X.

단점:

  • 수신 서버가 public endpoint 필요. localhost·내부망 X. 개발 환경은 ngrok/cloudflared tunnel 필요.
  • 요청 출처 확인 = 보안 핵심. 서명 검증 필수 ( HMAC 검증기 의 constant-time 비교).
  • 제공자 측 retry 정책에 의존. 우리 서버가 잠시 down 이면 어떻게 되나?

Webhook retry — 제공자별

  • Stripe — 최대 3 일, 지수 백오프 (5 초→8 시간).
  • GitHub — 8 시간 내 5 회 시도 후 비활성화.
  • Slack — 5 회 시도.

수신 측은 5 초 내 200 응답 권장. 처리는 큐로 비동기. signature 검증 → 200 → 작업 큐 enqueue 패턴이 표준.

Webhook 검증 코드

// Stripe 스타일 (자세히는 [[hmac-webhooks]] 가이드)
import {createHmac, timingSafeEqual} from "crypto";

function verify(body, sigHeader, secret) {
  const [t, v1] = parseSignature(sigHeader);
  const signed = `${t}.${body}`;
  const expected = createHmac("sha256", secret).update(signed).digest("hex");
  return timingSafeEqual(Buffer.from(expected, "hex"), Buffer.from(v1, "hex"));
}

HMAC 생성기 에서 같은 알고리즘으로 직접 계산해 보면 서명 형식 직관적.

3. Server-Sent Events — 단방향 스트림

HTTP 위에 만들어진 표준 (W3C). 서버가 클라이언트로 이벤트 텍스트를 스트림. 브라우저 EventSource API 가 자동 처리.

// 서버 (Node.js)
res.setHeader("Content-Type", "text/event-stream");
res.setHeader("Cache-Control", "no-cache");
res.setHeader("Connection", "keep-alive");

setInterval(() => {
  res.write(`data: ${JSON.stringify({time: Date.now()})}\n\n`);
}, 1000);

// 클라이언트
const es = new EventSource("/api/stream");
es.onmessage = (e) => {
  console.log(JSON.parse(e.data));
};

장점:

  • 표준 HTTP — 방화벽·프록시·CORS 익숙한 규칙 적용.
  • 브라우저 native API — polyfill 0.
  • connection 끊기면 자동 재시도 (retry: 디렉티브).
  • 단방향이라 안전 — 클라이언트가 서버를 못 누름.

단점:

  • 단방향 — 클라이언트 → 서버 메시지 X. 양방향 필요 시 WebSocket.
  • HTTP/1.1 에서는 같은 호스트에 6 connection 한도. HTTP/2 multiplex 로 해결.
  • 모든 환경에서 SSE 지원하는 건 아님 — IE 미지원 (IE 가 죽었지만).

4. WebSocket — 양방향 full-duplex

TCP 위에 만든 별도 프로토콜 (ws://, wss://). HTTP 업그레이드 핸드셰 이크 후 양방향 메시지.

// 서버 (ws library)
import {WebSocketServer} from "ws";
const wss = new WebSocketServer({port: 8080});
wss.on("connection", (ws) => {
  ws.on("message", (data) => {
    ws.send(`echo: ${data}`);
  });
});

// 클라이언트
const ws = new WebSocket("wss://example.com/socket");
ws.onmessage = (e) => console.log(e.data);
ws.send("hello");

장점:

  • 양방향 즉시. 게임·협업·거래소 등 필수.
  • 오버헤드 작음 — 메시지 frame 만.

단점:

  • 별도 인프라 — HTTP 캐시·프록시 통과 어려움. CDN edge 일부는 미지원 또는 별도 가격.
  • 재연결·인증·heartbeat 직접 구현. EventSource 와 달리 자동 재시도 X.
  • 확장 = sticky session 또는 broker (Redis pub/sub) 필요.

선택 가이드

Stripe·GitHub 같은 외부 이벤트 받기

Webhook 무조건. polling 은 제공자 API rate limit + 지연 이중 손해. 외부 SaaS 는 webhook 거의 다 지원.

장기 실행 작업 status

Polling (5-10 초). 단순. 작업이 30 초 이내면 polling 충분. 1 분+ 이면 webhook → 사용자 이메일/푸시 또는 SSE 로 진행률 스트림.

대시보드·라이브 차트

SSE. 단방향 + 자동 재시도. WebSocket 까지 갈 필요 0. 메트릭 1 초마다 push.

채팅·협업·게임

WebSocket. 양방향 + 메시지 순서 + 낮은 지연 모두 필요.

제공자 측이 webhook 없을 때

polling 의 사용처. Cron 표현식 분석 이나 Cron 표현식 빌더 로 cron 표현식 만들어 정기 fetch.

Webhook + polling 하이브리드

실무에서 가장 안정적인 패턴:

  1. 주된 채널: webhook (즉시).
  2. 백업 cron: 24 시간마다 polling. webhook 실패·누락 감지 (예: 지난 24 시간 결제 ID 가 우리 DB 와 일치하는지).
  3. 불일치 발견 시 재처리.

webhook 의 단점 (네트워크 일시 단절 시 누락) 을 polling 으로 보완. Stripe 도 공식 권장하는 패턴.

흔한 함정

1. Webhook 처리 동기로 무거운 작업

5 초 초과 시 제공자가 timeout 처리. 응답 빨리 반환 + 큐로 비동기.

2. Webhook 검증 누락

endpoint URL 만 알면 누구나 가짜 이벤트 보낼 수 있음. 결제 사기. HMAC 검증기 constant-time 비교 필수.

3. Polling 간격 너무 짧음

1 초 polling = 분당 60 req × 사용자 수. rate limit 또는 비용 폭발. WebSocket / SSE 로 전환 고려.

4. WebSocket 의 sticky session 무시

load balancer 가 매 connection 다른 서버로 라우팅 → 메시지 broadcast 깨짐. Redis pub/sub 또는 sticky session 으로 해결.

5. SSE / WebSocket 의 inactive timeout

프록시·CDN 이 일정 시간 idle 후 connection 끊음. heartbeat (15-30 초 주기 ping) 필수.

6. Webhook URL 노출

URL 자체가 비밀처럼 동작하면 안 됨. 서명 검증 + URL 추정 어렵게 + IP 화이트리스트 (제공자가 IP 공개 시).

7. HTTP 상태 코드 응답 의미 오해

webhook 수신 시 200 = "받았다" 이면 충분. 처리 결과는 별도. 4xx / 5xx 반환 시 제공자가 retry 트리거.

요약

  • 외부 SaaS 이벤트 = webhook (지연 즉시, 비용 최소).
  • 폴링은 단순함의 가치만큼 비용. 사용자별 5 초 이상 권장.
  • 단방향 라이브 = SSE. 양방향 = WebSocket.
  • Webhook 은 항상 서명 검증 + 5 초 내 응답 + 비동기 처리.
  • webhook + polling 하이브리드가 production 안정성 표준.
가이드 목록으로