🔖Contents
1. HTTP 스트리밍
HTTP 요청-응답 모델에서는 클라이언트가 요청을 보내면 서버가 전체 응답을 한 번에 전송하고 연결을 종료하는 경우가 일반적입니다.
HTTP 스트리밍은 서버가 연결을 유지한 채, 응답 본문을 시간에 따라 조금씩(점진적으로) 전송합니다.
서버에서 생성되는 데이터(이벤트, 로그, 진행률 등)를 즉시 클라이언트에 전달할 수 있어 실시간 업데이트에 유리합니다.
1.1 HTTP 스트리밍 작동 방식
1) 청크 전송 인코딩
HTTP 스트리밍을 구현하는 한 가지 방법은 "청크 전송 인코딩"을 사용하는 것입니다. 서버는 응답의 전체 크기를 미리 알 필요 없이 데이터를 청크 단위로 전송합니다. 각 청크는 준비되는 즉시 전송되므로 지연을 줄이고, 스트리밍 형태의 응답이 가능합니다.

RFC 9112
청크 전송 인코딩(Chunked Transfer Encoding)은 HTTP/1.1에서 도입된 전송 방식으로, 응답 본문의 전체 크기를 미리 알 수 없을 때 데이터를 여러 조각(청크)으로 나누어 스트리밍처럼 전송하는 방법입니다.

2) 서버 전송 이벤트(SSE)
또 다른 방식으로 서버 전송 이벤트(SSE)가 있습니다. SSE는 서버가 클라이언트로 이벤트를 스트리밍하기 위해 설계된 상위 수준의 이벤트 스트림 프로토콜입니다. 서버는 지속적인 연결을 유지하며, 구조화된 형식의 텍스트 이벤트를 클라이언트로 푸시합니다.
실시간 알림, 뉴스/로그 스트리밍, 서버가 클라이언트로 데이터를 지속적으로 푸시해야 하는 단방향 실시간 통신에서 널리 사용됩니다.

RFC 8895
Server-Sent Events (SSE)는 서버가 클라이언트(브라우저)로 실시간 이벤트를 지속적으로 푸시하는 단방향 통신 기술입니다.
HTTP 연결을 열어둔 채 서버에서 클라이언트로 데이터를 스트리밍할 수 있습니다.

1.2 HTTP 스트리밍을 흉내낸 방식
폴링과 롱 폴링은 ‘그냥 HTTP’이며, HTTP를 실시간처럼 쓰는 기법일 뿐 스트리밍 기술은 아닙니다.
1) 폴링
HTTP 폴링 방식은 전통적인 AJAX 애플리케이션에서 사용되던 방식으로, 일정 주기마다 서버로 HTTP 요청을 보내 데이터를 가져옵니다. 새로운 데이터가 있든 없든 주기적으로 요청과 응답을 반복합니다.

2) 롱 폴링
롱 폴링은 폴링과 유사하지만, 서버가 요청을 받았을 때 바로 응답할 데이터가 없다면 일정 시간 응답을 지연시킵니다.
그리고 데이터가 생기는 순간 응답하거나, 타임아웃 시 빈 응답을 내려줄 수 있습니다. 클라이언트는 응답을 받는 즉시(보통) 다시 요청을 보내며, 결과적으로 “긴 요청을 유지했다가 이벤트가 발생하면 응답”하는 형태로 동작합니다.

