업데이트됨 1개월 전
HTTP에는 기본적인 리듬이 있습니다: 요청, 응답, 완료. 클라이언트가 묻고, 서버가 답하고, 연결이 닫힙니다. 웹 페이지를 가져오는 데는 완벽하게 작동하지만, 실시간 애플리케이션에서는 문제가 생깁니다. 서버가 할 말이 있는데 클라이언트가 묻지 않는다면 어떻게 될까요?
서버 전송 이벤트는 그 리듬을 깨뜨립니다. 서버가 응답하고... 계속 응답합니다. 연결은 열린 채로 유지됩니다. 서버에 새로운 소식이 생기면—주가가 변했거나, 메시지가 도착했거나, 빌드가 완료됐거나—열린 통로를 통해 그 소식을 전달합니다. 폴링도 없고. 질문도 없습니다. 서버는 할 말이 있을 때 말합니다.
해결책의 형태
선거 결과를 지켜본다고 상상해 보세요. 기존 방식은 이렇습니다: 브라우저가 5초마다 "업데이트가 있나요?"라고 묻습니다. 서버는 대개 "없습니다"라고 답합니다. 대역폭이 낭비됩니다. 서버는 요청에 시달립니다. 그리고 결과가 바뀌더라도 최대 5초를 기다려야 볼 수 있습니다.
SSE는 이것을 뒤집습니다. 브라우저가 연결을 하나 열고 "뭔가 생기면 알려줘"라고 말합니다. 서버는 그 연결을 유지합니다. 결과가 바뀌면 즉시 전달합니다: "7구역 결과가 방금 나왔어요." 바로 볼 수 있습니다. 연결 하나. 낭비되는 요청 없음. 업데이트는 생기는 즉시 도착합니다.
WebSocket도 이 문제를 해결하지만, 양방향입니다—양쪽이 끊임없이 대화하는 채팅 애플리케이션을 위해 만들어졌습니다. SSE는 더 단순합니다: 그냥 끊기지 않는 HTTP입니다. 기존 프록시, 로드 밸런서, 캐싱 레이어가 이미 이해하고 있습니다.
작동 방식
클라이언트가 연결을 엽니다:
이게 전부입니다. 연결하는 한 줄, 메시지 수신 리스너 하나.
서버는 연결을 유지하면서 이벤트가 발생할 때마다 씁니다:
text/event-stream 콘텐츠 타입은 브라우저에게 이것이 SSE임을 알려줍니다. 이중 줄바꿈(\n\n)은 각 이벤트의 끝을 표시합니다. 그 외의 모든 것은 그냥 HTTP입니다.
이벤트 형식
SSE 이벤트는 간단한 구조의 평문 텍스트입니다:
data: 페이로드를 담습니다. 여러 data: 줄은 줄바꿈으로 합쳐집니다.
event: 사용자 정의 이벤트 타입의 이름을 지정합니다. 없으면 이벤트는 "message"로 발생합니다.
id: 재연결을 위해 이벤트에 태그를 붙입니다. 연결이 끊기면, 브라우저는 서버에게 "마지막으로 받은 것이 42번이었어요"라고 알리고, 서버는 놓친 것을 다시 전송할 수 있습니다.
retry: 재연결 지연 시간을 밀리초 단위로 제안합니다.
콜론으로 시작하는 주석은 까다로운 프록시를 통해서도 연결을 살려둡니다:
자동 재연결
이것이 SSE가 조용히 빛나는 부분입니다.
연결이 끊길 때—네트워크 불안정, 서버 재시작, 노트북이 절전 모드에서 깨어날 때—브라우저가 자동으로 재연결합니다. 코드가 필요 없습니다. 그냥 됩니다.
더 좋은 점: 이벤트에 ID가 있었다면, 브라우저는 재연결 시 Last-Event-ID: 42를 전송합니다. 서버는 놓친 이벤트를 다시 보낼 수 있습니다. 잠깐의 연결 끊김에도 데이터를 잃지 않습니다.
기본 재시도는 3초입니다. 서버는 retry: 필드로 연결별로 이를 조정할 수 있습니다.
사용자 정의 이벤트 타입
실제 애플리케이션에는 다양한 종류의 업데이트가 있습니다:
서버는 타입이 지정된 이벤트를 전송합니다:
각 타입에는 자체 리스너가 있습니다. 깔끔하게 분리됩니다.
SSE가 빛나는 곳
대시보드와 모니터링. 서버 지표, 애플리케이션 상태, 시스템 현황—이것들은 한 방향으로 흐릅니다. SSE는 업데이트가 생기는 즉시 전달합니다.
알림. 새 메시지, 멘션, 경고. 서버는 무언가 발생했을 때 즉시 알려줍니다.
실시간 피드. 뉴스, 스포츠 점수, 소셜 업데이트. 콘텐츠가 게시되는 즉시 나타납니다.
진행 상황 추적. 파일 업로드, 보고서 생성, 일괄 작업. 상태를 폴링하는 대신 완료 비율을 전달합니다.
공동 시청. 동일한 데이터를 보는 여러 사람이 동시에 업데이트를 볼 수 있습니다. (편집이 포함된 완전한 협업에는 보통 WebSocket이 필요합니다.)
한계
단방향만. SSE는 서버에서 클라이언트 방향입니다. 클라이언트는 일반 HTTP 요청(POST, PUT)으로 데이터를 전송합니다. 대부분의 경우 괜찮습니다—대부분의 앱은 서버 업데이트는 많이 필요하지만 클라이언트 작업은 가끔씩만 있습니다.
도메인당 6개 연결. HTTP/1.1에서 브라우저는 동시 연결을 제한합니다. API 도메인에 SSE 연결 하나를 사용하면 나머지 다섯 개가 남습니다. HTTP/2가 이를 해결하지만, 알아둘 만한 사항입니다.
텍스트만. 바이너리 데이터는 base64로 인코딩해야 합니다. 스트리밍 비디오나 대용량 파일에는 부담이 추가됩니다.
Internet Explorer 미지원. Edge는 지원합니다. Chrome, Firefox, Safari도 지원합니다. IE는 지원한 적이 없습니다. 폴리필이 존재하지만 복잡성이 더해집니다.
SSE vs. WebSocket
SSE를 선택할 때:
- 업데이트가 주로 서버에서 클라이언트로 흐를 때
- 최대한의 단순함을 원할 때
- 표준 HTTP 인프라가 중요할 때 (프록시, CDN, 로드 밸런서)
- ID를 통한 메시지 재생이 있는 자동 재연결이 유용할 때
WebSocket을 선택할 때:
- 양쪽이 빈번하게 메시지를 주고받을 때
- 클라이언트에서 서버로의 지연 시간이 중요할 때 (게임, 공동 편집)
- 원시 바이너리 데이터가 필요할 때
- 이미 WebSocket 인프라를 관리하고 있을 때
많은 애플리케이션이 둘 다 사용합니다: 쏟아지는 서버 업데이트에는 SSE, 가끔 있는 클라이언트 작업에는 표준 HTTP POST.
보안
SSE는 그냥 HTTP이므로, 표준 보안이 적용됩니다.
인증: 쿠키나 토큰이 정상적으로 작동합니다. EventSource 생성자는 크로스 오리진 쿠키를 위한 withCredentials를 허용합니다.
권한 부여: 연결이 열릴 때 반드시 권한을 확인하세요. 사용자가 봐서는 안 되는 이벤트는 절대 보내지 마세요.
CORS: 크로스 오리진 SSE에는 올바른 헤더가 필요합니다. Access-Control-Allow-Origin 등이 필요합니다.
HTTPS: 사용하세요. WebSocket에는 wss://, SSE에는 https://. 동일한 암호화, 동일한 인증서.
확장
모든 SSE 연결은 서버 메모리를 소비하는 열린 HTTP 연결입니다. 규모가 커지면 이것이 중요해집니다.
연결 한도: 서버가 얼마나 처리할 수 있는지 파악하세요. 최대 동시 사용자 수를 기준으로 용량을 계획하세요.
수평 확장: 사용자가 서로 다른 서버에 연결되어 있다면, 이벤트가 모든 서버에 도달해야 합니다. Redis Pub/Sub 또는 유사한 메시지 브로커가 이벤트를 모든 서버에 분산시킵니다.
고정 세션: 연결되면, 사용자는 동일한 서버에 머물러야 합니다 (또는 재연결을 매끄럽게 처리해야 합니다).
하트비트: 주기적으로 주석(: ping)을 보내 유휴 연결을 끊는 프록시를 통해서도 연결을 살려두세요.
디버깅
브라우저 개발자 도구는 네트워크 탭에 SSE를 표시합니다. 실시간으로 이벤트가 도착하는 것을 볼 수 있고, 연결 헤더를 확인하고, 연결 끊김을 발견할 수 있습니다.
EventSource에는 readyState 속성이 있습니다:
0= 연결 중1= 열림2= 닫힘
서버 전송 이벤트에 관해 자주 묻는 질문
SSE로 클라이언트에서 서버로 데이터를 보낼 수 있나요?
아니요. SSE는 단방향입니다—서버에서 클라이언트 방향만입니다. 클라이언트에서 서버로의 통신에는 표준 HTTP 요청(fetch, POST, PUT)을 사용하세요. 이것이 의도된 설계입니다: SSE는 "푸시" 방향을 담당하고, 일반 요청은 "풀"과 "전송" 방향을 담당합니다.
SSE 연결이 끊기면 어떻게 되나요?
브라우저가 자동으로 재연결합니다. 이벤트에 ID가 포함되어 있었다면, 마지막으로 받은 ID를 서버에 전송하고, 서버는 놓친 이벤트를 다시 재생할 수 있습니다. 재연결 로직을 작성할 필요가 없습니다—EventSource API에 내장되어 있습니다.
SSE와 롱 폴링은 어떻게 다른가요?
롱 폴링은 요청을 열고, 데이터를 기다리고, 받고, 닫고, 새 요청을 열기를 반복합니다. 각 응답마다 새 연결이 필요합니다. SSE는 연결 하나를 열어두고 서버가 시간이 지나면서 여러 이벤트를 씁니다. SSE가 더 효율적이고 지연 시간이 짧습니다.
SSE가 기업 프록시와 방화벽을 통과하나요?
일반적으로 그렇습니다. 표준 HTTP이기 때문입니다. 일부 까다로운 프록시는 응답을 버퍼링하거나 유휴 연결을 끊을 수 있습니다. 주기적으로 하트비트 주석(: ping)을 보내면 연결이 살아있습니다. HTTPS 연결은 간섭을 받을 가능성이 낮습니다.
SSE와 WebSocket 중 언제 SSE를 선택해야 하나요?
업데이트가 주로 서버에서 클라이언트로 흐를 때입니다. SSE는 구현이 더 단순하고, 기존 HTTP 인프라와 함께 작동하며, 내장 재연결 기능이 있고, 특별한 서버 소프트웨어가 필요 없습니다. 빈번한 양방향 통신이나 인코딩 부담 없는 바이너리 데이터가 필요할 때는 WebSocket을 사용하세요.
이 페이지가 도움이 되었나요?