본문 바로가기
Q&A해결2026년 7월 14일2분 읽기

OpenAI 스트리밍(SSE) 응답이 로컬에선 실시간인데 배포하면 마지막에 한꺼번에 쏟아집니다

l
llm_jaewon
조회 1152댓글 2

Next.js API route에서 OpenAI Chat Completions를 stream: true로 받아서 클라이언트로 SSE로 흘려주는 챗봇을 만들고 있어요. 로컬(next dev)에서는 타자 치듯이 토큰 단위로 예쁘게 나옵니다. 그런데 배포(nginx 리버스 프록시 뒤)로 올리면 몇 초 동안 아무것도 안 나오다가 답변 전체가 한 방에 툭 떨어져요. 스트리밍 의미가 없어졌습니다 😭

서버 코드는 대충 이렇게 청크를 그대로 write 하고 있고요, 로컬에서 curl로 때려봐도 실시간으로 잘 흘러옵니다.

for await (const chunk of stream) {
  const delta = chunk.choices[0]?.delta?.content || "";
  res.write(`data: ${JSON.stringify({ delta })}\n\n`);
}

Content-Type은 text/event-stream으로 줬고, 클라이언트는 EventSource 대신 fetch + ReadableStream reader로 읽고 있습니다. 앱 코드 문제였으면 로컬에서도 안 됐을 텐데 로컬은 멀쩡해서 네트워크단이 의심되는데 어디를 봐야 할지 감이 안 잡히네요. 혹시 짚이는 데 있으실까요?

댓글 2

김김영삼3개월 전
앱 코드는 정상이고 nginx가 응답을 버퍼링해서 다 모았다가 한 번에 내려주는 거예요. 로컬은 프록시가 없으니 멀쩡했던 거고요. 딱 이 증상입니다.

프록시 버퍼링을 이 라우트(또는 location)에 대해 꺼주세요. nginx conf에서 proxy_buffering off; 를 넣고, 더 확실하게 하려면 응답 헤더에 X-Accel-Buffering: no 를 직접 실어 보내면 nginx가 이 응답만 버퍼링 안 합니다. 코드에서 res.setHeader('X-Accel-Buffering', 'no') 한 줄이면 conf 안 건드리고도 돼요.

그리고 하나 더. gzip이 켜져 있으면 압축 버퍼 때문에 또 뭉쳐서 나갑니다. text/event-stream은 gzip 대상에서 빼주세요. Content-Encoding 협상이 끼면 스트리밍이 또 막혀요.

마지막으로 Node 쪽에서도 res.flushHeaders()를 스트림 시작 전에 한 번 불러서 헤더를 먼저 내보내고, 프레임워크에 따라 자체 버퍼가 있으면 그것도 꺼야 합니다. 순서대로 하면 nginx 버퍼링이 원인일 확률이 제일 높아요.
lllm_jaewon(guest)3개월 전
와 X-Accel-Buffering: no 넣으니까 바로 실시간으로 흘러나옵니다. gzip에서 event-stream 빼는 것도 같이 했어요. 로컬만 되고 서버만 안 되길래 진짜 뭐가 문제인지 몰랐는데 정확히 그거였네요. 감사합니다 🙏
Ctrl+Enter로 등록