또한 HTTP 연결 재사용(keep-alive)을 통해 매 요청마다 TCP 연결을 새로 맺는 오버헤드를 줄일 수 있습니다.
이는 폴링에도 적용되지만, 특히 요청 간격이 짧은 롱 폴링에서 더 효과적일 수 있습니다.
다만 롱 폴링 역시 요청을 반복해서 보내야 하므로 HTTP 헤더 오버헤드가 존재합니다. 업데이트가 매우 빈번한 상황에서는 “요청하자마자 응답 → 즉시 다음 요청”이 반복되어 폴링과 체감상 큰 차이가 없어질 수 있습니다.
1.3 SSE와 비교
| 특성 | SSE (Server-Sent Events) | WebSocket | Polling (Short & Long) |
| 통신 방향 | 서버 → 클라이언트 (단방향) | 클라이언트 ↔ 서버 (양방향) | 클라이언트 요청 → 서버 응답 |
| 프로토콜 | HTTP | WS (WebSocket 프로토콜) HTTP 핸드셰이크 후 업그레이드 |
HTTP |
| 연결 유지 | 하나의 HTTP 연결 지속 ( text/event-stream) |
전용 TCP 연결 (전이중 통신) | 요청마다 연결 생성/해제 또는 일정 시간 유지 |
| 구현 복잡성 | 간단 (표준 HTTP) | 복잡 (별도 라이브러리/서버 설정) | 간단 (정기적 요청) |
| 인프라 호환성 | HTTP 인프라에 친화적 | 일부 인프라에서 제한적 | HTTP 인프라에 친화적 |
| 자원 효율성 | 비교적 효율적 (단방향 지속 연결) |
높은 자원 효율 (지속 연결) | 낮음 (잦은 요청/응답) |
| 자동 재연결 | 브라우저 내장 지원 ( EventSource) |
수동 구현 또는 외부 라이브러리 필요 | 해당 없음 |
| 데이터 형식 | 주로 텍스트 (UTF-8 인코딩) | 텍스트 및 바이너리 모두 지원 | 텍스트 기반 |
| 주요 용도 | 실시간 뉴스 로그 스트리밍 주식 시세 등 | 채팅 게임 협업 편집 등 양방향 | 단순 주기적 갱신 트래픽 적은 환경 |
2. SSE 이해

