기타

Server-Sent Events(SSE) 완전 가이드

IT개발지식창고 2025. 8. 18. 11:26
반응형

🚀 Server-Sent Events(SSE) 완전 가이드

웹 애플리케이션에서 서버가 실시간으로 클라이언트에 이벤트를 푸시(push)해야 할 때, Server-Sent Events(SSE)가 간단하면서도 효율적인 솔루션이 됩니다. 오늘은 SSE의 개념, 작동 방식, 브라우저 지원, 구현 예제, 주의사항까지 블로그 글 형태로 자세히 알아보겠습니다.


1. SSE란 무엇인가?

Server-Sent Events(SSE)는 HTML5에서 도입된 기술로, 클라이언트가 HTTP 연결을 열어두면 서버가 이 연결을 통해 텍스트 기반 이벤트 스트림을 지속적으로 전송할 수 있게 합니다.

  • 단방향 스트리밍: 서버 → 클라이언트
  • 텍스트 기반: 간단한 텍스트 프로토콜로 메시지 전송
  • 자동 재연결: 네트워크 오류 시 자동으로 재접속 시도

2. 왜 SSE를 사용하나?

  1. 간편한 구현

    • HTTP 표준을 따르므로 별도의 WebSocket 핸드셰이크가 필요 없습니다.
    • API도 매우 단순해 클라이언트와 서버 코드를 몇 줄만 작성하면 됩니다.
  2. 브라우저 내장 지원

    • `` 태그 하나로 바로 사용 가능.
    • EventSource 인터페이스 제공.
  3. 자동 재연결 & 메시지 ID 관리

    • 네트워크 끊김 시 자동으로 재접속을 시도합니다.
    • 마지막으로 받은 이벤트 ID를 서버에 전달해 중복 없는 메시지 수신 보장.
  4. 낮은 오버헤드

    • 텍스트 스트림을 사용해 메시지 헤더/푸터가 WebSocket보다 가볍습니다.

3. SSE 동작 원리

  1. 클라이언트가 EventSource 객체 생성
  2. 브라우저가 서버에 HTTP GET 요청(특수 헤더 포함)
  3. 서버는 Content-Type: text/event-stream 헤더와 함께 텍스트 스트림 전송
  4. 클라이언트는 스트림을 파싱해 message, event, id, retry 필드 처리
  5. 연결이 끊기면 클라이언트는 retry 값(또는 기본 3초) 후 재접속 시도

4. 브라우저 지원 현황

  • Chrome, Firefox, Safari, Edge(Chromium) 모두 지원
  • IE는 지원하지 않음(폴리필 필수)

5. 클라이언트 구현 예제

// 클라이언트 코드 (JavaScript)
const evtSource = new EventSource('/sse-stream');

// 기본 메시지 수신
evtSource.onmessage = (event) => {
  console.log('메시지:', event.data);
  display(event.data);
};

// 커스텀 이벤트 수신
evtSource.addEventListener('customEvent', (event) => {
  console.log('customEvent:', event.data);
  handleCustom(JSON.parse(event.data));
});

// 오류 처리 및 재연결
evtSource.onerror = (err) => {
  if (evtSource.readyState === EventSource.CLOSED) {
    console.error('연결이 닫혔습니다.');
  } else {
    console.error('일시적 오류, 재연결 시도 중...', err);
  }
};

6. 서버 구현 예제

Node.js + Express

const express = require('express');
const app = express();

app.get('/sse-stream', (req, res) => {
  res.set({
    'Content-Type': 'text/event-stream',
    'Cache-Control': 'no-cache',
    Connection: 'keep-alive'
  });
  res.flushHeaders();

  let id = 0;
  const sendEvent = () => {
    id++;
    res.write(`id: ${id}\n`);
    res.write(`event: message\n`);
    res.write(`data: 안녕하세요! 현재 서버 시간은 ${new Date().toLocaleTimeString()} 입니다.\n\n`);
  };

  // 5초마다 이벤트 전송
  const interval = setInterval(sendEvent, 5000);

  // 클라이언트 연결 해제 시 정리
  req.on('close', () => {
    clearInterval(interval);
    res.end();
  });
});

app.listen(3000, () => {
  console.log('SSE 서버가 http://localhost:3000 에서 실행 중...');
});

Python + Flask

from flask import Flask, Response
import time

app = Flask(__name__)

@app.route('/sse-stream')
def sse_stream():
    def generate():
        id = 0
        while True:
            id += 1
            yield f"id: {id}\n"
            yield "event: message\n"
            yield f"data: 서버 시간이 {time.strftime('%H:%M:%S')} 입니다.\n\n"
            time.sleep(5)
    return Response(generate(), mimetype='text/event-stream')

if __name__ == '__main__':
    app.run(port=5000, threaded=True)

7. 주요 필드 설명

  • id: 마지막으로 받은 메시지 ID. 클라이언트 재접속 시 Last-Event-ID 헤더로 서버에 전달
  • event: 이벤트 이름. addEventListener로 개별 처리 가능
  • data: 실제 메시지 내용. 여러 줄 지원(각 줄 앞에 data:)
  • retry: 재접속 대기 시간(ms). 서버에서 동적 설정 가능
retry: 10000    // 클라이언트는 10초 후 재접속 시도

8. 주의사항 & 베스트 프랙티스

  1. Keep-Alive 설정

    • 프록시나 로드밸런서가 연결을 자동 종료하지 않도록 Connection: keep-alive 및 적절한 간격으로 빈 줄 전송
  2. 데이터 크기 주의

    • 대용량 메시지는 스트림 처리 성능에 영향. 필요 시 차분(diff) 데이터 전송
  3. 폴리필 사용

    • Internet Explorer 및 일부 구형 브라우저 대응을 위해 EventSource 폴리필 사용
  4. 동시 연결 제한

    • 브라우저마다 도메인당 연결 수 제한이 있으므로, 너무 많은 SSE 연결 생성하지 않도록 관리
  5. 보안

    • HTTPS 사용 권장
    • 인증이 필요한 경우, 토큰 기반 인증 또는 세션 쿠키 활용

9. 결론

Server-Sent Events는 단순성, 브라우저 내장 지원, 자동 재연결 등 장점을 가진 효율적인 서버 → 클라이언트 푸시 솔루션입니다. WebSocket 같은 복잡한 양방향 통신이 필요하지 않은 경우, SSE를 도입해 개발 생산성을 높이고 서버 부하를 낮춰보세요!

반응형