Skip to content

오류 처리

오류 응답 형식

모든 API 오류는 적절한 HTTP 상태 코드와 함께 일관된 JSON 구조를 따릅니다:

json
{
  "error": {
    "code": "ERROR_CODE",
    "message": "Human-readable description of what went wrong"
  }
}

code 필드는 오류 처리 로직에서 사용할 수 있는 안정적이고 기계 판독 가능한 식별자입니다. message 필드는 추가 컨텍스트를 제공하며 동일한 오류 코드의 발생 간에 다를 수 있습니다.

오류 카테고리

인증 오류 (401)

요청을 인증할 수 없을 때 반환됩니다.

코드설명
UNAUTHORIZEDAuthorization 헤더가 누락되었거나, 형식이 잘못되었거나, 유효하지 않은 토큰을 포함합니다.
KEY_EXPIREDAPI 키는 유효했지만 만료 날짜가 지났습니다. 계속하려면 새 키를 생성하세요.
json
{
  "error": {
    "code": "UNAUTHORIZED",
    "message": "Missing or invalid Authorization header"
  }
}

권한 오류 (403)

인증된 사용자가 요청된 리소스에 접근할 권한이 없을 때 반환됩니다.

코드설명
FORBIDDEN다른 사용자에게 속한 리소스에 접근하거나 수정하려 합니다 (예: 다른 사람의 API 키 폐기).

리소스 없음 오류 (404)

요청된 리소스가 존재하지 않을 때 반환됩니다.

코드설명
NOT_FOUNDURL에 지정된 리소스(추출, API 키 등)가 존재하지 않거나 삭제되었습니다.

유효성 검사 오류 (400)

요청 본문 또는 파라미터가 유효하지 않을 때 반환됩니다.

코드설명
VALIDATION_ERROR요청 본문이 스키마 유효성 검사에 실패했습니다. message 필드에 어떤 필드가 유효하지 않은지 설명됩니다.
MAX_KEYS_REACHED최대 10개의 활성 API 키에 도달했습니다. 새 키를 생성하기 전에 기존 키를 폐기하세요.
json
{
  "error": {
    "code": "MAX_KEYS_REACHED",
    "message": "Maximum of 10 active API keys allowed"
  }
}

요금 제한 오류 (429)

사용 한도를 초과했을 때 반환됩니다.

코드설명
USAGE_LIMIT_EXCEEDED월간 추출 한도에 도달했습니다. 플랜을 업그레이드하거나 다음 청구 기간까지 기다리세요.
json
{
  "error": {
    "code": "USAGE_LIMIT_EXCEEDED",
    "message": "Monthly extraction limit reached (25/25). Upgrade your plan for more extractions."
  }
}

서버 오류 (500)

서버 측에서 예기치 않은 일이 발생했을 때 반환됩니다.

코드설명
INTERNAL_ERROR예기치 않은 서버 오류가 발생했습니다. 자동으로 기록되고 조사됩니다.
EXTRACTION_FAILEDAI 처리 파이프라인이 문서에서 데이터를 추출하는 중 오류가 발생했습니다. 문서가 손상되었거나, 읽을 수 없거나, 지원되지 않는 형식일 수 있습니다.

재시도 가능한 오류

모든 오류를 재시도해야 하는 것은 아닙니다. 다음은 분류입니다:

코드재시도 가능조치
INTERNAL_ERROR지수 백오프로 재시도
EXTRACTION_FAILED재시도 -- AI가 다른 결과를 생성할 수 있습니다. 지속되면 문서 품질을 확인하세요
USAGE_LIMIT_EXCEEDED아니오플랜을 업그레이드하거나 다음 청구 기간까지 기다리세요
UNAUTHORIZED아니오인증 자격 증명을 수정하세요
KEY_EXPIRED아니오새 API 키를 생성하세요
FORBIDDEN아니오소유하지 않은 리소스에 접근하고 있습니다
NOT_FOUND아니오리소스가 존재하지 않습니다 -- ID를 확인하세요
VALIDATION_ERROR아니오예상 스키마와 일치하도록 요청 본문을 수정하세요
MAX_KEYS_REACHED아니오새 키를 생성하기 전에 기존 키를 폐기하세요

