요약 답변
세 문장 요약:
- 스트리밍과 비스트리밍 여부는 전적으로 사용자 코드에서 결정됩니다—요청 본문의
stream필드가 이를 결정합니다. 동일한 키, 동일한 모델, 동일한 엔드포인트를 사용하는 환경에서 모드가 계속 바뀐다면, 클라이언트 코드(또는 이를 감싸고 있는 SDK / 프레임워크)에서 전환이 발생하고 있는 것입니다. 게이트웨이가 이를 임의로 변경하는 일은 절대 없습니다. - 두 모드 모두 동일한 최종 콘텐츠를 반환하며 과금 또한 동일합니다. 유일한 차이점은 텍스트를 수신하는 시점과 이를 파싱하는 방식뿐입니다. 단 하나의 예외는 콘텐츠 안전 필터링입니다. 비스트리밍 요청은 자동으로 다른 경로로 전환(failover)되지만, 스트리밍 요청은 중간에 끊어집니다. 자세한 내용은 콘텐츠 필터링 시 스트리밍과 비스트리밍 요청은 어떻게 다른가요? 문서를 참고하십시오.
- 선택 기준: 사람이 화면을 직접 보고 있는 경우 → 스트리밍; 프로그램이 결과를 처리하는 경우(JSON 파싱, 배치 작업, 도구 호출) → 비스트리밍.
차이점 한눈에 보기
왜 제 요청은 스트리밍과 비스트리밍 사이를 오갑니까?
이것은 가장 흔한 질문이며, 답은 다음과 같습니다: 여러분 쪽의 어떤 것이 이를 바꾸고 있습니다. 아래 목록을 순서대로 점검하십시오 — 거의 항상 그중 하나가 맞습니다:1. stream은 코드의 변수 또는 설정값입니다
1. stream은 코드의 변수 또는 설정값입니다
전형적인 사례는 다음과 같습니다:
stream=config.get("stream", False) 또는 stream=is_web_request. 서로 다른 진입점이 같은 함수에 서로 다른 값으로 도달하며, 로그상으로는 모드가 무작위로 바뀌는 것처럼 보입니다.확인 방법: 실제로 보내는 요청 본문을 출력하고 stream 필드를 확인하십시오.2. 서로 다른 SDK와 프레임워크는 기본값이 다릅니다
2. 서로 다른 SDK와 프레임워크는 기본값이 다릅니다
같은 비즈니스 로직도 클라이언트에 따라 다르게 동작합니다:
- OpenAI SDK
chat.completions.create(): 기본값은 비스트리밍입니다 client.chat.completions.stream()또는with_streaming_response: 스트리밍- LangChain / LlamaIndex 같은 래퍼:
invoke또는stream중 무엇을 호출하는지, 그리고 모델 객체를 생성할 때streaming=True을 전달했는지에 따라 다릅니다 - 데스크톱 클라이언트, 에이전트 도구, 워크플로 플랫폼: 보통 설정에 “스트리밍 출력” 토글을 제공하며, 기본값은 제각각입니다
3. 여러 애플리케이션이 하나의 키를 공유하는 경우
3. 여러 애플리케이션이 하나의 키를 공유하는 경우
웹 채팅 UI(스트리밍)와 야간 배치 작업(비스트리밍)이 같은 키를 함께 사용하면, 로그를 같이 볼 때 무작위처럼 보입니다.확인 방법: 사용 사례별로 별도의 token을 만드십시오 — 그러면 로그가 자연스럽게 분리됩니다. Token 관리를 참조하십시오.
4. 중간 장비가 스트림을 평탄화했습니다
4. 중간 장비가 스트림을 평탄화했습니다
실제로는
stream: true를 보냈지만, Nginx나 기업 게이트웨이, 또는 어떤 프록시가 응답을 버퍼링했습니다 — 서버는 이를 청크 단위로 보냈고, 프록시는 이를 보관했다가 한 번에 내보냈기 때문에 비스트리밍처럼 느껴집니다.확인 방법: 프록시를 우회하여 한 번 테스트하십시오. Nginx에서 버퍼링을 끄십시오 (proxy_buffering off;). 이 경우 콘솔 로그에는 여전히 is_stream = true가 표시되는데, 이는 게이트웨이가 실제로 스트리밍으로 내보냈기 때문입니다.시나리오별 선택
스트리밍 사용
- 채팅 UI 및 지원 봇 — 사용자는 즉각적인 피드백이 필요합니다
- IDE 플러그인 / 코딩 어시스턴트(Claude Code, Cursor 등)
- 장문 생성(긴 문서, 긴 번역, 대규모 코드 블록)
- 장시간 추론 모델 작업 — 최소한 진행 상황을 확인할 수 있습니다
- 사용자가 생성 중간에 “중지”를 누를 수 있는 모든 경우
비스트리밍 사용
- 구조화된 출력:
json.loads()에 필요한 전체 JSON - 함수 호출 / 도구 호출 인수 파싱
- 배치 처리, 오프라인 작업, 예약된 작업
- 최종 결과만 중요하고 기다리는 사람이 없는 백엔드 흐름
- 빠른 검증, 디버깅, 테스트 케이스 작성
통합 작업량: 같은 작업, 두 방식 모두
- Python 비스트리밍 방식
- Python 스트리밍 방식
- Node.js 스트리밍 방식
- cURL 나란히 비교
Claude의 기본 형식(
/v1/messages)은 다른 스트리밍 프로토콜을 사용합니다: Anthropic의 named-event SSE (message_start / content_block_delta / message_delta 및 관련 이벤트), OpenAI의 균일한 data: 청크가 아니라, usage는 message_start 및 message_delta 이벤트에 걸쳐 분할됩니다. 전체 파싱 가이드: Claude 기본 형식: 스트리밍 및 비스트리밍 응답.과금과 사용량: 어느 쪽이든 동일합니다
usage와 관련된 두 가지 함정:
- 스트리밍은 기본적으로 usage를 반환하지 않습니다. OpenAI 호환 엔드포인트에서는
stream_options: {"include_usage": true}를 전달해야 합니다. 그러면 usage는 최종 청크에 도착하며(그 청크의choices배열은 비어 있으므로 인덱싱하기 전에 확인하십시오). 이는 APIYI의 여러 모델에서 정상 동작함이 확인되었습니다. - API가 되돌려주는
usage를 기준으로 과금을 정산하지 마십시오. 특히 캐시 관련 필드는 더욱 그렇습니다. 되돌려진 값은 실제 과금과 항상 일치하지 않으며, 캐시 적중 여부는 콘솔 로그의 **“캐시 과금 상세 정보”**로 판단됩니다. 캐시 과금 설명을 참조하십시오.
6가지 흔한 오해
1. 스트리밍을 사용하면 타임아웃을 무시할 수 있습니다
1. 스트리밍을 사용하면 타임아웃을 무시할 수 있습니다
두 가지를 구분해야 합니다. 스트리밍은 전체 생성 시간을 단축하지 않습니다. 첫 번째 token부터 마지막 token까지 걸리는 총 시간은 이전과 동일하며, 이 부분은 변하지 않습니다.하지만 스트리밍은 “아무것도 반환되지 않는” 문제를 해결합니다. 클라이언트 읽기 타임아웃을 이벤트 간 간격을 감당할 수 있도록 설정하기만 하면(추론 모델의 사고 단계에서 측정된 최대 무응답 간격은 약 42초이며, 중간에 keepalive ping이 있으므로 90~120초면 충분합니다), 타임아웃이 잘못 발생하는 일이 없습니다. 실제로 버틸 수 없는 것은 10분이 넘는 생성 전체 과정을 단 하나의 타임아웃 값으로 감당하게 만드는 순수 비스트리밍 방식입니다.따라서 올바른 접근 방식은 긴 출력은 스트리밍으로 수신하고, 읽기 타임아웃은 이벤트 간 간격에 맞게 설정하는 것입니다. 시나리오별 권장 값은 API 타임아웃 방지 방법에 있으며, 전체 가이드는 장문 출력 실무 가이드에서 확인할 수 있습니다.
2. 스트리밍이 더 빠릅니다
2. 스트리밍이 더 빠릅니다
첫 바이트는 더 빠르지만, 전체 시간은 그렇지 않습니다. 동일한 모델과 prompt의 경우 스트리밍과 비스트리밍은 거의 같은 시간에 완료됩니다.스트리밍이 제공하는 것은 체감 속도입니다. 사용자가 30초 동안 로딩 스피너를 쳐다보는 대신 1초 이내에 화면 움직임을 볼 수 있습니다. 화면을 보고 있는 사람이 아무도 없다면 그 가치는 0입니다.
3. 스트리밍이 더 저렴하거나 수신한 만큼만 과금됩니다
3. 스트리밍이 더 저렴하거나 수신한 만큼만 과금됩니다
그렇지 않습니다. 위의 “과금 및 사용량”을 참조하십시오. 과금 방식은 동일하며, 도중에 연결이 끊어져도 과금됩니다.
4. 모든 모델과 엔드포인트가 스트리밍을 지원합니다
4. 모든 모델과 엔드포인트가 스트리밍을 지원합니다
그렇지 않습니다. 텍스트 대화 모델은 대체로 지원하지만, 이미지 생성, 임베딩, rerank 엔드포인트에는 스트리밍 개념이 없으므로
stream을 무시하거나 거부합니다.일부 모델은 스트리밍 환경에서 특정 매개변수 조합에 대한 추가 제한이 있습니다. 확실하지 않은 경우 먼저 비스트리밍으로 호출이 작동하는지 확인한 다음 stream: true을 추가하십시오.5. 비스트리밍이 더 안정적입니다
5. 비스트리밍이 더 안정적입니다
두 방식 모두 고유한 실패 유형이 있습니다.
- 비스트리밍 위험: 생성 과정 내내 연결에 데이터가 흐르지 않으므로 프록시, CDN, 기업 게이트웨이가 유휴 타임아웃(idle timeout)으로 연결을 끊을 수 있습니다. 응답 본문이 매우 큰 경우(base64 이미지 출력은 수십 MB에 쉽게 도달함) 종결자 지연(stalled terminator) 현상이 발생할 수도 있습니다. 자세한 내용은 전송은 완료되었지만 반환되지 않는 요청 및 로그에는 완료로 표시되지만 클라이언트는 아무것도 받지 못하는 경우를 참조하십시오.
- 스트리밍 위험: SSE를 지원하지 않거나 버퍼링을 강제하는 미들박스(middlebox) 환경에 취약하며, 클라이언트 파싱이 더 복잡하여 사소한 오류가 발생하기 쉽습니다.
api-cf.apiyi.com(CDN 엔드포인트)에는 약 100초의 요청 상한선이 있어 두 모드 모두에 영향을 미친다는 것입니다. 소요 시간이 긴 요청에는 api.apiyi.com 또는 b.apiyi.com를 사용하십시오. 자세한 내용은 Base URL 구성 가이드를 참조하십시오.6. 스트림에서는 완전한 답변을 얻을 수 없습니다
6. 스트림에서는 완전한 답변을 얻을 수 없습니다
얻을 수 있습니다. 직접 조합하기만 하면 됩니다. 각 청크의
delta.content을 순서대로 연결하면 비스트리밍의 message.content과 완전히 동일한 결과를 얻을 수 있습니다.조합된 텍스트가 불완전해 보인다면 세 가지를 확인하십시오. finish_reason을 무시했는지, data: [DONE]을 수신하기 전에 루프를 종료했는지, 미들박스가 응답을 잘라냈는지 여부입니다.스트리밍이 작동하지 않습니까? 네 단계
1
요청 본문에 정말 stream: true가 포함되어 있는지 확인하십시오
실제로 전송하는 JSON을 출력하십시오. 래퍼 라이브러리에서는 “전달했다고 생각했습니다”와 “실제로 전달되었습니다”가 종종 다른 일입니다.
2
curl -N으로 직접 테스트하십시오
위의 “cURL 나란히 보기” 탭에 있는 명령을 사용하여 직접 작성한 코드와 모든 프록시를 우회하십시오. curl에서 청크가 점진적으로 도착하는 것이 보이면 서버 측은 정상이며 문제는 클라이언트 또는 중간 장치에 있습니다.
3
중간 장치 버퍼링을 확인하십시오
Nginx에
proxy_buffering off;를 추가하십시오. 기업용 게이트웨이와 보안 장비는 text/event-stream를 전체 페이로드로 검사할 수 있습니다 — 네트워크 관리자에게 이를 통과하도록 허용해 달라고 요청하십시오.4
파싱 로직을 검토하십시오
SSE를 한 줄씩 읽고, 빈 줄과
:로 시작하는 주석 줄은 건너뛰며, data: [DONE]에서 중단하십시오. usage를 담고 있는 마지막 청크에는 비어 있는 choices 배열이 있으므로, 그 안에 인덱싱하지 마십시오.관련 문서
API 타임아웃을 방지하는 방법
시나리오별 타임아웃 값과 긴 출력을 스트리밍해야 하는 이유
Base URL 구성 가이드
엔드포인트 간 차이와 CDN 노드의 100초 제한
로그에는 완료로 표시되지만 응답이 없는 경우
세그먼트 타이밍을 포함한 대규모 비스트리밍 응답의 전형적인 문제
Claude 스트리밍 및 비스트리밍
Anthropic의 네이티브 명명 이벤트 SSE 프로토콜 파싱
텍스트 생성 API
전체 매개변수 목록 및 호출 예시
로그 과금 세부 정보 이해하기
is_stream을 포함하여 각 콘솔 로그 필드의 의미