codingstairs
노트에듀라이프연락
⌕검색⌘K
koen

Navigation

  • Intro
  • Blog
  • Life

연락하기

로그인 없이도 보낼 수 있어요. 답변이 필요하면 이메일을 함께 적어 주세요.

  • 익명 폼으로 의견 남기기 →
  • ✉ warragon112@gmail.com
  • 카카오톡 오픈채팅 ↗

© 2026 codingstairs

  • 노트
  • 에듀
  • 검색
  • 라이프
  • 연락
  • 약관
  • RSS
  • GitHub
노트›backend

WebSocket · SSE — 실시간 통신

2026-04-28 게시· 2026-05-18 갱신·0회 조회

WebSocket · SSE — 실시간 통신

HTTP 는 본래 한 번 요청 → 한 번 응답의 모델입니다. 서버가 클라이언트에게 먼저 말 걸기 어렵습니다. 채팅·알림·라이브 업데이트 같은 자리에서는 서버가 능동적으로 데이터를 보낼 수 있어야 합니다.

1. 실시간 통신에 대한 이야기

사건 시기
Comet (long polling 의 부모 개념) 2006
WebSocket 첫 초안 (HyBi) 2009
WebSocket RFC 6455 2011
EventSource (SSE) HTML5 표준화 2011 ~
HTTP/2 + Server Push (별도 모델) 2015 (이후 사용 줄어듦)

세 가지 후보의 큰 그림:

모델 방향 프로토콜 사용처
Short Polling 클라이언트 주도 일반 HTTP 단순 · 비용 비효율
Long Polling 클라이언트 주도, 서버 대기 일반 HTTP 폴백 자리
SSE 서버 → 클라이언트 단방향 HTTP (Content-Type: text/event-stream) 알림 · 라이브 피드
WebSocket 양방향 ws:// · wss:// (HTTP Upgrade) 채팅 · 게임 · 협업

2. Polling

Short Polling — 클라이언트가 일정 주기로 요청해 새 데이터를 묻습니다.

setInterval(() => fetch('/messages?after=' + lastId), 3000);

장점은 단순. 단점은 빈 응답이 많아 비용 비효율, 지연이 폴링 주기에 묶임.

Long Polling — 요청을 받은 서버가 새 데이터가 생길 때까지 응답을 보류합니다. 응답 후 클라이언트가 즉시 다음 요청.

Client → /messages?after=42  ─────────────┐
                                          │ (서버가 새 메시지 올 때까지 대기)
Server ← 200 OK [{id:43,...}]  ←──────────┘
Client → /messages?after=43  ─────────────┐ (즉시 다음 폴 시작)

WebSocket 도입 전 표준 흐름이었습니다. 지금도 폴백 자리 (옛 브라우저, 일부 프록시 환경) 로 남아 있습니다.

3. SSE

EventSource API 와 text/event-stream MIME 타입으로 표준화됐습니다. 서버가 한 번 열린 HTTP 연결로 데이터를 계속 흘려보냅니다.

GET /events
Accept: text/event-stream

HTTP/1.1 200 OK
Content-Type: text/event-stream
Cache-Control: no-cache

data: {"id":1,"text":"hi"}

event: ping
data: {}

id: 12
data: {"type":"update"}

클라이언트는 브라우저 기본 API 로 열 수 있습니다.

const es = new EventSource('/events');
es.onmessage = (e) => console.log(JSON.parse(e.data));
es.addEventListener('ping', () => {});

연결이 끊기면 EventSource 가 자동 재연결합니다. 마지막 받은 id 가 Last-Event-ID 헤더로 다시 보내져 누락 복구를 돕습니다.

특징:

  • 단방향 (서버 → 클라이언트).
  • HTTP/1.1 · HTTP/2 모두 동작. HTTP/2 에서는 한 연결의 여러 SSE 가 가능.
  • 텍스트 기반.
  • 표준 HTTP 라 프록시·LB 와 잘 맞는 자리.

알림·뉴스 피드·LLM 토큰 스트리밍에 자주 쓰입니다.

4. WebSocket

HTTP Upgrade 헤더로 시작해 별도 프로토콜로 전환합니다.

GET /chat
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: <base64>
Sec-WebSocket-Version: 13

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: <derived>

이후 같은 TCP 연결 위에 프레임 기반의 양방향 통신. 텍스트·바이너리 모두 가능. ping/pong 프레임으로 keep-alive.

특징:

  • 양방향, 저지연.
  • 별도 프로토콜이라 일부 프록시·CDN 이 처리 못 할 수 있음 (대부분은 지원).
  • 연결 상태 관리 부담은 사용자 (재연결·세션·인증).

채팅·게임·협업 편집 (Yjs · CRDT) · 실시간 시세에 자주 쓰입니다.

5. Node — ws · Socket.IO

import { WebSocketServer } from 'ws';
const wss = new WebSocketServer({ port: 8080 });
wss.on('connection', (sock) => {
  sock.on('message', (m) => sock.send(`echo ${m}`));
});

Socket.IO 는 ws 위에 layer 를 얹어 자동 재연결·룸·이벤트 emit 을 표준화합니다. 프로토콜이 표준 WebSocket 과 살짝 달라 호환은 Socket.IO 클라이언트가 필요합니다.