- 서버에서 클라이언트로 실시간 데이터를 단방향으로 지속적으로 보내는 기술
- 연결이 이뤄지면 Client 의 별도 추가 요청 없어도 서버가 event를 전송(push) 한다.
- HTTP 프로토콜을 사용한다.
SSE 의 사용 목적
- 웹 브라우저에서 새로고침이나 조작 없이 실시간으로 데이터 업데이트, 알림 등을 구현할 때 사용
SSE의 장점
| 장점 항목 | 설명 |
| 단순한 구현 | - WebSocket에 비해 서버 설정 및 코딩이 간단함 |
| HTTP 기반 | - 기존 HTTP 인프라(로드 밸런서, 프록시, 방화벽)와 호환성이 좋음 - 별도 포트/프로토콜 전환 부담이 적음 |
| 자원 효율성 | - 단방향 통신에 최적화되어 비교적 자원 소모가 적음 |
| 자동 재연결 | - 브라우저의 EventSource 객체에 자동 재연결 기능 내장 |
| 명확한 의미론 | - 단방향 데이터 흐름이 직관적이라 애플리케이션 로직이 단순함 |
SSE의 단점
| 단점 항목 | 설명 |
| 단방향 통신 | - 서버 → 클라이언트만 가능 - 클라이언트 → 서버는 별도 HTTP 요청 필요 |
| 텍스트 기반 | - 기본적으로 텍스트 위주 - 바이너리 전송은 인코딩(Base64 등) 필요 |
| HTTP/1.1 연결 제한 | - 브라우저/호스트당 동시 연결 수 제한(보통 6개)이 존재할 수 있음 |
2.1 동작 방식
1)클라이언트 요청
- 클라이언트가 SSE 연결 요청을 보냄
- 요청을 보낼 때 HTTP 요청 헤더에는
Accept: text/event-stream포함 - 이는 곳 “이제부터 서버가 무슨 일이 생기면 계속 알려줘. 나는 듣기만 할게”라는 의미
2)서버 응답 및 연결 유지
- 서버가 요청을 수락하고 응답함
- 응답할 때는
Content-Type을text/event-stream로 설정 Connection헤더를 포함하여 연결 유지- 하나의 TCP 연결을 유지하면서 데이터 전송 가능
3)실시간 메시지 전송
- 서버에서 이벤트 발생 시 메시지를 클라이언트에 전송
- 클라이언트는 수신한 데이터로 필요한 작업 수행 (예: 화면 업데이트)
- 클라이언트 → 서버 방향의 통신은 별도 HTTP 요청 필요
4)자동 재연결
- 연결이 끊기면 클라이언트가 자동으로 재연결 시도
EventSource객체가 재접속 기능 내장- 메시지에
id가 있다면, 재접속 시Last-Event-ID를 통해 이어받을 수 있음
5)연결 종료
- 클라이언트 또는 서버가 명시적으로 연결을 종료할 수 있음
- 불필요한 연결을 정리하여 자원 낭비를 방지
HTTP Connection 유지 방법
- HTTP/1.0:
Connection: keep-alive로 연결 유지 - HTTP/1.1 이상: 기본적으로 keep-alive가 기본이므로 별도 설정이 필요 없는 경우가 많음
2.2 HTTP 헤더
| 구분 | 설명 |
| Request Header | 헤더에 Accept: text/event-stream을 포함 |
| Response Header | 서버는 Content-Type: text/event-stream 및 Cache-Control: no-store를 포함하여 응답 |
Stream Event Format
| 항목 | 설명 |
| data | - data:로 시작하며 전송할 텍스트 데이터를 담음 |
| id | - id:로 시작하며 메시지에 고유 ID 부여- 재접속 시 이 ID로 이어받기 가능 |
| event | - event:로 시작하며 이벤트 타입 명시- 타입별 처리 가능 |
| retry | - retry:로 시작하며 재접속 시도 간격(ms) 지정 |
- 주석:
:로 시작하는 줄은 주석이며 클라이언트에서 무시됨 - 메시지 구분: 각 메시지는
\n\n(두 번 줄바꿈)으로 구분됨 - 인코딩: UTF-8
3. SSE와 청크 전송 인코딩 관계
Spring Boot(Spring MVC)에서 제공하는 SseEmitter로 SSE를 구현한다고 가정하겠습니다.
HTTP/1.1 환경에서 SSE는 내부적으로 청크 전송 인코딩(Chunked Transfer Encoding)을 통해 스트리밍 응답이 전달될 수 있고, HTTP/2에서는 DATA 프레임 기반으로 스트림이 전달됩니다. (HTTP/3에서의 SSE 동작도 확장 주제로 학습하면 좋습니다.)
1. 청크 전송 인코딩을 사용하는 이유
HTTP 응답 본문을 전송할 때는 보통 다음 중 하나를 선택합니다.
Content-Length를 명시: 전체 크기를 미리 알리고, 그만큼 전송Transfer-Encoding: chunked: 전체 크기를 알 수 없으니, 데이터가 생길 때마다 조각(청크)으로 전송
SSE는 서버가 언제, 얼마나 많은 데이터를 보낼지 미리 알 수 없는 지속적인 스트림입니다. 따라서 응답 전체 길이를 나타내는 Content-Length를 사전에 확정하기 어렵고, HTTP/1.1에서는 이런 경우 스트리밍 전송을 위해 청크 전송 인코딩이 활용됩니다.
2. Spring Boot SseEmitter의 동작 과정
Spring MVC에서 SseEmitter를 반환하면 일반적으로 다음 흐름으로 동작합니다.
- 헤더 설정: Spring이
Content-Type: text/event-stream을 설정 - 전송 방식 결정: HTTP/1.1 연결이면 서버(예: Tomcat)가 상황에 따라
Transfer-Encoding: chunked를 사용 - 데이터 푸시:
emitter.send()호출 시, SSE 포맷(data: Hello\n\n)으로 구성된 이벤트가 클라이언트로 전송됨
(참고) 실제 “이벤트 하나 = 청크 하나”로 딱 떨어지기보다는, 서버, 컨테이너, 버퍼링 조건에 따라 묶이거나 쪼개질 수 있습니다.
다만 “길이를 모르는 응답을 끊어서 흘려보낸다”는 큰 원리는 동일합니다.
3.1 HTTP 버전에 따른 차이
최신 환경에서는 사용하는 프로토콜에 따라 전송 방식이 달라질 수 있습니다
- HTTP/1.1: Chunked Transfer Encoding 기반으로 SSE가 동작할 수 있음
- HTTP/2:
Transfer-Encoding: chunked개념이 없고, HTTP/2 스트림의 DATA 프레임으로 전송됨(헤더에chunked가 나타나지 않음)
| 구분 | HTTP/1.1 + SSE | HTTP/2 + SSE |
| SSE 포맷 | 동일 (text/event-stream) |
동일 |
| 전송 방식 | Chunked Transfer Encoding | HTTP/2 Stream(Frame) |
| Content-Length | 없음 | 없음 |
| 헤더에 chunked | 보임 | ❌ 없음 |
| 중간 버퍼링 영향 | 큼 | 적음 |
| 안정성 | 상대적으로 약함 | 더 안정적 |
정리하면, SSE 자체(포맷과 의미)는 동일하지만, HTTP 레벨의 전송 메커니즘이 다르게 동작합니다.
HTTP/1.1 SSE 헤더
SSE는 “끝이 정해지지 않은 응답”이므로 Content-Length를 두기 어렵습니다.
따라서 HTTP/1.1에서는 스트리밍 전송을 위해 Transfer-Encoding: chunked가 사용되는 경우가 많습니다.
HTTP/1.1 200 OK
Content-Type: text/event-stream
Transfer-Encoding: chunked
chunk
data: event 1\n\n // 이벤트 하나 = 하나의 chunk (보통)
chunk
data: event 2\n\n
chunk
data: event 3\n\n
HTTP/2 SSE 헤더
HTTP/2에는 chunked 개념이 없기 때문에 Transfer-Encoding: chunked가 나타나지 않습니다.
데이터는 HTTP/2의 프레임(DATA frame) 단위로 스트림을 통해 계속 흘러갑니다.
:status: 200
content-type: text/event-stream
DATA frame
data: event 1\n\n // 하나의 요청 = 하나의 Stream
DATA frame
data: event 2\n\n
HTTP/1.1에서는 SSE 연결이 연결 자원을 상대적으로 더 점유할 수 있지만, HTTP/2에서는 스트림 멀티플렉싱 덕분에 동시 처리에 더 유리합니다.
HTTP 스트리밍 개념과 흉내내는 방식 그리고 SSE를 살펴봤습니다. HTTP 스트리밍이 무엇인지부터 시작해서, HTTP/1.1에서 스트리밍을 가능하게 해주는 청크 전송 인코딩, 그리고 애플리케이션 레벨에서 실시간 이벤트 전송을 표준화한 SSE까지 흐름으로 정리해봤습니다.
핵심은 “SSE는 텍스트 이벤트 포맷이고, 실제 전송은 HTTP 버전에 따라 달라진다”는 점입니다. HTTP/1.1에서는 길이를 알 수 없는 응답을 chunked로 흘려보내고, HTTP/2에서는 같은 SSE 포맷이 DATA frame으로 전달됩니다. SSE를 적용할 때 반드시 부딪히는 주제(SseEmitter 사용 방법, 버퍼링, 타임아웃, 재연결/Last-Event-ID, 연결 수 관리, 로드밸런서/프록시 설정)를 중심으로, Spring에서 안정적으로 운영하는 포인트를 정리해보겠습니다.
'🌎Http' 카테고리의 다른 글
| HTTP: HTTP와 TCP 관계 (1) | 2026.01.13 |
|---|