오류 처리 가이드¶
이 가이드는 Continuum Router의 오류 처리 메커니즘, 상태 코드, 재시도 전략 및 문제 해결을 다룹니다.
목차¶
오류 범주¶
클라이언트 오류 (4xx)¶
수정 없이는 재시도하면 안 되는 잘못된 클라이언트 요청으로 인한 오류입니다.
| 범주 | 설명 | 예제 |
|---|---|---|
| 유효성 검사 오류 | 잘못된 요청 형식 또는 매개변수 | 필수 필드 누락, 잘못된 JSON |
| 인증 오류 | 인증 또는 권한 부여 실패 | 잘못된 API 키, 만료된 토큰 |
| 리소스 오류 | 요청한 리소스를 찾을 수 없음 | 사용할 수 없는 모델, 엔드포인트를 찾을 수 없음 |
| 속도 제한 오류 | 너무 많은 요청 | 쿼터 초과, 속도 제한 도달 |
서버 오류 (5xx)¶
일시적이고 재시도 가능할 수 있는 서버 측 오류입니다.
| 범주 | 설명 | 예제 |
|---|---|---|
| 백엔드 오류 | 백엔드 서비스 문제 | 연결 거부, 백엔드 타임아웃 |
| 라우터 오류 | 내부 라우터 문제 | 설정 오류, 패닉 |
| 인프라 오류 | 인프라 장애 | 데이터베이스 다운, 네트워크 파티션 |
| 용량 오류 | 리소스 고갈 | 메모리 제한, 연결 풀 가득 참 |
HTTP 상태 코드¶
성공 코드 (2xx)¶
| 상태 | 이름 | 설명 | 사용 시기 |
|---|---|---|---|
| 200 | OK | 요청이 성공적으로 완료됨 | 일반 응답 |
| 201 | Created | 리소스가 성공적으로 생성됨 | 리소스 생성 |
| 202 | Accepted | 처리를 위해 요청이 수락됨 | 비동기 작업 |
| 204 | No Content | 응답 본문 없이 성공 | 삭제 |
클라이언트 오류 코드 (4xx)¶
| 상태 | 이름 | 설명 | 사용 시기 |
|---|---|---|---|
| 400 | Bad Request | 잘못된 요청 형식 | 잘못된 JSON, 누락된 필드 |
| 401 | Unauthorized | 인증 필요 | 누락되거나 잘못된 인증 |
| 403 | Forbidden | 접근 거부 | 불충분한 권한 |
| 404 | Not Found | 리소스를 찾을 수 없음 | 모델/엔드포인트 사용 불가 |
| 405 | Method Not Allowed | HTTP 메서드 지원되지 않음 | 잘못된 HTTP 동사 |
| 408 | Request Timeout | 클라이언트 요청 타임아웃 | 느린 클라이언트 |
| 413 | Payload Too Large | 요청 본문이 너무 큼 | 크기 제한 초과 |
| 422 | Unprocessable Entity | 유효성 검사 실패 | 비즈니스 로직 오류 |
| 429 | Too Many Requests | 속도 제한 초과 | 속도 제한 |
서버 오류 코드 (5xx)¶
| 상태 | 이름 | 설명 | 사용 시기 |
|---|---|---|---|
| 500 | Internal Server Error | 예상치 못한 서버 오류 | 처리되지 않은 예외 |
| 501 | Not Implemented | 기능이 구현되지 않음 | 지원되지 않는 작업 |
| 502 | Bad Gateway | 잘못된 백엔드 응답 | 백엔드 오류 |
| 503 | Service Unavailable | 서비스가 일시적으로 다운 | 모든 백엔드 비정상 |
| 504 | Gateway Timeout | 백엔드 타임아웃 | 백엔드가 너무 느림 |
| 507 | Insufficient Storage | 스토리지 가득 참 | 디스크/메모리 가득 참 |
오류 응답 형식¶
표준 오류 응답¶
{
"error": {
"code": 404,
"type": "model_not_found",
"message": "정상 백엔드에서 모델 'gpt-5'를 찾을 수 없습니다",
"details": {
"requested_model": "gpt-5",
"available_models": ["gpt-4", "gpt-3.5-turbo", "llama2"],
"backends_checked": 3,
"healthy_backends": 2
},
"request_id": "req_12345",
"timestamp": "2024-01-15T10:30:45Z"
}
}
유효성 검사 오류 응답¶
{
"error": {
"code": 400,
"type": "validation_error",
"message": "잘못된 요청 매개변수",
"details": {
"validation_errors": [
{
"field": "messages",
"error": "필수 필드 누락"
},
{
"field": "temperature",
"error": "0과 2 사이여야 합니다",
"value": 3.5
}
]
},
"request_id": "req_12346"
}
}
속도 제한 오류 응답¶
{
"error": {
"code": 429,
"type": "rate_limit_exceeded",
"message": "API 속도 제한 초과",
"details": {
"limit": 100,
"window": "1m",
"retry_after": 45,
"reset_at": "2024-01-15T10:31:30Z"
},
"headers": {
"X-RateLimit-Limit": "100",
"X-RateLimit-Remaining": "0",
"X-RateLimit-Reset": "1705316490",
"Retry-After": "45"
}
}
}
백엔드 오류 전달¶
백엔드가 4xx 오류를 반환하면 Continuum Router는 백엔드의 원본 오류 세부 정보를 파싱하고 전달합니다. 이를 통해 디버깅에 더 유용한 오류 정보를 제공합니다.
지원되는 백엔드 형식:
- OpenAI API (
{"error": {"message": "...", "type": "...", "param": "...", "code": "..."}}) - Anthropic Claude API (
{"error": {"message": "...", "type": "..."}}) - Google Gemini API (
{"error": {"message": "...", "status": "...", "code": ...}})
예제 응답 (백엔드 오류 전달 포함):
{
"error": {
"code": 400,
"type": "invalid_request_error",
"message": "잘못된 크기 '512x512'. gpt-image-1에 유효한 크기: 1024x1024, 1536x1024, 1024x1536, auto",
"param": "size"
}
}
동작:
- 백엔드가 파싱 가능한 오류 응답을 반환하면 원본
message,type,param,code필드가 보존됨 - 백엔드가 제공할 때
param필드가 포함됨 (어떤 매개변수가 오류를 일으켰는지 식별하는 데 유용) - 백엔드 응답을 파싱할 수 없으면 일반적인 오류 메시지 반환
- 모든 오류 응답은 OpenAI 호환 유지
폴백 응답 (백엔드 오류를 파싱할 수 없을 때):
재시도 전략¶
설정¶
retry:
# 기본 설정
max_attempts: 3
initial_delay: "100ms"
max_delay: "10s"
backoff_multiplier: 2.0
jitter: true
# 재시도 가능 조건
retryable_status_codes:
- 429 # Too Many Requests
- 502 # Bad Gateway
- 503 # Service Unavailable
- 504 # Gateway Timeout
retryable_errors:
- ConnectionError
- TimeoutError
- TemporaryError
# 엔드포인트별 설정
endpoints:
"/v1/chat/completions":
max_attempts: 5
timeout: "60s"
"/v1/completions":
max_attempts: 3
timeout: "30s"
지수 백오프¶
backoff:
type: exponential
base: 100ms
multiplier: 2
max: 10s
jitter: 0.1 # ±10% 무작위화
# 지연 계산:
# delay = min(base * multiplier^attempt + jitter, max)
#
# 시도 1: 100ms
# 시도 2: 200ms
# 시도 3: 400ms
# 시도 4: 800ms
# ...
스마트 재시도 로직¶
smart_retry:
# 다른 백엔드로 재시도
try_different_backend: true
# 재시도 시 요청 축소
reduce_on_retry:
max_tokens: 0.8 # 20% 감소
temperature: 0.9 # 온도 낮춤
# 특정 오류에 대한 재시도 건너뛰기
non_retryable:
- AuthenticationError
- ValidationError
- PaymentRequired
서킷 브레이커¶
아키텍처 세부 정보
상태 머신 다이어그램, 관리자 엔드포인트, Prometheus 메트릭을 포함한 자세한 구현 아키텍처는 서킷 브레이커 아키텍처를 참조하세요.
설정¶
circuit_breaker:
enabled: true
# 장애 감지
failure_threshold: 5 # 회로 개방까지의 실패 횟수
success_threshold: 2 # 회로 닫힘까지의 성공 횟수
# 타이밍
timeout: "30s" # 요청 타임아웃
half_open_timeout: "15s" # 반개방 상태 지속 시간
reset_timeout: "60s" # 재시도까지의 시간
# 모니터링 윈도우
window_size: "60s"
min_requests: 10 # 통계를 위한 최소 요청
회로 상태¶
stateDiagram-v2
[*] --> Closed
Closed --> Open: 장애 임계값 도달
Open --> HalfOpen: 리셋 타임아웃 만료
HalfOpen --> Closed: 성공 임계값 도달
HalfOpen --> Open: 장애 감지 백엔드별 서킷 브레이커¶
backends:
- name: primary
url: http://primary:8000
circuit_breaker:
failure_threshold: 3
reset_timeout: "30s"
- name: secondary
url: http://secondary:8000
circuit_breaker:
failure_threshold: 5
reset_timeout: "60s"
오류 복구¶
자동 복구¶
recovery:
# 헬스 체크 복구
health_checks:
enabled: true
interval: "30s"
recovery_threshold: 2 # 연속 성공 횟수
# 연결 풀 복구
connection_pool:
validation_interval: "60s"
evict_invalid: true
replace_invalid: true
# 캐시 복구
cache:
clear_on_error: false
partial_invalidation: true
모델 폴백¶
라우터는 기본 모델을 사용할 수 없을 때 자동 모델 폴백을 지원합니다. 이는 포괄적인 오류 복구를 위해 서킷 브레이커와 통합됩니다.
# 모델 폴백 설정
fallback:
enabled: true
fallback_chains:
"gpt-4o":
- "gpt-4-turbo"
- "gpt-3.5-turbo"
"claude-opus-4-5-20251101":
- "claude-sonnet-4-5"
- "claude-haiku-4-5"
# 크로스 프로바이더 폴백
"gemini-2.5-pro":
- "gemini-2.5-flash"
- "gpt-4o"
fallback_policy:
trigger_conditions:
error_codes: [429, 500, 502, 503, 504]
timeout: true
connection_error: true
circuit_breaker_open: true
max_fallback_attempts: 3
fallback_timeout_multiplier: 1.5
폴백 트리거 조건¶
| 조건 | HTTP 상태 | 설명 |
|---|---|---|
| Rate Limit | 429 | 백엔드 속도 제한 초과 |
| Server Error | 500 | 내부 백엔드 오류 |
| Bad Gateway | 502 | 백엔드에서 잘못된 응답 |
| Service Unavailable | 503 | 백엔드가 일시적으로 사용 불가 |
| Gateway Timeout | 504 | 백엔드 요청 타임아웃 |
| Circuit Open | N/A | 서킷 브레이커가 열림 |
폴백 응답 헤더¶
폴백이 발생하면 다음 헤더가 추가됩니다:
X-Fallback-Used: true
X-Original-Model: gpt-4o
X-Fallback-Model: gpt-4-turbo
X-Fallback-Reason: error_code_429
X-Fallback-Attempts: 2
폴백 오류 응답¶
모든 폴백이 소진되었을 때:
{
"error": {
"code": 503,
"type": "all_fallbacks_exhausted",
"message": "'gpt-4o'에 대한 모든 폴백 모델 실패",
"details": {
"original_model": "gpt-4o",
"attempted_fallbacks": ["gpt-4-turbo", "gpt-3.5-turbo"],
"failure_reasons": [
{"model": "gpt-4-turbo", "reason": "error_code_503"},
{"model": "gpt-3.5-turbo", "reason": "timeout"}
]
},
"request_id": "req_12345"
}
}
우아한 성능 저하¶
degradation:
# 폴백 모델 (레거시 - fallback.fallback_chains 사용 권장)
model_fallbacks:
"gpt-4": ["gpt-3.5-turbo", "gpt-3"]
"claude-opus": ["claude-sonnet", "claude-haiku"]
# 기능 저하
features:
streaming:
fallback_to_non_streaming: true
functions:
disable_on_error: true
# 응답 저하
response:
reduce_max_tokens: true
lower_temperature: true
simplify_prompts: true
장애 조치 전략¶
failover:
strategy: priority # 또는 round-robin, least-failures
backends:
- name: primary
priority: 1
weight: 100
- name: secondary
priority: 2
weight: 50
- name: tertiary
priority: 3
weight: 10
conditions:
- error_rate > 0.1
- latency_p99 > 5s
- health_score < 0.5
사용자 정의 오류 처리¶
오류 미들웨어¶
// 사용자 정의 오류 핸들러 구현
pub async fn error_handler(error: Error) -> Response {
let (status, error_type, message) = match error {
Error::Validation(e) => (
StatusCode::BAD_REQUEST,
"validation_error",
e.to_string()
),
Error::NotFound(e) => (
StatusCode::NOT_FOUND,
"not_found",
e.to_string()
),
Error::Backend(e) => (
StatusCode::BAD_GATEWAY,
"backend_error",
"Backend service error"
),
Error::Internal(e) => {
error!("Internal error: {:?}", e);
(
StatusCode::INTERNAL_SERVER_ERROR,
"internal_error",
"An internal error occurred"
)
}
};
Json(ErrorResponse {
error: ErrorDetails {
code: status.as_u16(),
error_type: error_type.to_string(),
message: message.to_string(),
request_id: request_id(),
timestamp: Utc::now(),
}
}).into_response()
}
오류 변환¶
error_transformation:
# 백엔드 오류를 클라이언트 친화적 메시지로 매핑
mappings:
- backend_error: "CUDA out of memory"
client_error: "모델을 일시적으로 사용할 수 없습니다. 다시 시도해 주세요"
status: 503
- backend_error: "Model not loaded"
client_error: "모델 초기화 진행 중"
status: 503
retry_after: 30
# 민감한 정보 숨기기
sanitization:
remove_stack_traces: true
remove_internal_ips: true
remove_credentials: true
오류 훅¶
error_hooks:
# 오류 전 훅
pre_error:
- log_error
- capture_metrics
- notify_monitoring
# 오류 후 훅
post_error:
- cleanup_resources
- update_circuit_breaker
- trigger_failover
# 사용자 정의 핸들러
handlers:
- type: webhook
url: https://alerts.example.com/errors
events: [critical_error, repeated_error]
- type: email
to: oncall@example.com
events: [service_down]
오류 디버깅¶
오류 로깅¶
logging:
errors:
level: debug # 모든 오류를 상세히 로깅
include_request_body: true
include_response_body: true
include_headers: true
# 구조화된 오류 로깅
format:
type: json
fields:
- timestamp
- level
- error_type
- error_message
- request_id
- backend_id
- model
- latency
- stack_trace
디버그 엔드포인트¶
# 최근 오류 가져오기
curl http://localhost:8080/admin/errors/recent
# 오류 통계 가져오기
curl http://localhost:8080/admin/errors/stats
# 특정 오류 세부 정보 가져오기
curl http://localhost:8080/admin/errors/req_12345
# 테스트용 오류 트리거
curl -X POST http://localhost:8080/admin/debug/error \
-d '{"type": "backend_timeout", "backend": "primary"}'
오류 트레이싱¶
tracing:
errors:
capture_stack_trace: true
capture_variables: true
capture_context: true
# 분산 트레이싱
propagation:
- tracecontext
- baggage
# 오류 샘플링
sampling:
all_errors: true
error_rate_threshold: 0.01
일반적인 오류 시나리오¶
시나리오 1: 모든 백엔드 다운¶
# 감지
condition: all_backends.health_status == unhealthy
# 응답
response:
status: 503
message: "서비스가 일시적으로 사용할 수 없습니다"
retry_after: 30
# 복구
recovery:
- increase_health_check_frequency
- notify_oncall
- attempt_backend_restart
- switch_to_backup_region
시나리오 2: 모델을 찾을 수 없음¶
# 감지
condition: requested_model not in available_models
# 응답
response:
status: 404
message: "모델 '{model}'을 찾을 수 없습니다"
suggestions: similar_models
# 완화
mitigation:
- check_model_aliases
- refresh_model_cache
- try_alternative_backends
시나리오 3: 속도 제한 초과¶
# 감지
condition: request_count > rate_limit
# 응답
response:
status: 429
retry_after: calculate_backoff()
headers:
X-RateLimit-Remaining: 0
# 처리
handling:
- queue_request_if_premium
- suggest_upgrade_plan
- apply_exponential_backoff
시나리오 4: 타임아웃¶
# 감지
condition: request_duration > timeout
# 응답
response:
status: 504
message: "요청 타임아웃"
# 완화
mitigation:
- try_with_reduced_max_tokens
- switch_to_faster_backend
- enable_streaming_if_possible
시나리오 5: 백엔드 불일치¶
# 감지
condition: backend_response_format != expected_format
# 응답
response:
status: 502
message: "잘못된 백엔드 응답"
# 복구
recovery:
- log_response_for_debugging
- mark_backend_as_degraded
- retry_with_different_backend
- update_backend_adapter
시나리오 6: 파일 해결 실패¶
# 감지
condition: file_reference_not_found OR invalid_file_id
# 응답
response:
status: 404 # 파일을 찾을 수 없음
# 또는
status: 400 # 잘못된 파일 ID 형식
message: "파일 참조 해결 실패"
type: "invalid_request_error"
code: "file_resolution_failed"
# 세부 정보
details:
- file_id: "file-abc123"
- reason: "file not found" OR "invalid file ID format"
# 완화
mitigation:
- verify_file_was_uploaded
- check_file_id_format_starts_with_file_prefix
- ensure_file_not_deleted
시나리오 7: 파일이 너무 큼¶
# 감지
condition: file_size > max_file_size
# 응답
response:
status: 413
message: "파일 크기가 최대 허용량을 초과합니다"
type: "invalid_request_error"
# 세부 정보
details:
file_size: 600000000 # 바이트
max_size: 536870912 # 바이트 (512MB 기본값)
# 완화
mitigation:
- compress_file_before_upload
- use_smaller_file
- increase_max_file_size_in_config
시나리오 8: 너무 많은 파일 참조¶
# 감지
condition: file_references_count > 20
# 응답
response:
status: 400
message: "요청에 파일 참조가 너무 많습니다"
type: "invalid_request_error"
# 세부 정보
details:
count: 25
max_allowed: 20
# 완화
mitigation:
- split_request_into_multiple_calls
- reduce_number_of_files
오류 모니터링¶
메트릭¶
# 오류율
rate(http_requests_total{status=~"5.."}[5m])
# 유형별 오류율
rate(errors_total[5m]) by (error_type)
# 백엔드 오류율
rate(backend_errors_total[5m]) by (backend_id, error_type)
# 서킷 브레이커 상태
circuit_breaker_state{backend_id="primary"}
알림¶
alerts:
- name: HighErrorRate
condition: error_rate > 0.05
duration: 5m
severity: warning
- name: AllBackendsDown
condition: healthy_backends == 0
duration: 1m
severity: critical
- name: CircuitBreakerOpen
condition: circuit_breaker_state == "open"
duration: 5m
severity: warning
모범 사례¶
오류 설계¶
- 적절한 HTTP 상태 코드 사용
- 명확하고 실행 가능한 오류 메시지 제공
- 추적을 위한 요청 ID 포함
- 클라이언트에 내부 세부 정보 노출 금지
- 충분한 컨텍스트와 함께 오류 로깅
오류 처리¶
- 백오프와 함께 재시도 로직 구현
- 연쇄 장애 방지를 위해 서킷 브레이커 사용
- 적절한 타임아웃 설정
- 적절한 계층에서 오류 처리
- 복구 불가능한 오류는 빠르게 실패
오류 복구¶
- 자동 복구를 위한 헬스 체크 구현
- 중요한 경로에 대한 폴백 전략 사용
- 오류율과 패턴 모니터링
- 이상에 대한 알림 설정
- 오류 시나리오 및 복구 절차 문서화
참고 문서¶
- 서킷 브레이커 아키텍처 - 자세한 서킷 브레이커 구현
- 모델 폴백 아키텍처 - 모델 폴백 시스템 설계
- 설정 가이드
- 모니터링 가이드
- API 레퍼런스
- 빠른 시작 가이드