OpenAI API 스트리밍 응답 처리 중 connection timeout 해결

문제 상황

GPT-4o API를 활용한 챗봇 서비스에서 스트리밍 응답을 구현했다. 짧은 답변은 문제없이 동작했지만, 1000 토큰 이상의 긴 응답에서 중간에 연결이 끊기는 현상이 발생했다.

const response = await openai.chat.completions.create({
  model: 'gpt-4o',
  messages: messages,
  stream: true,
});

에러 로그를 확인하니 ECONNRESET 에러가 발생했다. Vercel의 기본 function timeout이 10초로 설정되어 있었고, 긴 응답의 경우 이를 초과하는 것이 원인이었다.

해결 방법

1. Vercel function timeout 설정

vercel.json에서 타임아웃을 60초로 연장했다.

{
  "functions": {
    "app/api/chat/route.ts": {
      "maxDuration": 60
    }
  }
}

2. 스트림 처리 개선

ReadableStream으로 변환하면서 주기적으로 데이터를 전송해 connection을 유지하도록 수정했다.

const stream = new ReadableStream({
  async start(controller) {
    for await (const chunk of response) {
      const content = chunk.choices[0]?.delta?.content || '';
      if (content) {
        controller.enqueue(
          new TextEncoder().encode(`data: ${JSON.stringify({ content })}\n\n`)
        );
      }
    }
    controller.close();
  },
});

return new Response(stream, {
  headers: {
    'Content-Type': 'text/event-stream',
    'Cache-Control': 'no-cache',
    'Connection': 'keep-alive',
  },
});

3. 클라이언트 재연결 로직

만약의 경우를 대비해 EventSource에 재연결 로직을 추가했다.

const eventSource = new EventSource('/api/chat');
eventSource.onerror = () => {
  eventSource.close();
  // 재시도 로직
};

결과

2000 토큰 이상의 긴 응답도 안정적으로 스트리밍되는 것을 확인했다. GPT-4o의 빠른 응답 속도 덕분에 실제로는 30초 이내에 대부분 완료되지만, 여유있게 설정해두는 것이 안전하다.