SSE, Polling, WebSocket 비교 학습 노트
개요
웹에서 서버와 클라이언트 간 실시간 데이터 통신을 구현하는 세 가지 주요 방식을 비교한다.
1. Polling (폴링)
개념
클라이언트가 일정 주기로 서버에 요청을 보내 새로운 데이터가 있는지 확인하는 방식이다. 가장 단순하지만 비효율적인 접근법이다.
동작 방식
클라이언트 → 서버: 새 데이터 있어?
서버 → 클라이언트: 없음
(3초 후)
클라이언트 → 서버: 새 데이터 있어?
서버 → 클라이언트: 없음
(3초 후)
클라이언트 → 서버: 새 데이터 있어?
서버 → 클라이언트: 있음! 여기 데이터구현 예시
javascript
// Short Polling
setInterval(async () => {
const response = await fetch('/api/updates');
const data = await response.json();
if (data.hasUpdates) {
handleUpdates(data);
}
}, 3000);Long Polling (개선된 방식)
서버가 새 데이터가 있을 때까지 응답을 보류하는 방식이다.
javascript
async function longPoll() {
const response = await fetch('/api/updates?timeout=30000');
const data = await response.json();
handleUpdates(data);
longPoll(); // 즉시 다음 요청
}장단점
| 장점 | 단점 |
|---|---|
| 구현이 매우 단순함 | 불필요한 요청으로 서버 부하 증가 |
| 모든 브라우저/환경 지원 | 실시간성이 낮음 (주기에 의존) |
| 방화벽/프록시 문제 없음 | 네트워크 트래픽 낭비 |
| HTTP 인프라 그대로 활용 | 배터리 소모 (모바일) |
2. SSE (Server-Sent Events)
개념
서버에서 클라이언트로 단방향 실시간 데이터 스트림을 제공하는 방식이다. HTTP 연결을 유지하면서 서버가 원할 때 데이터를 푸시한다.
동작 방식
클라이언트 → 서버: 연결 요청 (Accept: text/event-stream)
서버 → 클라이언트: 연결 유지...
서버 → 클라이언트: data: {"message": "새 알림"}
서버 → 클라이언트: data: {"message": "또 다른 알림"}
(연결 유지 중...)구현 예시
클라이언트
javascript
const eventSource = new EventSource('/api/stream');
eventSource.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log('받은 데이터:', data);
};
eventSource.onerror = (error) => {
console.error('SSE 에러:', error);
};
// 특정 이벤트 타입 수신
eventSource.addEventListener('notification', (event) => {
console.log('알림:', event.data);
});서버 (Express)
javascript
app.get('/api/stream', (req, res) => {
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
const sendEvent = (data) => {
res.write(`data: ${JSON.stringify(data)}\n\n`);
};
// 이벤트 발생 시 전송
const interval = setInterval(() => {
sendEvent({ time: new Date().toISOString() });
}, 1000);
req.on('close', () => {
clearInterval(interval);
});
});SSE 프로토콜 형식
event: notification
data: {"title": "새 메시지", "body": "안녕하세요"}
id: 12345
retry: 3000event: 이벤트 타입 (선택)data: 실제 데이터 (필수)id: 이벤트 ID, 재연결 시 Last-Event-ID 헤더로 전송retry: 재연결 대기 시간(ms)
장단점
| 장점 | 단점 |
|---|---|
| 자동 재연결 내장 | 단방향만 지원 (서버→클라이언트) |
| 이벤트 ID로 메시지 복구 가능 | IE/Edge 레거시 미지원 |
| HTTP/2 멀티플렉싱 활용 | 브라우저당 연결 수 제한 (6개) |
| 구현이 비교적 단순 | 바이너리 데이터 비효율적 |
| 프록시/방화벽 친화적 |
3. WebSocket
개념
TCP 위에서 동작하는 양방향 전이중(full-duplex) 통신 프로토콜이다. 초기 HTTP 핸드셰이크 후 독립적인 WebSocket 연결로 업그레이드된다.
동작 방식
클라이언트 → 서버: HTTP 업그레이드 요청
서버 → 클라이언트: 101 Switching Protocols
(이제부터 WebSocket 프로토콜)
클라이언트 ↔ 서버: 양방향 메시지 교환
클라이언트 ↔ 서버: 양방향 메시지 교환구현 예시
클라이언트
javascript
const ws = new WebSocket('wss://example.com/socket');
ws.onopen = () => {
console.log('연결됨');
ws.send(JSON.stringify({ type: 'subscribe', channel: 'updates' }));
};
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log('받은 데이터:', data);
};
ws.onclose = (event) => {
console.log('연결 종료:', event.code, event.reason);
};
// 메시지 전송
ws.send(JSON.stringify({ type: 'chat', message: '안녕하세요' }));서버 (Node.js ws 라이브러리)
javascript
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });
wss.on('connection', (ws) => {
console.log('클라이언트 연결됨');
ws.on('message', (message) => {
const data = JSON.parse(message);
// 모든 클라이언트에 브로드캐스트
wss.clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN) {
client.send(JSON.stringify(data));
}
});
});
ws.on('close', () => {
console.log('클라이언트 연결 종료');
});
});장단점
| 장점 | 단점 |
|---|---|
| 진정한 양방향 통신 | 구현 복잡도 높음 |
| 낮은 지연시간 | 연결 관리 필요 (재연결 직접 구현) |
| 헤더 오버헤드 최소 | 일부 프록시/방화벽 문제 |
| 바이너리 데이터 효율적 | 상태 유지 필요 (Stateful) |
| 대규모 동시 연결 가능 | 수평 확장 시 추가 인프라 필요 |
핵심 비교표
| 항목 | Polling | SSE | WebSocket |
|---|---|---|---|
| 통신 방향 | 단방향 (요청-응답) | 단방향 (서버→클라이언트) | 양방향 |
| 프로토콜 | HTTP | HTTP | WS/WSS |
| 연결 | 매번 새로 | 유지 | 유지 |
| 실시간성 | 낮음 | 높음 | 매우 높음 |
| 서버 부하 | 높음 | 중간 | 낮음 |
| 구현 난이도 | 쉬움 | 중간 | 어려움 |
| 자동 재연결 | 없음 | 내장 | 직접 구현 |
| 바이너리 | 비효율 | 비효율 | 효율적 |
| 방화벽 | 문제없음 | 문제없음 | 간혹 문제 |
사용 시나리오별 권장
Polling이 적합한 경우
실시간성이 중요하지 않은 경우 (분 단위 업데이트)
레거시 시스템 호환이 필요한 경우
서버리스 환경에서 장시간 연결 유지가 어려운 경우
SSE가 적합한 경우
서버에서 클라이언트로의 푸시만 필요한 경우
실시간 알림, 뉴스피드, 주식 시세
AI 스트리밍 응답 (ChatGPT, Claude 등)
간단한 실시간 대시보드
WebSocket이 적합한 경우
양방향 실시간 통신이 필요한 경우
채팅 애플리케이션
멀티플레이어 게임
협업 도구 (동시 편집)
실시간 트레이딩 시스템
실무 팁
SSE 연결 수 제한 우회
브라우저별 도메인당 SSE 연결 제한(6개)이 있다. HTTP/2를 사용하면 멀티플렉싱으로 해결 가능하다.
WebSocket 재연결 전략
javascript
function connect() {
const ws = new WebSocket(url);
ws.onclose = () => {
// 지수 백오프로 재연결
setTimeout(connect, Math.min(1000 * 2 ** retryCount, 30000));
retryCount++;
};
ws.onopen = () => {
retryCount = 0;
};
}하이브리드 접근
Socket.IO 같은 라이브러리는 WebSocket을 기본으로 사용하되, 불가능한 환경에서 자동으로 Long Polling으로 폴백한다.
