🔥 고급2026-06-016~8분
스트리밍 UI 에러 복구 설계: 부분 응답 저장과 재시도 이음새 전략
SSE 스트리밍 도중 네트워크 단절이 발생하면 사용자는 빈 화면을 본다. 부분 응답을 보존하고 중단 지점부터 이어붙이는 프로덕션 패턴을 다룬다.
streamingerror-handlingresilience
스트리밍 실패의 세 가지 유형
유형 A: 네트워크 단절 — 클라이언트 연결이 끊기지만 서버 스트림은 계속 흐름. 응답 소실.
유형 B: 서버 사이드 오류 — overloaded_error (529) 또는 api_error (500)로 스트림 자체가 중단. Anthropic 공식 문서상 529는 지수 백오프 재시도 권장.
유형 C: 부분 tool_use 블록 중단 — input_json_delta가 절반만 수신되어 JSON 파싱 불가.
실패 모드별 트레이드오프:
- 무조건 처음부터 재시도 → 비용 2× + 응답 일관성 깨짐
- 부분 텍스트를 그냥 이어붙이기 → 문장 중간 접합으로 품질 저하
- 재시도 없이 에러 표시 → UX 최악
최적 전략: 부분 텍스트를 서버에 저장 → 재시도 시 "이어서 작성" 프롬프트로 연속성 확보
부분 응답 저장과 이음새 프롬프트 구현
import anthropic
import time
from dataclasses import dataclass, field
@dataclass
class StreamState:
partial_text: str = ""
input_tokens: int = 0
attempt: int = 0
def stream_with_recovery(
messages: list,
system: str,
max_retries: int = 3
) -> str:
client = anthropic.Anthropic()
state = StreamState()
for attempt in range(max_retries):
state.attempt = attempt
current_messages = messages.copy()
# 이음새 전략: 부분 응답이 있으면 assistant 턴으로 주입
if state.partial_text:
current_messages.append({"role": "assistant", "content": state.partial_text})
current_messages.append({
"role": "user",
"content": "(이전 응답이 중단됨. 마지막 문장에서 자연스럽게 이어서 완성해줘.)"
})
try:
with client.messages.stream(
model="claude-opus-4-5",
max_tokens=2048,
system=system,
messages=current_messages,
) as stream:
for text in stream.text_stream:
state.partial_text += text
yield text # UI로 실시간 전송
# 정상 완료
final = stream.get_final_message()
state.input_tokens = final.usage.input_tokens
return
except (anthropic.APIStatusError, anthropic.APIConnectionError) as e:
status = getattr(e, 'status_code', 0)
if status == 529 or status >= 500:
wait = 2 ** attempt # 1s, 2s, 4s
print(f"재시도 {attempt+1}/{max_retries} — {wait}s 대기 (부분 저장: {len(state.partial_text)}자)")
time.sleep(wait)
continue
raise # 4xx는 재시도 무의미
raise RuntimeError(f"최대 재시도 초과. 부분 응답 {len(state.partial_text)}자 보존됨")
이음새 프롬프트의 핵심은 partial_text를 assistant 메시지로 넣어 모델이 자신이 작성 중이었다고 인식하게 하는 것이다. 실험 결과 단순 "이어서 써줘" 대비 접합 품질이 유의미하게 향상된다.
운영 체크리스트와 수치 기준
지수 백오프 기준: 529 오류 시 1→2→4초, 최대 3회. 4회 이상은 비용 대비 효과 없음.
부분 응답 보존 임계값: 50자 미만이면 처음부터 재시도가 낫다 (이음새 오버헤드 > 절약 토큰).
타임아웃 설정: httpx_client 커스텀으로 read timeout 120초 권장 (기본값 600초는 과도).
운영 체크리스트
- [ ]
stream.text_stream루프에서 청크별 누적 저장 로직 존재 여부 - [ ] 529 / 500 구분 처리: 4xx는 재시도 금지
- [ ] 이음새 프롬프트 삽입 후 토큰 증가분 비용 모니터링
- [ ] 부분 응답 50자 미만 시 처음부터 재시도 분기 존재 여부
- [ ] 최종 실패 시
partial_text사용자에게 노출 또는 로그 저장 여부