안녕하세요, 개발자 여러분. 저는 HolySheep AI에서 실제 서비스를 운영하는 엔지니어입니다. 이번 글에서는 AI 스트리밍 API에서 가장 중요한 WebSocket 긴 연결 관리를 초보자도 이해할 수 있도록 쉽게 설명드리겠습니다.
WebSocket이 뭔가요? 왜 필요한가요?
여러분이 AI 채팅 앱을 만든다고 상상해보세요. AI가 한 글자씩 타이핑하듯 답변을 보여주려면 어떻게 해야 할까요?
기존 방식 (REST API): 질문 → 서버가 전체 답변 완성 → 한 번에 전송 → 사용자는 답변이 끝날 때까지 기다려야 합니다.
WebSocket 방식: 질문 → AI가 답변 작성 중 → 한 글자씩 실시간 전송 → 사용자가 바로바로 답변을 확인할 수 있습니다.
AI 스트리밍 API의 핵심이 바로 이 실시간 데이터 흐름입니다. HolySheep AI는 이러한 스트리밍 연결을 안정적으로 관리해주는 게이트웨이 서비스를 제공합니다.
긴 연결(Long Connection)이란?
브라우저와 서버가 지속적으로 연결된 상태를 말합니다. 일반 HTTP는 요청-응답 후 연결이 끊기지만, WebSocket은:
- 연결을 한번 맺으면 계속 유지
- 서버가 먼저 데이터를 보낼 수 있음
- 여러 번의 요청-응답 없이 효율적 통신
Node.js로 HolySheep AI 스트리밍 API 연결하기
먼저 프로젝트 폴더를 만들고 필요한 도구를 설치합니다.
mkdir ai-streaming-demo
cd ai-streaming-demo
npm init -y
npm install ws dotenv
프로젝트 구조는 다음과 같이 됩니다:
ai-streaming-demo/
├── .env # API 키 저장
├── server.js # WebSocket 서버
└── package.json # 프로젝트 설정
.env 파일에 HolySheep AI API 키를 저장합니다.
# .env 파일
HOLYSHEEP_API_KEY=YOUR_HOLYSHEEP_API_KEY
완전한 스트리밍 API 서버 코드
// server.js
const WebSocket = require('ws');
require('dotenv').config();
// HolySheep AI 스트리밍 엔드포인트
const HOLYSHEEP_WS_URL = 'wss://api.holysheep.ai/v1/chat/completions';
const apiKey = process.env.HOLYSHEEP_API_KEY;
// 클라이언트 연결 관리 맵
const clientConnections = new Map();
let connectionId = 0;
// WebSocket 서버 생성 (포트 8080)
const wss = new WebSocket.Server({ port: 8080 });
console.log('🚀 AI 스트리밍 서버가 포트 8080에서 실행 중입니다.');
console.log('📡 HolySheep AI 게이트웨이 연결 대기 중...');
wss.on('connection', (clientWs, req) => {
const clientId = ++connectionId;
clientConnections.set(clientId, {
ws: clientWs,
connectedAt: Date.now(),
messageCount: 0
});
console.log(✅ 클라이언트 ${clientId} 연결됨 (총 ${clientConnections.size}개 연결));
// 클라이언트에게서 메시지 수신
clientWs.on('message', async (message) => {
try {
const data = JSON.parse(message);
console.log(📨 클라이언트 ${clientId}: ${JSON.stringify(data).substring(0, 100)}...);
// HolySheep AI로 스트리밍 요청
const response = await fetch(HOLYSHEEP_WS_URL, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': Bearer ${apiKey}
},
body: JSON.stringify({
model: 'gpt-4.1',
messages: data.messages || [
{ role: 'user', content: data.prompt || '안녕하세요' }
],
stream: true
})
});
if (!response.ok) {
throw new Error(HolySheep API 오류: ${response.status});
}
// 스트리밍 응답을 클라이언트에게 전달
const reader = response.body.getReader();
const decoder = new TextDecoder();
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 jsonStr = line.slice(6);
if (jsonStr === '[DONE]') {
clientWs.send(JSON.stringify({ type: 'done' }));
} else {
try {
const parsed = JSON.parse(jsonStr);
clientWs.send(JSON.stringify(parsed));
clientConnections.get(clientId).messageCount++;
} catch (e) {
// 파싱 오류 무시 (하트비트 등)
}
}
}
}
}
console.log(📤 클라이언트 ${clientId}: 스트리밍 완료 (총 ${clientConnections.get(clientId)?.messageCount}개 청크));
} catch (error) {
console.error(❌ 클라이언트 ${clientId} 오류:, error.message);
clientWs.send(JSON.stringify({ error: error.message }));
}
});
// 클라이언트 연결 해제
clientWs.on('close', () => {
clientConnections.delete(clientId);
console.log(🔌 클라이언트 ${clientId} 연결 해제됨 (남은 연결: ${clientConnections.size}개));
});
// 에러 처리
clientWs.on('error', (error) => {
console.error(⚠️ 클라이언트 ${clientId} 소켓 에러:, error.message);
clientConnections.delete(clientId);
});
});
// 주기적 하트비트 및 연결 상태 모니터링
setInterval(() => {
const now = Date.now();
let activeConnections = 0;
let staleConnections = 0;
clientConnections.forEach((conn, id) => {
const age = now - conn.connectedAt;
if (age > 300000) { // 5분 이상 방치된 연결
console.log(⏰ 클라이언트 ${id} 연결 타임아웃 처리 (${Math.floor(age/1000)}초));
conn.ws.terminate();
clientConnections.delete(id);
staleConnections++;
} else {
activeConnections++;
// Ping 전송으로 연결 유지
if (conn.ws.readyState === WebSocket.OPEN) {
conn.ws.ping();
}
}
});
if (activeConnections > 0 || staleConnections > 0) {
console.log(📊 연결 상태: 활성 ${activeConnections}개, 정리됨 ${staleConnections}개);
}
}, 60000);
// 서버 종료 처리
process.on('