본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 06. 24. 02:10

Server-Sent Events(SSE)를 이용한 LLM 응답 스트리밍: 실시간 AI 채팅 인터페이스 구축하기

요약

Server-Sent Events(SSE)를 활용하여 LLM의 응답을 실시간으로 스트리밍하는 채팅 인터페이스 구축 방법을 설명합니다. WebSockets 대비 SSE의 장점과 Node.js 및 프론트엔드 구현 시 주의사항을 다룹니다.

핵심 포인트

  • SSE는 단방향 스트리밍에 최적화되어 구현이 단순하고 자동 재연결을 지원함
  • Nginx 환경에서는 X-Accel-Buffering 헤더 설정이 필수적임
  • OpenAI 호환 API의 [DONE] 센티널을 통한 스트림 종료 처리 방법 안내
  • 네트워크 단절 및 오류 발생 시 수동 재연결 로직의 중요성

Server-Sent Events(SSE)를 이용한 LLM 응답 스트리밍: 실시간 AI 채팅 인터페이스 구축하기

만약 당신이 LLM이 2,000단어 분량의 응답 생성을 마칠 때까지 15초 동안 로딩 스피너(loading spinner)를 바라보며 기다려 본 적이 있다면, 왜 스트리밍(streaming)이 중요한지 이미 알고 있을 것입니다. 사용자들은 당신의 백엔드(backend) 아키텍처에는 관심이 없습니다. 그들은 _단어가 나타나는 것을 보는 것_에 관심이 있습니다.

Server-Sent Events (SSE)는 LLM 출력을 브라우저로 스트리밍하는 표준 방식입니다. WebSockets도 아니고, 폴링(polling)도 아닙니다. 바로 SSE입니다. 이 가이드에서는 모든 OpenAI 호환 API 엔드포인트(endpoint)와 작동하는 프로덕션급 스트리밍 채팅 인터페이스를 구축합니다.

왜 WebSockets가 아니라 SSE인가?

WebSockets는 양방향(bidirectional)이며 강력하지만, LLM 스트리밍에는 과할 수 있습니다. 데이터 흐름은 단방향(one-directional)입니다. 서버가 토큰(tokens)을 밀어주면(push), 클라이언트가 이를 읽습니다. SSE는 복잡성을 줄이면서 정확히 그 기능을 제공합니다:

  • 브라우저에 내장된 자동 재연결 (Auto-reconnection)
  • 단순한 HTTP — 업그레이드 핸드셰이크(upgrade handshake)나 프로토콜 협상(protocol negotiation)이 필요 없음
  • 특별한 설정 없이도 프록시(proxies)를 통해 작동
  • 모든 현대적 브라우저에 내장된 EventSource API

유일한 실제 제한 사항은 SSE가 단방향(server → client)이라는 점인데, 이는 정확히 우리가 필요로 하는 방식입니다.

백엔드: Node.js + Express

다음은 OpenAI 호환 API로 요청을 프록시하는 최소한의 스트리밍 엔드포인트입니다:

const express = require("express");
const app = express();

...

주의해야 할 몇 가지 사항:

  • nginx 뒤에 있는 경우 X-Accel-Buffering: no 헤더가 매우 중요합니다. 이 헤더가 없으면 nginx가 전체 응답을 버퍼링(buffering)하여 LLM이 완료될 때까지 사용자는 아무것도 볼 수 없습니다.
  • 청크(chunks)가 경계선에서 JSON을 분할할 수 있기 때문에 data: 라인을 개별적으로 파싱(parse)합니다.
  • [DONE] 센티널(sentinel)은 OpenAI 호환 API가 스트림의 끝을 알리는 방식입니다.

프론트엔드: Vanilla EventSource

프레임워크는 필요하지 않습니다. (EventSource는 GET 방식만 지원하므로) ReadableStream과 Fetch API를 사용하는 전체 클라이언트 코드는 다음과 같습니다:

