서버가 하나의 열린 연결로 이벤트를 계속 밀어 보내는 Server-Sent Events(SSE, `text/event-stream`)를 소비하는 레시피입니다.
서버가 하나의 열린 연결로 이벤트를 계속 밀어 보내는 Server-Sent Events(SSE, `text/event-stream`)를 소비하는 레시피입니다.
# -N disables buffering so events print as they arrive:
curl -N https://api.example.com/events \
-H 'Accept: text/event-stream'const es = new EventSource('https://api.example.com/events');
es.onmessage = (e) => console.log('data:', e.data);
es.addEventListener('ping', (e) => console.log('ping', e.data));
es.onerror = () => { /* browser auto-reconnects */ };
// es.close() to stopimport requests
with requests.get('https://api.example.com/events',
headers={'Accept': 'text/event-stream'},
stream=True) as r:
for line in r.iter_lines(decode_unicode=True):
if line.startswith('data:'):
print(line[5:].strip())SSE 응답은 `Content-Type: text/event-stream`이며, `data:`·`event:`·`id:`·`retry:` 필드로 이뤄진 텍스트 스트림을 빈 줄로 이벤트를 구분해 보냅니다. curl에서는 `-N`(`--no-buffer`)으로 버퍼링을 꺼야 이벤트가 도착하는 즉시 화면에 보입니다.
브라우저의 `EventSource`는 SSE 전용 API로, 파싱과 자동 재연결을 대신 처리합니다. 연결이 끊기면 자동으로 다시 붙고, 마지막으로 받은 `id:`를 `Last-Event-ID` 헤더로 보내 서버가 놓친 이벤트부터 재개하게 합니다. 단, `EventSource`는 GET만 지원하고 커스텀 헤더를 못 붙이는 제약이 있어, 인증 토큰이 필요하면 fetch 스트리밍으로 직접 구현하기도 합니다.
SSE는 서버→클라이언트 단방향입니다. 양방향 실시간이 필요하면 WebSocket을 쓰세요. 프록시·로드밸런서가 응답을 버퍼링하면 이벤트가 뭉쳐서 오므로, 서버는 `Cache-Control: no-cache`와 (nginx의 경우) `X-Accel-Buffering: no` 등으로 버퍼링을 꺼야 합니다.