재시도 전략

재시도 가능한 오류의 경우 서버에 과부하를 주지 않도록 지수 백오프를 사용하세요. 다음은 재사용 가능한 구현입니다:

typescript
interface ApiResponse<T> {
  data?: T;
  error?: { code: string; message: string };
}

async function fetchWithRetry<T>(
  url: string,
  options: RequestInit,
  maxRetries = 3,
): Promise<ApiResponse<T>> {
  let lastError: Error | null = null;

  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    try {
      const response = await fetch(url, options);
      const body = await response.json();

      if (response.ok) {
        return body as ApiResponse<T>;
      }

      const errorCode = body?.error?.code;

      // Only retry server errors
      if (
        (errorCode === "INTERNAL_ERROR" || errorCode === "EXTRACTION_FAILED") &&
        attempt < maxRetries
      ) {
        const delayMs = 1000 * Math.pow(2, attempt); // 1s, 2s, 4s
        console.log(`Retrying in ${delayMs}ms (attempt ${attempt + 1}/${maxRetries})...`);
        await new Promise((resolve) => setTimeout(resolve, delayMs));
        continue;
      }

      // Non-retryable error -- return immediately
      return body as ApiResponse<T>;
    } catch (err) {
      lastError = err as Error;

      if (attempt < maxRetries) {
        const delayMs = 1000 * Math.pow(2, attempt);
        await new Promise((resolve) => setTimeout(resolve, delayMs));
        continue;
      }
    }
  }

  throw lastError ?? new Error("Max retries exceeded");
}

사용법:

typescript
const result = await fetchWithRetry<ExtractionResult>(
  "https://api.docmap.io/v1/extractions/run",
  {
    method: "POST",
    headers: {
      Authorization: `Bearer ${apiKey}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      templateId: "tmpl_abc123",
      fileName: "invoice.pdf",
      pdfBase64: encodedPdf,
      mimeType: "application/pdf",
    }),
  },
);

if (result.error) {
  console.error(`Extraction failed: [${result.error.code}] ${result.error.message}`);
} else {
  console.log("Extracted data:", result.data);
}

모범 사례

  • 항상 HTTP 상태 코드를 먼저 확인하세요. 2xx 상태는 성공을 의미하고, 그 외는 오류입니다. 상태를 확인하지 않고 응답 본문에 data 필드가 있다고 가정하지 마세요.

  • code 필드를 파싱하여 오류를 처리하세요. 제어 흐름에서 message가 아닌 code를 사용하세요. 오류 메시지는 시간이 지남에 따라 변경될 수 있지만 오류 코드는 안정적입니다.

  • 특정 오류 코드를 처리하세요. USAGE_LIMIT_EXCEEDED 또는 KEY_EXPIRED 같은 알려진 코드에 대해 분기하여 사용자에게 맞춤 메시지를 표시하거나 특정 워크플로를 트리거하세요.

  • 알 수 없는 오류를 로그에 기록하세요. 명시적으로 처리하지 않는 오류 코드를 만나면 전체 응답(상태 코드, 오류 코드, 메시지)을 디버깅을 위해 로그에 기록하세요.

  • 4xx 오류를 재시도하지 마세요. 인증, 권한, 유효성 검사, 리소스 없음 오류는 자체적으로 해결되지 않습니다. 재시도하기 전에 근본 원인을 수정하세요.

  • 재시도 제한을 설정하세요. 무한 재시도를 하지 마세요. 지수 백오프(1초, 2초, 4초)로 세 번 재시도하는 것이 서버 오류에 대한 합리적인 기본값입니다.

DocMap API 문서