6. FastAPI · Spring WebFlux

from fastapi import FastAPI, WebSocket

app = FastAPI()

@app.websocket('/ws')
async def ws(s: WebSocket):
    await s.accept()
    while True:
        msg = await s.receive_text()
        await s.send_text(f"echo {msg}")

Spring WebFlux 는 Reactive Streams 위의 WebSocket. 백프레셔 자연스러움. Spring MVC 도 @MessageMapping (STOMP over WebSocket) 으로 같은 자리 가능.

대부분의 프레임워크가 응답을 chunked 로 흘려 SSE 를 만듭니다. FastAPI 는 StreamingResponse, Express 는 res.write 루프, Spring WebFlux 는 Flux<ServerSentEvent<...>>.

7. 매니지드 서비스

서비스 모델 메모
Pusher (2010) 매니지드 WebSocket 채널·이벤트 모델. SDK 풍부.
Ably (2016) 매니지드 메시징 글로벌 분산. 메시지 보장.
Supabase Realtime Postgres 변경 → WebSocket DB 변경 스트림.
Firebase Realtime DB · Firestore 실시간 DB DB 자체가 변경 스트림.
Pub/Sub (Google) · SNS · EventBridge 이벤트 백본 클라이언트 직접보다는 백엔드 간.
Centrifugo OSS WebSocket 서버 자체 호스팅 매니지드 대안.

직접 운영의 부담 (연결 수 · 스케일링 · 재연결) 을 빼고 싶으면 매니지드 서비스. 이미 PostgreSQL 가 있고 변경 알림이면 Supabase Realtime 같은 자리가 자연스럽습니다.

8. 어떤 모델을 고를까

  • 단방향, 비교적 저빈도, 알림·피드 — SSE. HTTP 인프라와 잘 맞고 복원도 자동.
  • 양방향, 저지연, 채팅·게임·협업 — WebSocket.
  • 레거시 프록시·옛 브라우저 호환 — Long Polling 폴백.
  • DB 변경 알림 — Supabase Realtime · CDC + 백엔드 → SSE/WebSocket.

9. 인증과 스케일링

표준 EventSource API 는 헤더 추가를 못 합니다. 토큰 인증은 cookie 또는 query 로 전달합니다. WebSocket 도 비슷합니다 — 첫 핸드셰이크 시점에 인증.

토큰을 query 에 두면 access log 에 남을 수 있어 cookie 가 권장된다는 의견이 자주 보입니다.

연결 수가 많아지면 단일 인스턴스의 메모리·FD 한도에 닿습니다.

인스턴스를 여러 대로 분산
→ Sticky Session 또는 Redis Pub/Sub 으로 메시지 fan-out

ALB · NLB 가 WebSocket 지원
→ ALB idle timeout 기본 60 초라 ping 으로 유지

10. 자주 걸리는 자리

idle timeout — ALB · CloudFront · 일부 프록시가 60 초 ~ 몇 분에 끊습니다. 클라이언트·서버 양쪽에서 ping/pong 으로 유지합니다.

중복 연결 — 클라이언트가 재연결 로직을 잘못 짜면 두 연결을 동시에 들고 메시지가 두 번 도착합니다.

SSE 의 EventSource 헤더 한계 — 표준 EventSource API 는 헤더 추가 못 함. 토큰 인증은 cookie 또는 query.

메시지 순서·중복 — WebSocket 이 본질적으로 신뢰 채널이지만 재연결 후 누락 가능. 서버가 마지막 ID 기반 복원을 제공해야 합니다.

백프레셔 누락 — 서버가 빠르게 보내는데 클라이언트가 못 따라가면 메모리·연결 고갈. 토큰 단위 throttle.

CORS/Origin 검증 — WebSocket 은 same-origin 정책의 영향이 다릅니다. 핸드셰이크의 Origin 헤더 검증.

TLS 미적용 — ws:// 는 평문. 운영에서는 항상 wss://. 일부 환경 (공공 와이파이) 에서 평문 WebSocket 이 끊김 보고가 있습니다.

하고픈 말

실시간 통신은 WebSocket 이 정답이라는 인상이 있지만 SSE 가 더 나은 자리가 자주 있습니다. 단방향이라면 SSE 의 단순함이 운영을 줄여 줍니다. WebSocket 은 양방향이 진짜 필요한 자리에서만 들이는 편이 안전합니다.

Next

  • typeorm-readonly
  • spring-multi-module

RFC 6455 — WebSocket Protocol · HTML Living Standard — Server-Sent Events · WebSockets API (MDN) · ws · Socket.IO · FastAPI WebSocket · Spring WebFlux WebSocket · Pusher · Ably · Supabase Realtime 를 참고합니다.

backend 카테고리의 다른 글

카테고리 전체 보기 →
  • 공공 OpenAPI 는 자체 BFF 로 한 번 감싼다
  • 이메일 발송과 OTP — SMTP
  • 감사로그 — logAdminAction 패턴
  • REST API 입문
  • OpenAPI 사양
  • 크롤러 윤리와 도구