에러 응답 형식
모든 에러는 다음 구조를 따릅니다:
{
"success": false,
"error": {
"code": "error_code",
"message": "사람이 읽을 수 있는 설명",
"hint": "해결 방법 (선택)",
"field": "specific.field.path (선택)"
},
"meta": {
"requestId": "req_abc123"
}
}
지원팀에 문의할 때 항상 requestId를 포함하세요 - 문제를 빠르게 디버깅하는 데 도움이 됩니다.
HTTP 상태 코드
| 상태 | 의미 | 일반적인 원인 |
|---|
| 200 | 성공 | 요청이 성공적으로 완료됨 |
| 400 | 잘못된 요청 | 잘못된 JSON, 누락된 필드 |
| 401 | 인증 실패 | 잘못되거나 누락된 API 키 |
| 403 | 접근 금지 | 권한 부족 |
| 404 | 찾을 수 없음 | 존재하지 않는 엔드포인트 |
| 429 | 요청 과다 | 요청 제한 초과 |
| 500 | 서버 에러 | 내부 에러 (지원팀 문의) |
| 503 | 서비스 불가 | AI 제공자 장애 (나중에 재시도) |
에러 코드
인증 에러 (401)
| 코드 | 설명 | 해결 방법 |
|---|
missing_api_key | 요청에 API 키 없음 | X-API-Key 헤더 추가 |
invalid_api_key | 키가 존재하지 않음 | 키 형식 확인 (bs_live_xxx) |
expired_api_key | 키가 폐기됨 | 새 키 생성 |
suspended_api_key | 남용으로 키 정지됨 | 지원팀 문의 |
{
"error": {
"code": "invalid_api_key",
"message": "제공된 API 키가 유효하지 않습니다",
"hint": "API 키는 'bs_live_' 또는 'bs_test_'로 시작합니다"
}
}
유효성 검사 에러 (400)
| 코드 | 설명 | 해결 방법 |
|---|
invalid_json | 잘못된 JSON 형식 | JSON 문법 확인 |
missing_field | 필수 필드 누락 | 지정된 필드 추가 |
invalid_field | 필드 타입/형식 오류 | 필드 요구사항 확인 |
invalid_saju | 사주 구조 오류 | 사주 객체 형식 참고 |
invalid_model | 알 수 없는 모델 지정 | haiku, sonnet, gpt4o 사용 |
invalid_date | 날짜 형식 오류 | YYYY-MM-DD 형식 사용 |
{
"error": {
"code": "missing_field",
"message": "필수 필드 'saju.pillars.day'가 누락되었습니다",
"field": "saju.pillars.day"
}
}
요청 제한 에러 (429)
| 코드 | 설명 | 해결 방법 |
|---|
rate_limited | 요청 과다 | 대기 후 재시도 |
daily_limit_exceeded | 일일 할당량 도달 | 플랜 업그레이드 또는 대기 |
cost_limit_exceeded | 월간 지출 한도 도달 | 대시보드에서 한도 증가 |
{
"error": {
"code": "rate_limited",
"message": "요청 제한 초과. 42초 후에 다시 시도하세요.",
"retryAfter": 42
}
}
서버 에러 (5xx)
| 코드 | 설명 | 해결 방법 |
|---|
internal_error | 예상치 못한 서버 에러 | 백오프와 함께 재시도 |
ai_provider_error | AI API 실패 | 재시도 또는 다른 모델 시도 |
timeout | 요청 시간 초과 | 더 간단한 요청으로 재시도 |
service_unavailable | 일시적 장애 | 상태 페이지 확인, 재시도 |
{
"error": {
"code": "ai_provider_error",
"message": "AI 제공자가 에러를 반환했습니다",
"hint": "다시 시도하거나 다른 모델을 사용하세요"
}
}
에러 처리 모범 사례
JavaScript/TypeScript
class SajuClientError extends Error {
constructor(
public code: string,
message: string,
public requestId?: string,
public retryAfter?: number
) {
super(message);
this.name = 'SajuClientError';
}
}
async function callAPI(endpoint: string, body: object) {
const response = await fetch(`https://api.sajuapi.dev${endpoint}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': process.env.BITHUMB_API_KEY!
},
body: JSON.stringify(body)
});
const data = await response.json();
if (!data.success) {
throw new SajuClientError(
data.error.code,
data.error.message,
data.meta?.requestId,
data.error.retryAfter
);
}
return data;
}
// 에러 처리와 함께 사용
try {
const fortune = await callAPI('/api/daily-fortune', { saju, userName });
console.log(fortune.data);
} catch (error) {
if (error instanceof SajuClientError) {
switch (error.code) {
case 'rate_limited':
await sleep(error.retryAfter! * 1000);
// 재시도...
break;
case 'invalid_api_key':
console.error('API 키 설정을 확인하세요');
break;
default:
console.error(`API 에러 [${error.code}]: ${error.message}`);
}
}
}
Python
class SajuClientError(Exception):
def __init__(self, code: str, message: str, request_id: str = None, retry_after: int = None):
self.code = code
self.message = message
self.request_id = request_id
self.retry_after = retry_after
super().__init__(message)
def call_api(endpoint: str, body: dict):
response = requests.post(
f'https://api.sajuapi.dev{endpoint}',
headers={
'Content-Type': 'application/json',
'X-API-Key': os.environ['BITHUMB_API_KEY']
},
json=body
)
data = response.json()
if not data.get('success'):
error = data.get('error', {})
raise SajuClientError(
code=error.get('code', 'unknown'),
message=error.get('message', '알 수 없는 에러'),
request_id=data.get('meta', {}).get('requestId'),
retry_after=error.get('retryAfter')
)
return data
# 사용법
try:
fortune = call_api('/api/daily-fortune', {'saju': saju, 'userName': '홍길동'})
except SajuClientError as e:
if e.code == 'rate_limited':
time.sleep(e.retry_after)
# 재시도...
else:
print(f'API 에러 [{e.code}]: {e.message}')
print(f'요청 ID: {e.request_id}')
디버깅 팁
- requestId 확인: 모든 에러에 고유 ID가 포함됩니다 - 지원 티켓에 포함하세요
- field 확인: 유효성 검사 에러는 종종 어떤 필드가 잘못되었는지 지정합니다
- hint 읽기: 많은 에러에 해결 방법에 대한 힌트가 포함됩니다
- 상태 페이지 확인: 5xx 에러의 경우 status.sajuapi.dev 확인