TL;DR
- SSE는 HTTP 응답 스트림 위에서 서버가 클라이언트로 이벤트를 보내기 위한 이벤트 스트림 프레이밍 규격이다.
- NDJSON은 JSON 값을 LF(
\n) 또는 CRLF(\r\n) 줄 구분자로 하나씩 나열하는 줄 단위 데이터 포맷이다.- 주요 상용 LLM API에서는 보통 SSE 이벤트의
data:필드 안에 JSON payload를 담아 전송한다.- 구조는 NDJSON과 비슷해 보이지만,
data:필드와 SSE 이벤트 프레이밍을 사용하는 순간 전체 스트림은 NDJSON이 아니라 SSE다.
SSE vs NDJSON 비교
두 기술은 모두 줄 단위로 읽히는 스트리밍 형태를 만들 수 있지만, 작동하는 레이어와 목적이 다르다. SSE는 HTTP 기반 이벤트 스트림의 프레이밍 규칙이고, NDJSON은 전송 방식과 독립적인 데이터 직렬화 포맷이다.
1. 기술적 차이 비교
| 항목 | SSE (Server-Sent Events) | NDJSON (Newline Delimited JSON) |
|---|---|---|
| 역할 | HTTP 이벤트 스트림 프레이밍 규격 | 줄 단위 데이터 포맷 |
| 미디어 타입 | text/event-stream | application/x-ndjson |
| 핵심 목적 | 서버에서 클라이언트로 이벤트를 순차 전달 | 대용량 데이터를 줄 단위 JSON 값으로 직렬화 |
| 기본 구조 | data:, event:, id: 등 필드 줄 + 빈 줄로 이벤트 경계 표시 | 순수 JSON 값 + LF(\n) 또는 CRLF(\r\n) |
| 연결성 | 지속적인 HTTP 응답 스트림을 전제로 함 | 파일, HTTP 응답 스트림, TCP 등 전송 방식과 독립적 |
| 기능 | 이벤트 타입, 이벤트 ID, 재연결 힌트, EventSource 기반 자동 재연결 | 줄 단위 스트리밍 파싱 용이성 |
2. 순수 NDJSON 스트리밍 vs SSE 스트리밍
SSE 프레이밍을 쓰지 않고도 HTTP 응답 스트림으로 NDJSON 레코드를 한 줄씩 전송할 수 있다. 이 경우 스트림의 의미는 SSE 이벤트가 아니라 “줄마다 하나의 JSON 값”이라는 NDJSON 규칙에서 나온다.
| 기능 | SSE 스트리밍 | 순수 NDJSON 스트리밍 |
|---|---|---|
| 포맷 | data: {…}\n\n | {“…”: “…”}\n |
| 브라우저 API | EventSource (표준 지원) | fetch() + ReadableStream 직접 구현 |
| 자동 재연결 | 지원 (브라우저 내장 기능) | 직접 구현 필요 |
| 이벤트 구분 | event: 필드로 명시적 구분 가능 | JSON 내부 필드로 직접 정의해야 함 |
| 전송 오버헤드 | 필드명(data: ) 등 소폭의 오버헤드 | 오버헤드 거의 없음 (순수 데이터만 전송) |
실제 전송 예시
- SSE 방식:
data: {"delta": "안"} data: {"delta": "녕"} - 순수 NDJSON 방식:
{"delta": "안"} {"delta": "녕"}
3. LLM Streaming에서의 SSE + JSON payload
주요 상용 LLM API(OpenAI, Anthropic 등)는 보통 SSE 이벤트의 data: 필드 안에 JSON payload를 싣는다. 이는 순수 NDJSON이 아니라 text/event-stream으로 해석되는 SSE 스트림이다.
- 프레이밍 (SSE): HTTP 연결을 열어두고
data:,event:같은 필드 줄과 빈 줄로 이벤트를 구분한다. - 내용물 (JSON payload):
data:필드 내부에 직렬화된 JSON payload를 담는다.
실제 전송 예시
event: message
data: {"id": "123", "choices": [{"delta": {"content": "Hello"}}]}
event: message
data: {"id": "123", "choices": [{"delta": {"content": " World"}}]}용어 정리
- SSE는 payload 형식을 강제하지 않는다.
data:안에는 일반 텍스트, JSON 문자열,[DONE]같은 관습적 종료 표식이 들어갈 수 있다. - NDJSON은 transport를 강제하지 않는다. 파일로 저장할 수도 있고, HTTP 응답 스트림으로 보낼 수도 있다. 중요한 조건은 각 줄이 독립적인 JSON 값이고, 줄 구분자는 LF(
\n) 또는 CRLF(\r\n)여야 한다는 점이다. data: {...}\n\n는 NDJSON이 아니다. JSON처럼 보이는 payload가 있어도data:필드와 빈 줄 이벤트 경계를 쓰면 전체 스트림은 SSE 프레이밍을 따른다.
Connections
- SSE (Server-Sent Events) — 기반 이벤트 스트림 프레이밍 규격
- NDJSON (Newline Delimited JSON) — 기반 데이터 포맷
- JSON 값과 JSON 객체의 차이 — NDJSON의 줄 단위를 JSON 객체가 아니라 JSON 값으로 설명해야 하는 이유
- UTF-8 — 두 방식 모두 표준으로 사용하는 인코딩


Discussion
Comments
댓글은 승인 후 공개됩니다.