Server-Sent Events (SSE)

"Server-Sent Events allows a web page to get updates from a server automatically."

— W3C 및 MDN의 공식 SSE 명세 정의서.

"웹소켓이 비싸고 거창한 양방향 광선검이라면, SSE는 물 한 바가지씩 서버에서 일방적으로 부어주는 양동이와 같다. 그런데 그 양동이만으로도 세상 실시간 기능의 80%는 충분히 커버된다." 어설프게 소켓 서버 올렸다가 인프라 터뜨리지 말고 얌전히 SSE에 이벤트 메시지나 실어 보냅시다.(...)

1. 개요

Server-Sent Events (SSE)는 웹 브라우저와 서버 간에 실시간 단방향 통신을 구현하기 위한 HTML5 표준 기술이다. 클라이언트가 서버로 단 한 번의 HTTP 요청을 보내 빨대를 꽂으면, 서버는 연결을 유지한 채 데이터를 text/event-stream 형식으로 끊임없이 밀어 넣어 준다.

과거에는 주식 전광판이나 실시간 스포츠 알림용으로 소소하게 쓰였으나, 최근 ChatGPT로 대변되는 LLM 생성형 AI 생태계가 메가히트를 기록하면서 위상이 수직 상승했다. AI가 생성하는 긴 답변 텍스트를 한 자 한 자 실시간으로 타이핑 치듯 브라우저에 뿌려주는 기술적 정체가 바로 이 SSE 스트리밍이기 때문이다.(...)

2. Uni-directional 실시간 스트리밍 메커니즘

2.1. 단방향 라이브 스트리밍 (Uni-directional Streaming)

전이중 양방향 통신을 보장하는 웹소켓과 달리, SSE는 철저히 서버에서 클라이언트로만 데이터가 흐른다. 클라이언트는 처음에 한 번만 빨대(EventSource)를 꽂아 요청을 보낼 뿐, 이후 데이터를 보낼 때는 일반 HTTP POST 등의 요청을 별도로 활용해야 한다.

2.2. 텍스트 기반 포맷 (text/event-stream)

SSE는 웹소켓처럼 복잡한 바이너리 프레임이 아닌, 순수한 텍스트 데이터를 사용한다. 서버의 응답은 다음과 같은 극도의 정형화된 형태를 띤다:

event: user_join
data: {"name": "Globin"}
id: 1

event: message
data: Hello World!
id: 2

순수 텍스트 프로토콜이기 때문에, 서버 로그에 그대로 찍히며 개발자 도구의 네트워크 탭에서도 아주 손쉽게 패킷을 뜯어보고 디버깅할 수 있어 백엔드 개발자들의 마음에 평온을 선사한다.

2.3. 자동 재연결 (Reconnections) 지원

SSE는 기본 프로토콜 스펙 단에서 자동 재연결(Auto Reconnection) 메커니즘을 기본으로 내장하고 있다. 네트워크가 끊기더라도 웹 브라우저가 알아서 지정된 주기마다 서버에 재접속을 시도하며, 서버가 마지막으로 보낸 이벤트 ID(Last-Event-ID) 헤더를 다시 동봉하여 유실된 이벤트부터 누락 없이 똑똑하게 이어받을 수 있게 해 준다.1

3. 웹소켓(WebSocket) vs Server-Sent Events (SSE)

비교 항목 웹소켓 (WebSocket) SSE (Server-Sent Events)
통신 방향 양방향 (Full-Duplex) 단방향 (Server -> Client)
기반 프로토콜 전용 ws/wss 프로토콜 기존 HTTP / HTTPS 표준
데이터 포맷 이진(Binary) 및 텍스트 데이터 모두 가능 오직 텍스트 (UTF-8) 만 가능
재연결 지원 직접 수동으로 자바스크립트 구현 필요 브라우저가 기본 자동 재연결 수호
커넥션 한계 HTTP 버전의 무관한 소켓 한도 HTTP/1.1 하에서 브라우저당 최대 6개 제한

3.1. HTTP/1.1 커넥션 제한이라는 악마의 함정

SSE의 가장 끔찍한 함정은 브라우저의 HTTP/1.1 동시 커넥션 제한에 있다. 크롬 등 현대 브라우저는 동일한 도메인으로 동시에 열 수 있는 HTTP/1.1 커넥션을 최대 6개로 엄격히 제한한다. 만약 사용자가 브라우저 탭을 6개 이상 띄워 놓고 모든 탭에서 SSE 커넥션을 무지성으로 열게 되면, 7번째 탭부터는 아예 웹사이트 로딩 자체가 영구 차단되어 먹통이 되는 미스터리한 장애가 일어난다.2 이 악랄한 제약 조건을 극복하기 위해서는 인프라에 반드시 HTTP/2 혹은 HTTP/3를 강제 적용해야만 한다.3

4. SSE와 ChatGPT 스트리밍 드립

4.1. ChatGPT 타이핑 효과의 뒤편

대다수 일반 사용자는 ChatGPT가 답변을 한 글자씩 타이핑할 때마다 마치 인공지능이 실시간으로 뇌를 쥐어짜며 생각하는 속도라고 착각한다. 하지만 실상은 백엔드에서 답변 전체를 다 만들 때까지 기다리기엔 타임아웃(Timeout)이 나거나 사용자 경험이 박살 나므로, SSE를 타고 나오는 텍스트 조각들을 자바스크립트로 한 땀 한 땀 화면에 흩뿌려 주는 고도의 UI 연출 쇼에 가깝다. 이를 알 리 없는 주니어 개발자들은 "실시간 렉 걸린 연출 어떻게 해요?"라며 프론트엔드 단에서 setTimeout을 돌리는 바보 같은 삽질을 반복하곤 한다.(...)

5. 여담

  • 이벤트 리스너의 간편함: 자바스크립트에서 SSE는 const source = new EventSource('/stream') 단 한 줄로 연결이 끝난다. 메시지를 받을 때도 source.onmessageaddEventListener를 써서 일반 웹 이벤트 다루듯 직관적으로 이벤트를 분기 처리할 수 있어 코드가 극단적으로 짧아진다.
  • 사파리(Safari)의 지독한 뒤끝: 애플의 사파리 브라우저는 역사적으로 SSE를 표준 명세에 오랫동안 추가해주지 않고 홀대하다가, 개발자들의 끊임없는 쌍욕을 얻어먹고 나서야 뒤늦게 완벽히 지원하기 시작했다. 역시 21세기형 익스플로러다운 행보다.(...)
  • 기업 방화벽 돌파의 수호신: 많은 기업이나 공공기관 방화벽은 웹소켓용 ws:// 프로토콜을 '보안 위협'이라 판단하여 무참히 차단하지만, SSE는 일반 웹 서비스용 포트(포트)인 80/443과 동일한 HTTP/S 프로토콜을 그대로 쓰기 때문에 방화벽을 소리소문없이 무조건 통과하는 강점을 지닌다.

6. 관련 문서

각주

  1. 서버가 에러로 꺼지거나 로드밸런서에 의해 다른 서버로 커넥션이 넘어갈 때 Last-Event-ID는 유실 없는 메시지 수신을 보증하는 은총과도 같다.

  2. 실제로 사내 인프라를 대충 구상한 개발자가 사내 모니터링 대시보드를 SSE로 만들어 배포했다가, 전 사원의 크롬 브라우저가 동시에 뻗어버리는 대참사를 일으킨 실화가 수두룩하다.

  3. HTTP/2 이상부터는 멀티플렉싱(Multiplexing)이 지원되므로, 하나의 커넥션 안에 최대 100개 이상의 SSE 스트림을 가볍게 실어 나를 수 있다.