OpenAI API 스트리밍 응답 처리 시 끊김 현상 해결
문제 상황
사내 챗봇 서비스에 GPT-4o API를 연동하면서 스트리밍 응답을 구현했다. 프론트엔드에서 타이핑 효과를 주기 위해 stream: true 옵션을 사용했는데, 간헐적으로 응답 중간이 끊기거나 일부 청크가 유실되는 현상이 발생했다.
원인 분석
처음에는 네트워크 타임아웃을 의심했지만, 로그를 확인한 결과 OpenAI API는 정상적으로 데이터를 보내고 있었다. 문제는 Next.js API 라우트에서 SSE(Server-Sent Events)를 파싱하는 과정에 있었다.
기존 코드는 response.body를 직접 읽으면서 줄바꿈 기준으로 split 했는데, 멀티바이트 문자가 청크 경계에 걸릴 경우 깨지는 문제가 있었다.
// 문제가 있던 코드
const reader = response.body.getReader();
while (true) {
const { done, value } = await reader.read();
if (done) break;
const text = new TextDecoder().decode(value);
// 청크 경계에서 멀티바이트 문자 깨짐
}
해결 방법
TextDecoderStream을 사용해 스트림 레벨에서 디코딩하도록 수정했다. 이렇게 하면 청크 경계를 자동으로 처리해준다.
const stream = response.body
.pipeThrough(new TextDecoderStream())
.pipeThrough(new TransformStream({
transform(chunk, controller) {
const lines = chunk.split('\n');
for (const line of lines) {
if (line.startsWith('data: ')) {
const data = line.slice(6);
if (data === '[DONE]') return;
controller.enqueue(JSON.parse(data));
}
}
}
}));
추가로 프론트엔드에서도 청크를 받을 때 이전 불완전한 라인을 버퍼에 저장했다가 다음 청크와 합치는 로직을 추가했다.
결과
배포 후 일주일간 모니터링했을 때 응답 끊김 현상이 완전히 사라졌다. 한글, 이모지 등 멀티바이트 문자가 포함된 긴 응답도 안정적으로 처리되었다. 스트리밍 응답을 다룰 때는 TextDecoderStream 같은 표준 API를 활용하는 것이 안전하다는 것을 배웠다.