Skip to main content

에러 응답 형식

모든 에러는 다음 구조를 따릅니다:
{
  "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_errorAI 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}')

디버깅 팁

  1. requestId 확인: 모든 에러에 고유 ID가 포함됩니다 - 지원 티켓에 포함하세요
  2. field 확인: 유효성 검사 에러는 종종 어떤 필드가 잘못되었는지 지정합니다
  3. hint 읽기: 많은 에러에 해결 방법에 대한 힌트가 포함됩니다
  4. 상태 페이지 확인: 5xx 에러의 경우 status.sajuapi.dev 확인