"HTTP 는 request/response. WebSocket 은 전화 통화. 밑은 같은 TCP, 대화 모양은 완전 달라."
WebSocket 이 푸는 문제
HTTP 는 "클라이언트가 묻고, 서버가 답하고, 대화 끝" 에 아름답게 작동. "서버가 예측 불가능한 순간에 클라이언트한테 뭔가 push 하고 싶음" 엔 나쁘게 작동. 역사적 우회책 — long-polling, server-sent events — 다 단점 (오버헤드, 단방향만, 재연결 churn).
WebSocket 이 이거 위해 설계된 프로토콜: 일회성 HTTP-기반 handshake 후, 같은 TCP 소켓이 양방향 메시지-패싱 채널 됨. 어느 쪽이든 언제든 메시지 보낼 수 있어. 프레이밍은 작아 (메시지당 몇 바이트 오버헤드). Connection 이 양쪽이 필요한 만큼 열려 있어.
Native ws 스토리 (그리고 왜 라이브러리 쓰는지)
Node 가 node:ws *클라이언트* 출하 — Node 22+ 에 브라우저 API 매치하는 클라이언트 사이드 connection 위한 글로벌 WebSocket 클래스 있음:
const ws = new WebSocket('wss://echo.example.com');
ws.addEventListener('open', () => ws.send('hello'));
ws.addEventListener('message', (ev) => console.log(ev.data));
ws.addEventListener('close', () => console.log('done'));
*서버* 쪽엔 Node 가 내장 안 출하. De facto 라이브러리는 ws (npm: ws) — battle-tested, Socket.IO 가 밑에서 씀, LSP 에코시스템이, Next.js dev 서버가. cwkPippa 가 Cinder 브리지에 씀. 재발명 마.
최소 서버
import { WebSocketServer } from 'ws';
const wss = new WebSocketServer({ port: 7070 });
wss.on('connection', (socket, req) => {
console.log('client connected from', req.socket.remoteAddress);
socket.on('message', (data) => {
// Broadcast to everyone
for (const client of wss.clients) {
if (client.readyState === 1) client.send(data.toString());
}
});
socket.on('close', () => console.log('client gone'));
});
이게 작동하는 chat 서버. 모든 클라이언트가 연결, 모든 메시지가 모두한테 감. 진짜 앱엔 room, auth, persistence 필요 — 근데 프로토콜 primitive 가 이렇게 단순해.
WebSocket 이 맞을 때 (그리고 아닐 때)
- 채팅, presence, 멀티플레이어 게임.
- 라이브 대시보드 (주식 ticker, 시스템 메트릭).
- 협업 편집 (WebSocket 위 Yjs).
- SSE 부족할 때 백엔드 → 프론트엔드 push.
- 단일 connection 위 양방향 RPC.
- 단방향 서버 → 클라이언트 스트림 (Server-Sent Events — SSE — 더 단순 프로토콜, plain HTTP, 자동 재연결).
- 일회성 request (plain HTTP — 영속 connection 유지할 이유 없음).
- HTTP 중간자가 캐시해야 하는 거 (WebSocket 메시지는 캐시 불가).
Backpressure, Heartbeat, 재연결
오래 살아있는 connection 엔 HTTP 가 안 가진 plumbing 필요:
- Heartbeat — 30s 마다 ping/pong frame 이 죽은 connection 감지.
ws가ping()+pong이벤트 지원 출하. 없으면 half-closed connection (NAT timeout, 모바일 네트워크 swap) 조용히 leak. - Backpressure — 네트워크 느리면
socket.send(data)가 queue.socket.bufferedAmount검사하고 자라면 back off. 안 그러면 잘못된 클라이언트가 서버 RAM 채울 수 있음. - 재연결 — 클라이언트가 close 시 exponential backoff 로 재연결해야. 브라우저
WebSocket은 자동 재연결 안 함; 얇은 reconnecting 클라이언트로 wrap 또는 higher-level 라이브러리.