채팅·실시간 알림·라이브 대시보드 — server 가 client 에 데이터를 push 해야 한다. HTTP 의 request/response 패턴으로는 어려움. 답은 WebSocket 또는 Server-Sent Events (SSE). 둘이 어떻게 다른가? HTTP 위에서 어떻게 양방향 통신이 가능한가? 이 가이드는 WebSocket handshake, SSE EventSource, long polling 의 비교, 그리고 어떤 상황에 무엇을 쓸지 정리한다.
왜 HTTP 만으로 부족한가
HTTP 기본 패턴:
Client → Request → Server
Server → Response → Client
↓ connection close
채팅 messages 받으려면?
- 매 1 초마다 polling?
→ 99% 빈 응답, 부하 폭주
- server 가 client 에 직접 push?
→ 어떻게? client 의 IP/port 모름, NAT 뒤실시간 통신 = "server 가 데이터 push" + "client 가 data send" 둘 다.
옛 해결책 — Long Polling
Client → GET /messages?since=0 (HTTP)
Server: 응답 보류 (open connection)
...
새 메시지 도착!
Server → 200 OK, {messages: [...]}
↓ connection close
Client → GET /messages?since=last (즉시 다시)
...장점 — HTTP 만 사용, proxy / firewall 호환. 단점:
- 매 message 마다 connection 새로 → overhead
- HTTP header 반복 (kB 씩)
- server 가 응답 보류 중 timeout 위험
2010 이전에는 표준. 지금은 fallback only.
WebSocket — 양방향 single connection
2011 표준 (RFC 6455). HTTP 위 handshake 한 번 → connection 을 WebSocket 으로 "upgrade" → 양방향 byte stream:
Client → GET /ws HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
Server → HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
→ 이제 같은 TCP connection 으로 WebSocket frame 양방향 송수신Sec-WebSocket-Accept = client 의 Key 와 magic string 결합 후 SHA-1. 단순 echo 가 아닌 처리로 cache proxy 가 잘못 응답 못 함.
WebSocket frame 형식
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-------+-+-------------+-------------------------------+
|F|R|R|R| op |M| Payload len | Extended payload length |
|I|S|S|S| code |A| (7) | (16/64) |
|N|V|V|V| (4) |S| | |
| |1|2|3| |K| | |
+-+-+-+-+-------+-+-------------+-------------------------------+
op code:
0x1 = text frame (UTF-8)
0x2 = binary frame
0x8 = close frame
0x9 = ping frame
0xA = pong frame매 frame 2-14 byte overhead (HTTP header 의 200-2000 byte 대비 훨씬 적음). 초당 수천 메시지 가능.
WebSocket 클라이언트 코드
const ws = new WebSocket("wss://example.com/ws");
ws.onopen = () => {
ws.send("Hello server!");
};
ws.onmessage = (event) => {
console.log("받은 메시지:", event.data);
};
ws.onclose = (event) => {
console.log("연결 종료:", event.code, event.reason);
};
ws.onerror = (event) => {
console.error("에러:", event);
};Server-Sent Events (SSE) — 단방향 server push
2009 W3C 표준. HTTP 의 long-running response 활용 + 표준 라이브 러리 wrapper:
Client:
const source = new EventSource("/events");
source.onmessage = (e) => {
console.log("server 가 보낸:", e.data);
};
Server (Node):
res.writeHead(200, {
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache",
"Connection": "keep-alive",
});
// 매 event 마다
res.write(`data: ${JSON.stringify(msg)}\n\n`);
// 연결 유지, 닫지 않음Wire format — 단순 text:
event: chat-message
data: {"user":"Alice","text":"hi"}
id: 42
event: notification
data: New user joined
id: 43
(blank line 으로 event 구분)SSE 의 강점
- HTTP 위 — 별도 protocol X, proxy / firewall 호환 ↑
- 자동 재연결 — EventSource 가 connection drop 감지 후 자동 재연결.
Last-Event-ID헤더로 미수신 event 복구 - browser 표준 API — EventSource. 라이브러리 X
SSE 의 약점
- 단방향만 — server → client. client → server 는 별도 HTTP POST
- browser connection 제한 — domain 당 6 개 (HTTP/1.1). 여러 tab 의 SSE 가 모두 같은 domain 이면 한도 hit
- binary 미지원 — text/event-stream 만, base64 필요
WebSocket vs SSE — 매트릭스
| WebSocket | SSE | |
|---|---|---|
| 방향 | 양방향 | server → client 만 |
| Protocol | ws:// / wss:// | http:// / https:// |
| Binary | ✅ | X (base64 필요) |
| 자동 재연결 | 수동 구현 | ✅ 내장 |
| Proxy / Firewall | 가끔 blocked | HTTP — 통과 잘됨 |
| Multiplex | connection 1 + topic | connection 마다 별개 |
| Overhead per message | 2-14 byte | ~50 byte (text framing) |
| HTTP/2 multiplexing | X (별도 protocol) | ✅ (HTTP/2) |
| Server 구현 | 전용 library (Socket.IO, ws) | 표준 HTTP, 라이브러리 X |
언제 어떤 것
WebSocket 권장
- 채팅 (양방향, low latency)
- 실시간 multi-player 게임
- collaborative editing (Google Docs)
- trading / 금융 ticker (대량 binary)
SSE 권장
- 알림 (server → client 만)
- 실시간 dashboard (stock price, server metrics)
- activity feed (Twitter, GitHub 알림)
- build / job progress (CI 로그 stream)
- client → server 는 적은 (form 제출 등 일반 HTTP)
그 외 후보
- WebRTC — peer-to-peer + UDP. 음성·영상·게임 저지연. server 부담 ↓.
- HTTP/2 Server Push — preload 용. 일반 실시간 X. deprecated by Chrome (2022).
- Long polling — 옛 fallback. 모던 환경에서는 last resort.
실용 — Heartbeat 와 reconnection
WebSocket — ping/pong
Server → ping frame (op 0x9)
Client → pong frame (op 0xA) 자동 응답
30 초 이상 pong 안 옴 → connection dead, 재연결Cloudflare / nginx 가 idle connection 60-100 초 timeout 으로 kill — heartbeat 30 초 주기 필수.
SSE — 자동 재연결
server: 빈 comment 으로 heartbeat
: heartbeat
client EventSource:
- connection drop 감지
- retry: 3000 (server 가 지정 가능)
- 3 초 후 자동 재연결
- Last-Event-ID 헤더에 마지막 받은 id 박음
- server 가 미수신 event 부터 다시 보냄인증·security
- WebSocket — handshake 의 HTTP request 에 cookie / token 박힘. 첫 메시지로 auth 또는 query param 으로 token
- SSE — HTTP 그대로, cookie / Authorization 자연 동작
WebSocket 의 함정 — Origin 헤더 검증 안 하면 CSWSH (Cross-Site WebSocket Hijacking) 공격. SSE 는 CORS 적용.
흔한 함정
1. browser connection 한도
HTTP/1.1 에서 domain 당 6 connection. SSE 여러 개 + 일반 fetch 병행 시 hit. HTTP/2 는 multiplex 로 해결.
2. CDN / Proxy 의 buffering
nginx / Cloudflare 의 default buffer 가 SSE response 모아서 보냄 → 실시간 X. X-Accel-Buffering: no header + proxy 설정.
3. WebSocket 재연결 폭주
Server outage 후 모든 client 가 동시 재연결 시도 → thundering herd → server 또 down. exponential backoff + jitter 필수.
4. Memory leak
Client side WebSocket 의 message handler 가 closure 로 outer scope 변수 capture → GC 안 됨. 명시적 ws.close() + listener 제거.
5. Backpressure
Server 가 slow client 에 빠르게 send → 메시지 buffer 폭주. drop oldest 또는 throttling.
참고 자료
- RFC 6455 (WebSocket) — datatracker
- MDN — Server-Sent Events — MDN
- MDN — WebSockets API — MDN
- WHATWG HTML — EventSource spec — WHATWG
요약
- 실시간 통신 옵션 — long polling (옛) / WebSocket (양방향) / SSE (단방향) / WebRTC (peer-to-peer).
- WebSocket = HTTP 위 handshake → 양방향 binary/text frame. ws:// / wss:// protocol. 채팅 / 게임 / 양방향.
- SSE = HTTP long-running response + text/event-stream. EventSource API. 단방향, 자동 재연결, proxy 호환 ↑.
- 매 message overhead — WebSocket 2-14 byte vs SSE 50 byte vs HTTP 1-2 KB. WebSocket 가장 효율적.
- SSE 가 단방향 + HTTP 호환 + 자동 재연결로 단순. 알림 / 대시보드 에 first choice.
- Heartbeat 필수 — WebSocket ping/pong, SSE comment.
- CDN / proxy 의 buffering 주의 — X-Accel-Buffering: no
- 재연결 시 exponential backoff + jitter — thundering herd 방지.