async function streamChat(userMessage) {
  const output = document.getElementById("chat-output");
  const bubble = document.createElement("div");
...

재연결 및 오류 처리 (Handling Reconnection and Errors)

프로덕션 시스템에는 정상적인 경로(happy path) 이상의 처리가 필요합니다. 여러분을 괴롭힐 세 가지 실패 모드는 다음과 같습니다:

1. 네트워크 끊김 (Network Drops)

SSE는 EventSource를 통해 기본적으로 자동 재연결을 지원하지만, fetch 스트리밍은 그렇지 않습니다. 스트리밍 도중 연결이 끊어지면 수동 복구가 필요합니다:

let retryCount = 0;
const MAX_RETRIES = 3;

...

2. 속도 제한 (Rate Limiting)

API는 속도 제한(rate limits)을 초과할 때 429 Too Many Requests를 반환합니다. 항상 Retry-After 헤더를 확인하세요:

if (response.status === 429) {
  const wait = parseInt(response.headers.get("Retry-After") || "5", 10);
  await new Promise((r) => setTimeout(r, wait * 1000));
...

3. 하트비트 유지 (Heartbeat Keep-Alive)

일부 로드 밸런서(load balancers)와 CDN 프록시(Cloudflare, AWS ALB)는 60~100초 동안 유휴 상태인 연결을 종료합니다. 만약 LLM이 첫 번째 토큰을 생성하는 데 오랜 시간이 걸린다면(추론 모델(reasoning models)에서 흔히 발생함), 데이터가 흐르기도 전에 프록시가 연결을 닫아버릴 수 있습니다.

주기적인 하트비트(heartbeats)로 이를 해결하세요:

// 서버 측: 15초마다 주석(comment) 전송
const heartbeat = setInterval(() => {
  res.write(": heartbeat\n\n");
...

주석(:로 시작하는 줄)은 SSE 명세의 일부이며 클라이언트에서는 조용히 무시되지만, TCP 연결을 활성 상태로 유지해 줍니다.

스트리밍을 위한 적절한 모델 선택 (Choosing the Right Model for Streaming)

모든 모델이 스트리밍을 동일하게 잘 수행하는 것은 아닙니다. 사용자는 초당 약 20토큰(tokens/second) 미만의 속도를 느리다고 인식하기 때문에 처리량(throughput)이 중요합니다. 비용 효율적인 스트리밍을 위해, aiwave.live는 DeepSeek, GLM, Qwen, ERNIE를 포함한 50개 이상의 모델에 대한 통합 액세스를 제공합니다. 이 모든 모델은 단일 OpenAI 호환 엔드포인트를 통해 제공되므로, 코드 한 줄 변경 없이 모델을 교체할 수 있습니다.

스트리밍 처리량 측면에서 비교해 볼 만한 몇 가지 모델은 다음과 같습니다:

모델 (Model)평균 토큰/초 (Avg Tokens/sec)컨텍스트 윈도우 (Context Window)비용 (1M 입력당) (Cost (per 1M input))
DeepSeek Chat~60128K$0.27
...
가격은 2026년 중반 기준 대략적인 수치이며 제공업체에 따라 다를 수 있습니다.

종합하기 (Putting It All Together)

다음은 모든 요소를 하나로 결합한 최소한의 HTML 페이지입니다:

<!DOCTYPE html>
<html lang="en">
<head>
...

핵심 요약 (Key Takeaways)

  • SSE는 LLM 스트리밍을 위한 적절한 도구입니다 — WebSockets보다 간단하며 브라우저에서 기본적으로 지원됩니다.
  • POST 요청이 필요한 경우 EventSource 대신 fetch + ReadableStream을 사용하세요.
  • 하트비트(Heartbeats)는 프록시 타임아웃을 방지합니다 — 15초마다 SSE 주석(comment)을 전송하세요.
  • 지수 백오프(Exponential backoff)를 사용하여 항상 재연결(reconnection)을 처리하세요.
  • 모델 처리량(Throughput)이 중요합니다 — 단순히 가격뿐만 아니라 초당 토큰 수(tokens/sec)를 비교하세요.

스트리밍은 AI 기반 애플리케이션에서 구현할 수 있는 가장 큰 UX(사용자 경험) 개선 사항입니다. 전체 응답을 받기 위해 10초를 기다리는 것과 단어들이 실시간으로 나타나는 것을 지켜보는 것의 차이는, 살아있는 것처럼 느껴지는 도구와 고장 난 것처럼 느껴지는 도구의 차이와 같습니다.

즐거운 스트리밍 되세요!

AI 자동 생성 콘텐츠

본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0