🤖

ChatGPT 스트리밍은 어떻게 동작하나?

SSE로 토큰 단위 실시간 응답 전달

OpenAI의 Chat Completions API에 stream: true를 설정하면, 응답이 한꺼번에 오지 않고 SSE(Server-Sent Events) 형식으로 토큰이 생성될 때마다 전송됩니다. 클라이언트는 EventSource나 fetch + ReadableStream으로 이 스트림을 수신하며, 각 chunk는 data: {"choices":[{"delta":{"content":"안"}}]} 형식입니다. 마지막에 data: [DONE]이 오면 스트림 종료. 사용자는 LLM이 생각하는 동안 결과를 실시간으로 볼 수 있습니다.

구조 다이어그램

🌐
ChatGPT UI
fetch + ReadableStream
① POST stream:true
text/event-stream
data: {"delta":{"content":"안"}}
data: {"delta":{"content":"녕"}}
data: {"delta":{"content":"하"}}
data: {"delta":{"content":"세"}}
data: [DONE]
🧠
OpenAI API
LLM 토큰 생성
토큰 하나씩 생성 → 즉시 전송
화면에 보이는 모습:
핵심: LLM이 전체 답변을 완성할 때까지 기다리지 않고 <strong>토큰 생성 즉시 SSE로 전송</strong>
왜 SSE인가? (WebSocket 아닌 이유)
  • 요청은 1회(POST), 응답만 스트리밍 → 단방향이면 충분
  • HTTP 기반이라 CDN/프록시 호환성 좋음
  • 연결 끊김 시 새 요청으로 재시도 (stateless)
  • WebSocket 대비 서버 구현이 단순

동작 흐름

1

클라이언트가 POST /v1/chat/completions에 stream: true로 요청

2

서버가 Content-Type: text/event-stream으로 응답 시작

3

LLM이 토큰 생성 → data: {"delta":{"content":"안"}} 즉시 전송

4

클라이언트가 각 chunk를 받아 UI에 append

5

모든 토큰 생성 완료 → data: [DONE] 전송

6

클라이언트가 스트림 종료 처리

장점

  • 체감 응답 속도 대폭 향상 (TTFT 최소화)
  • LLM 전체 생성 완료를 기다릴 필요 없음
  • HTTP 기반이라 구현 단순
  • 중간에 취소 가능 (AbortController)

단점

  • 토큰 단위 처리 로직 필요
  • 에러 처리 복잡 (중간 끊김)
  • 총 토큰 수를 미리 알 수 없음
  • 클라이언트 버퍼링 관리 필요

사용 사례

ChatGPT / Claude 웹 UI AI 코딩 어시스턴트 AI 챗봇 인터페이스 문서 요약/번역 실시간 표시