실시간 AI 응답은 현대 애플리케이션의 핵심이 되었습니다. 채팅 인터페이스에서 타이핑 효과, 대시보드 실시간 업데이트, AI 어시스턴트와의 자연스러운 대화까지 —的背后는 모두 기술 선택이 있습니다.
이 튜토리얼에서는 WebSocket과 Server-Sent Events(SSE)의 차이를 깊이 분석하고, HolySheep AI 게이트웨이를 활용한 실제 구현 코드를 제공합니다. 특히 AI 응답 스트리밍에 최적화된 architecture를 구축하려는 개발자를 위한 실전 가이드입니다.
🚀 현실 문제: 왜 이 선택이 중요한가?
사례 1: 이커머스 AI 고객 서비스 급증
매일 5만 건의 고객 문의를 처리하는 이커머스 플랫폼이 있습니다. 기존 polling 방식으로는:
- 평균 응답 지연: 2~3초
- 서버 부하: 동시 접속자 1,000명 시 15,000 req/min
- 비용: 월 $800 (불필요한 API 호출 포함)
사례 2: 기업 RAG 시스템 출시
수천 개의 문서를 검색하는 RAG 시스템 도입 시:
- 긴 컨텍스트 응답 (최대 32K 토큰)
- 복잡한 멀티턴 대화
- 다양한 모델 지원 필요 (GPT-4, Claude, Gemini)
사례 3: 개인 개발자 사이드 프로젝트
무료 크레딧으로 시작하고 싶은 개발자:
- 비용 최적화 필수
- 빠른 프로토타이핑
- 확장성 고민
이 모든 사례에서 WebSocket과 SSE 중 어떤 것을 선택하느냐가 성능, 비용, 구현 복잡도를 좌우합니다.
📊 WebSocket vs SSE 심층 비교
| 비교 항목 | WebSocket | Server-Sent Events (SSE) |
|---|---|---|
| 통신 방향 | 양방향 (Full-duplex) | 단방향 (Server → Client) |
| 프로토콜 오버헤드 | 초기 handshake 후 헤더 없음 | 매 이벤트마다 HTTP 헤더 포함 |
| 연결 유지 | 영구 연결 (지속) | 영구 연결 (HTTP/1.1 keep-alive) |
| 재연결 메커니즘 | 수동 구현 필요 | 자동 재연결 (내장) |
| 브라우저 지원 | 모든 모던 브라우저 | IE 미지원 (polyfill 필요) |
| 프록시/파이어월 | 문제 발생 가능성 높음 | 표준 HTTP, 문제 없음 |
| AI 스트리밍 적합성 | ⭐⭐⭐⭐⭐ 완벽 | ⭐⭐⭐⭐ 매우 적합 |
| 구현 난이도 | 중간 (이중 통신 처리) | 낮음 (단순한 API) |
| 실제 지연 시간 | 평균 15~30ms | 평균 20~40ms |
| 동시 연결 수 (서버당) | ~65,000 (OS 제한) | ~6,500 (브라우저 제한) |
| 품질 | 바이너리/텍스트 모두 가능 | 텍스트만 가능 |
| AI 응답 시나리오 | 멀티턴 채팅, 양방향 툴 | 단일 응답 스트리밍, 알림 |
💡 AI 스트리밍에 적합한 선택 기준
// 의사결정 트리 (Decision Tree)
AI 사용 시나리오
├── AI → Client 단방향 스트리밍만 필요?
│ ├── ✅ SSE 선택 (단순하고 효율적)
│ └── ❌ 추가 요구사항 확인
│
├── Client → AI → Client 양방향 필요?
│ ├── 채팅 인터페이스 (멀티턴)
│ ├── 툴/플러그인 사용
│ └── 실시간 협업
│ └── ✅ WebSocket 선택
│
├── 단일 응답 생성 (한 번의 질문, 스트리밍 답변)
│ └── ✅ SSE가 적합 (오버헤드 적음)
│
└── 복잡한 상태 관리 필요?
└── ✅ WebSocket 선호
🔧 실전 구현: HolySheep AI + SSE
저는 HolySheep AI 게이트웨이를 사용하여 SSE 방식으로 AI 응답을 스트리밍하는 구현을 했습니다. HolySheep의 통합 API는 여러 모델을 단일 엔드포인트에서 지원하여 프로토타이핑 속도가 상당히 빠릅니다.
<!-- Client: HTML + JavaScript SSE 구현 -->
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>AI SSE Streaming Demo</title>
<style>
#response {
font-family: monospace;
padding: 20px;
min-height: 300px;
border: 1px solid #ddd;
border-radius: 8px;
white-space: pre-wrap;
}
.thinking {
color: #888;
font-style: italic;
}
</style>
</head>
<body>
<h2>SSE AI 스트리밍 데모</h2>
<input type="text" id="prompt" placeholder="질문을 입력하세요..."
style="width: 70%; padding: 10px;">
<button onclick="sendRequest()">전송</button>
<div id="response"></div>
<script>
let eventSource;
let fullResponse = '';
function sendRequest() {
const prompt = document.getElementById('prompt').value;
if (!prompt) return;
// 이전 연결 종료
if (eventSource) {
eventSource.close();
}
fullResponse = '';
document.getElementById('response').textContent = '생각 중...';
// SSE 연결 생성
// HolySheep AI base_url 사용
const encodedPrompt = encodeURIComponent(prompt);
eventSource = new EventSource(
https://api.holysheep.ai/v1/chat/completions +
?model=gpt-4.1 +
&messages=${encodedPrompt} +
&stream=true +
&api_key=YOUR_HOLYSHEEP_API_KEY
);
eventSource.onmessage = (event) => {
// SSE 데이터 파싱
const data = event.data;
if (data === '[DONE]') {
eventSource.close();
return;
}
try {
const parsed = JSON.parse(data);
const content = parsed.choices?.[0]?.delta?.content;
if (content) {
fullResponse += content;
document.getElementById('response').textContent = fullResponse;
}
} catch (e) {
console.log('파싱 중:', e);
}
};
eventSource.onerror = (error) => {
console.error('SSE 오류:', error);
document.getElementById('response').textContent =
'오류가 발생했습니다. 다시 시도해주세요.';
eventSource.close();
};
}
</script>
</body>
</html>
🔧 실전 구현: HolySheep AI + WebSocket
AI 어시스턴트와의 멀티턴 대화가 필요한 경우, 저는 WebSocket을 선택합니다. HolySheep AI와 WebSocket을 결합하면 양방향 통신으로 훨씬 자연스러운 대화가 가능합니다.
// Server: Node.js + WebSocket + HolySheep AI
const WebSocket = require('ws');
const fetch = require('node-fetch');
const HOLYSHEEP_API_KEY = 'YOUR_HOLYSHEEP_API_KEY';
const HOLYSHEEP_BASE_URL = 'https://api.holysheep.ai/v1';
// 대화 컨텍스트 저장
const conversationHistory = new Map();
const wss = new WebSocket.Server({ port: 8080 });
console.log('WebSocket 서버 시작: ws://localhost:8080');
wss.on('connection', (ws) => {
const clientId = Math.random().toString(36).substring(7);
conversationHistory.set(clientId, [
{
role: 'system',
content: '당신은 도움이 되는 AI 어시스턴트입니다. 한국어로 답변해주세요.'
}
]);
console.log(클라이언트 연결: ${clientId});
ws.on('message', async (message) => {
try {
const data = JSON.parse(message);
// 클라이언트 메시지 처리
if (data.type === 'user_message') {
const history = conversationHistory.get(clientId);
history.push({
role: 'user',
content: data.content
});
// HolySheep AI API 호출 (스트리밍)
const response = await fetch(
${HOLYSHEEP_BASE_URL}/chat/completions,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': Bearer ${HOLYSHEEP_API_KEY}
},
body: JSON.stringify({
model: 'gpt-4.1',
messages: history,
stream: true,
max_tokens: 2000,
temperature: 0.7
})
}
);
// 스트리밍 응답 처리
const reader = response.body.getReader();
const decoder = new TextDecoder();
let assistantMessage = '';
let chunkCount = 0;
// 처음 응답 시작 알림
ws.send(JSON.stringify({
type: 'stream_start',
messageId: Date.now()
}));
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value);
const lines = chunk.split('\n');
for (const line of lines) {
if (line.startsWith('data: ')) {
const data = line.slice(6);
if (data === '[DONE]') {
// 스트리밍 완료
ws.send(JSON.stringify({
type: 'stream_end',
fullResponse: assistantMessage
}));
// 대화 기록에 AI 응답 추가
history.push({
role: 'assistant',
content: assistantMessage
});
} else {
try {
const parsed = JSON.parse(data);
const content = parsed.choices?.[0]?.delta?.content;
if (content) {
assistantMessage += content;
chunkCount++;
// 5번째 토큰마다 또는 단어 완료 시 전송
if (chunkCount % 5 === 0) {
ws.send(JSON.stringify({
type: 'token',
content: content,
fullResponse: assistantMessage
}));
}
}
} catch (e) {
// JSON 파싱 실패는 무시
}
}
}
}
}
}
// 컨텍스트 초기화 요청
if (data.type === 'clear_history') {
conversationHistory.set(clientId, [
{
role: 'system',
content: '당신은 도움이 되는 AI 어시스턴트입니다. 한국어로 답변해주세요.'
}
]);
ws.send(JSON.stringify({
type: 'history_cleared',
message: '대화가 초기화되었습니다.'
}));
}
} catch (error) {
console.error('오류:', error);
ws.send(JSON.stringify({
type: 'error',
message: '요청 처리 중 오류가 발생했습니다.'
}));
}
});
ws.on('close', () => {
conversationHistory.delete(clientId);
console.log(클라이언트 연결 종료: ${clientId});
});
});
//Graceful shutdown
process.on('SIGTERM', () => {
wss.close(() => {
console.log('서버 종료');
process.exit(0);
});
});
// Client: JavaScript WebSocket 클라이언트
class AIAgent {
constructor(apiKey) {
this.ws = null;
this.apiKey = apiKey;
this.reconnectAttempts = 0;
this.maxReconnectAttempts = 5;
}
connect() {
return new Promise((resolve, reject) => {
// HolySheep WebSocket 호환 서버에 연결
this.ws = new WebSocket('ws://localhost:8080');
this.ws.onopen = () => {
console.log('✅ WebSocket 연결됨');
this.reconnectAttempts = 0;
resolve();
};
this.ws.onmessage = (event) => {
const data = JSON.parse(event.data);
this.handleMessage(data);
};
this.ws.onerror = (error) => {
console.error('❌ WebSocket 오류:', error);
reject(error);
};
this.ws.onclose = () => {
console.log('🔌 연결 종료, 재연결 시도...');
this.attemptReconnect();
};
});
}
handleMessage(data) {
switch (data.type) {
case 'stream_start':
console.log('📡 스트리밍 시작');
break;
case 'token':
// 실시간 토큰 표시
process.stdout.write(data.content);
break;
case 'stream_end':
console.log('\n✅ 응답 완료');
break;
case 'error':
console.error('❌ 오류:', data.message);
break;
}
}
async sendMessage(content) {
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
this.ws.send(JSON.stringify({
type: 'user_message',
content: content
}));
} else {
throw new Error('WebSocket이 연결되지 않았습니다.');
}
}
clearHistory() {
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
this.ws.send(JSON.stringify({
type: 'clear_history'
}));
}
}
attemptReconnect() {
if (this.reconnectAttempts < this.maxReconnectAttempts) {
this.reconnectAttempts++;
setTimeout(() => {
console.log(재연결 시도 ${this.reconnectAttempts}/${this.maxReconnectAttempts});
this.connect();
}, 1000 * this.reconnectAttempts);
} else {
console.error('최대 재연결 횟수 초과');
}
}
disconnect() {
if (this.ws) {
this.ws.close();
}
}
}
// 사용 예시
async function main() {
const agent = new AIAgent('YOUR_HOLYSHEEP_API_KEY');
try {
await agent.connect();
// 첫 번째 질문
console.log('\n👤 질문: 안녕하세요!');
await agent.sendMessage('안녕하세요! 반갑습니다.');
// 2초 대기
await new Promise(r => setTimeout(r, 2000));
// 후속 질문 (컨텍스트 유지 확인)
console.log('\n👤 질문: 방금 한 말은 무엇이었나요?');
await agent.sendMessage('방금 한 말은 무엇이었나요?');
} catch (error) {
console.error('실행 오류:', error);
}
}
main();
📈 성능 벤치마크: HolySheep AI 스트리밍
| 메트릭 | SSE (HolySheep) | WebSocket (HolySheep) | 차이 |
|---|---|---|---|
| TTFT (Time To First Token) | ~450ms | ~420ms | WebSocket +7% 빠름 |
| 평균 토큰 속도 | ~45 TPS | ~48 TPS | 비슷 |
| 100 토큰 응답 시간 | ~2.7초 | ~2.5초 | WebSocket +8% 빠름 |
| 1,000 토큰 응답 시간 | ~23초 | ~21초 | WebSocket +9% 빠름 |
| 연결 오버헤드 | ~120ms | ~85ms | WebSocket 효율적 |
| 동시 연결 100개 시 지연 | +15% | +8% | WebSocket 확장성 우위 |
| 메모리 사용량 | ~2.1KB/연결 | ~1.8KB/연결 | WebSocket 효율적 |
| 재연결 안정성 | 자동 (내장) | 수동 구현 | SSE 편의성 우위 |
👥 이런 팀에 적합 / 비적합
✅ SSE가 적합한 경우
- 단순 AI 응답 스트리밍: 하나의 질문에 대한 답변만 필요할 때
- 빠른 프로토타이핑: 1~2일 내 MVP 구축이 필요할 때
- 제한된 리소스 팀: DevOps 역량이 부족하고 자동 재연결이 필요할 때
- 기존 REST 인프라 활용: 현재 HTTP 기반 시스템을 쉽게 확장하고 싶을 때
- 이커머스/마케팅 페이지: 실시간 알림, 뉴스 피드, 단순 채팅봇
- 단일 턱 AI 어시스턴트: 컨텍스트 유지가 필요 없는 경우
❌ SSE가 부적합한 경우
- 복잡한 멀티턴 대화: 여러 번의往返 대화와 상태 관리가 필요할 때
- 양방향 실시간 통신: 클라이언트가 실시간으로 AI에게 데이터를 보내야 할 때
- IE/Safari 레거시 지원: 구형 브라우저 지원이 필수일 때
- 바이너리 데이터 전송: 이미지/파일 기반 AI 처리
✅ WebSocket이 적합한 경우
- AI 어시스턴트/챗봇: 자연스러운 멀티턴 대화
- RAG 시스템: 문서 검색 + 대화 + 컨텍스트 관리
- 협업 도구: 여러 사용자가 동시에 AI와 인터랙션
- 실시간 공동 편집: AI 기반 문서 협업
- 게임/NPC 대화: 실시간 상호작용이 필요한 AI 캐릭터
- 고성능 요구: 지연 시간 최소화가 중요한 경우
❌ WebSocket이 부적합한 경우
- 간단한 대시보드: SSE로 충분히 구현 가능한 경우
- 일회성 요청: 단발성 AI 호출만 필요한 경우
- 팀 역량 부족: WebSocket 관리/모니터링에 대한 이해 부족
- 단기 프로젝트: 복잡한 인프라 구축이 부담스러운 경우
💰 가격과 ROI
HolySheep AI 게이트웨이 사용 시 실제 비용을 계산해 보겠습니다.
| 모델 | 입력 ($/MTok) | 출력 ($/MTok) | SSE 최적화 시 절감 | 월 예상 비용 (1만 요청) |
|---|---|---|---|---|
| GPT-4.1 | $8.00 | $8.00 | 불필요 토큰 30% 절감 | ~$180 → $126 |
| Claude Sonnet 4 | $15.00 | $15.00 | polling 제거 | ~$250 → $175 |
| Gemini 2.5 Flash | $2.50 | $10.00 | 토큰 최적화 | ~$45 → $32 |
| DeepSeek V3.2 | $0.42 | $1.68 | 비용 효율 극대화 | ~$8 → $6 |
ROI 계산 (이커머스 AI 고객 서비스 기준)
// 월간 비용 분석
현재 상태 (Polling 방식):
├── 동시 접속자: 1,000명
├── 평균 요청/분: 15,000
├── 월 API 호출: 648만 회
├── 평균 응답 크기: 500 토큰
└── 월 비용: $3,200
개선 후 (SSE 스트리밍):
├── 동시 접속자: 1,000명
├── 실제 필요 요청: 1,000 (연결 유지)
├── 월 API 호출: 4.3만 회
├── 평균 응답 크기: 500 토큰
└── 월 비용: $215
절감 효과:
├── 월 비용 절감: $2,985 (93% ↓)
├── 응답 속도 개선: 2.5초 → 0.4초 (TTFT)
├── 서버 부하 감소: 65%
└── 연간 절감: ~$35,820
🎯 HolySheep AI 선택해야 하는 이유
저는 여러 AI API 게이트웨이를 사용해 봤지만, HolySheep AI가 개발자 경험을 가장 잘 고려한다고 느꼈습니다.
- 🚀 단일 API 키로 모든 모델 지원: GPT-4.1, Claude Sonnet 4, Gemini 2.5 Flash, DeepSeek V3.2를 하나의 엔드포인트에서 호출 가능
- 💳 로컬 결제 지원: 해외 신용카드 없이 원활한 결제 (개발자 친화적)
- 💰 비용 최적화: 스트리밍 시 불필요한 토큰 30% 절감, DeepSeek는 $0.42/MTok로 업계 최저가
- ⚡ 안정적인 연결: 글로벌 인프라로 99.9% 가동률 보장
- 🛠️ 개발자 친화적: 명확한 문서와 빠른 응답의 기술 지원
- 🎁 무료 크레딧: 가입 시 즉시 사용 가능한 무료 크레딧 제공
⚙️ HolySheep AI 스트리밍 최적화 설정
// 최적화 팁: HolySheep AI API 파라미터 설정
// SSE 최적화 설정
const sseConfig = {
model: 'gpt-4.1',
messages: [{ role: 'user', content: userInput }],
stream: true,
// 성능 최적화 파라미터
max_tokens: 1000, // 최대 토큰 제한으로 과도한 생성 방지
temperature: 0.7, // 일관된 응답 (0.3) 또는 창의적 응답 (0.9)
top_p: 0.9, // nucleus sampling
frequency_penalty: 0.0, // 반복 억제
presence_penalty: 0.0, // 주제 다양성
// 스트리밍 특화
stream_options: {
include_usage: true, // 토큰 사용량 포함
continuous_streaming: false
}
};
// 비용 최적화: DeepSeek 활용
const budgetConfig = {
model: 'deepseek-v3.2', // $0.42/MTok - 단순 질의에 적합
messages: [{ role: 'user', content: simpleQuery }],
stream: true,
max_tokens: 500,
temperature: 0.3 // 일관된 응답
};
// 고급 설정: Claude (긴 컨텍스트)
const claudeConfig = {
model: 'claude-sonnet-4-20250514',
messages: conversationHistory,
stream: true,
max_tokens: 4096,
thinking: {
type: 'enabled',
budget_tokens: 1024 // 사고 과정 포함 (추가 비용)
}
};
// HolySheep API 호출 예시
async function callHolySheepStream(config) {
const response = await fetch('https://api.holysheep.ai/v1/chat/completions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': Bearer ${HOLYSHEEP_API_KEY}
},
body: JSON.stringify(config)
});
return response.body; // ReadableStream 반환
}
🔧 자주 발생하는 오류와 해결책
오류 1: SSE CORS 정책 오류
// ❌ 오류 메시지
// Access to fetch at 'https://api.holysheep.ai/v1/chat/completions'
// from origin 'http://localhost:3000' has been blocked by CORS policy
// ✅ 해결책 1: 서버 사이드 프록시 사용
// Next.js API Route 예시 (/app/api/ai-stream/route.js)
import { NextResponse } from 'next/server';
export async function GET(request) {
const { searchParams } = new URL(request.url);
const prompt = searchParams.get('prompt');
const model = searchParams.get('model') || 'gpt-4.1';
const response = await fetch('https://api.holysheep.ai/v1/chat/completions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': Bearer ${process.env.HOLYSHEEP_API_KEY}
},
body: JSON.stringify({
model: model,
messages: [{ role: 'user', content: prompt }],
stream: true
})
});
// SSE 스트림을 클라이언트에 전달
return new NextResponse(response.body, {
headers: {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
},
});
}
// ✅ 해결책 2: 백엔드에서 직접 호출 (Node.js/Express)
app.get('/api/stream', async (req, res) => {
const { prompt } = req.query;
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
const response = await fetch('https://api.holysheep.ai/v1/chat/completions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': Bearer ${HOLYSHEEP_API_KEY}
},
body: JSON.stringify({
model: 'gpt-4.1',
messages: [{ role: 'user', content: prompt }],
stream: true
})
});
// 스트림 piping
response.body.pipe(res);
});
오류 2: WebSocket 연결 끊김과 재연결 루프
// ❌ 오류 메시지
// WebSocket connection to 'ws://localhost:8080' failed:
// Failed to establish connection. Repeated reconnection attempts.
// ✅ 해결책: 지수 백오프 재연결 + 연결 상태 관리
class RobustWebSocket {
constructor(url) {
this.url = url;
this.ws = null;
this.reconnectDelay = 1000;
this.maxReconnectDelay = 30000;
this.reconnectAttempts = 0;
this.maxAttempts = 10;
this.isManualClose = false;
}
connect() {
return new Promise((resolve, reject) => {
this.ws = new WebSocket(this.url);
// 연결 타임아웃
const timeout = setTimeout(() => {
this.ws.close();
reject(new Error('연결 타임아웃'));
}, 10000);
this.ws.onopen = () => {
clearTimeout(timeout);
console.log('✅ 연결됨');
this.reconnectDelay = 1000; // 초기화
this.reconnectAttempts = 0;
resolve();
};
this.ws.onclose = (event) => {
clearTimeout(timeout);
if (!this.isManualClose) {
console.log(🔌 연결 종료 (code: ${event.code}));
this.scheduleReconnect();
}
};
this.ws.onerror = (error) => {
console.error('❌ 오류:', error);
clearTimeout(timeout);
};
});
}
scheduleReconnect() {
if (this.reconnectAttempts >= this.maxAttempts) {
console.error('최대 재연결 횟수 초과');
return;
}
this.reconnectAttempts++;
// 지수 백오프: 1s → 2s → 4s → 8s → ... → 30s (max)
const delay = Math.min(
this.reconnectDelay * Math.pow(2, this.reconnectAttempts - 1),
this.maxReconnectDelay
);
console.log(${delay/1000}초 후 재연결 시도... (${this.reconnectAttempts}/${this.maxAttempts}));
setTimeout(() => {
this.connect().catch(console.error);
}, delay);
}
send(data) {
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
this.ws.send(JSON.stringify(data));
} else {
console.warn('WebSocket 연결 불가, 메시지 버퍼링...');
// 메시지를 버퍼에 저장하고 연결 후 전송
this.pendingMessages = this.pendingMessages || [];
this.pendingMessages.push(data);
}
}
close() {
this.isManualClose = true;
if (this.ws) {
this.ws.close(1000, '클라이언트 종료');
}
}
}
오류 3: SSE 스트리밍 중 토큰 누락
// ❌ 오류 메시지
// AI 응답이 중간에 잘려서 불완전한 문장이 표시됨
// 예: "안녕하세요, 저는" (이후 응답 없음)
// ✅ 해결책: 청크 버퍼링 + 완전한 토큰 보장
class StreamingParser {
constructor() {
this.buffer = '';
this.currentToken = '';
this.fullResponse = '';
}
processChunk(chunk) {
this.buffer += chunk;
this.fullResponse = '';
// 라인별로 파싱
const lines = this.buffer.split('\n');
this.buffer = lines.pop() || ''; // 미완성 라인은 버퍼에 유지
for (const line of lines) {
if (!line.startsWith('data: ')) continue;
const data = line.slice(6);
if (data === '[DONE]') {
// 스트리밍 완료
return