메인 콘텐츠로 건너뛰기

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: 3000
  • event: 이벤트 타입 (선택)

  • 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)
대규모 동시 연결 가능수평 확장 시 추가 인프라 필요

핵심 비교표

항목PollingSSEWebSocket
통신 방향단방향 (요청-응답)단방향 (서버→클라이언트)양방향
프로토콜HTTPHTTPWS/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으로 폴백한다.

#실시간 통신#폴링#SSE#WebSocket#서버-클라이언트