오류 처리
오류 응답 형식
모든 API 오류는 적절한 HTTP 상태 코드와 함께 일관된 JSON 구조를 따릅니다:
{
"error": {
"code": "ERROR_CODE",
"message": "Human-readable description of what went wrong"
}
}code 필드는 오류 처리 로직에서 사용할 수 있는 안정적이고 기계 판독 가능한 식별자입니다. message 필드는 추가 컨텍스트를 제공하며 동일한 오류 코드의 발생 간에 다를 수 있습니다.
오류 카테고리
인증 오류 (401)
요청을 인증할 수 없을 때 반환됩니다.
| 코드 | 설명 |
|---|---|
UNAUTHORIZED | Authorization 헤더가 누락되었거나, 형식이 잘못되었거나, 유효하지 않은 토큰을 포함합니다. |
KEY_EXPIRED | API 키는 유효했지만 만료 날짜가 지났습니다. 계속하려면 새 키를 생성하세요. |
{
"error": {
"code": "UNAUTHORIZED",
"message": "Missing or invalid Authorization header"
}
}권한 오류 (403)
인증된 사용자가 요청된 리소스에 접근할 권한이 없을 때 반환됩니다.
| 코드 | 설명 |
|---|---|
FORBIDDEN | 다른 사용자에게 속한 리소스에 접근하거나 수정하려 합니다 (예: 다른 사람의 API 키 폐기). |
리소스 없음 오류 (404)
요청된 리소스가 존재하지 않을 때 반환됩니다.
| 코드 | 설명 |
|---|---|
NOT_FOUND | URL에 지정된 리소스(추출, API 키 등)가 존재하지 않거나 삭제되었습니다. |
유효성 검사 오류 (400)
요청 본문 또는 파라미터가 유효하지 않을 때 반환됩니다.
| 코드 | 설명 |
|---|---|
VALIDATION_ERROR | 요청 본문이 스키마 유효성 검사에 실패했습니다. message 필드에 어떤 필드가 유효하지 않은지 설명됩니다. |
MAX_KEYS_REACHED | 최대 10개의 활성 API 키에 도달했습니다. 새 키를 생성하기 전에 기존 키를 폐기하세요. |
{
"error": {
"code": "MAX_KEYS_REACHED",
"message": "Maximum of 10 active API keys allowed"
}
}요금 제한 오류 (429)
사용 한도를 초과했을 때 반환됩니다.
| 코드 | 설명 |
|---|---|
USAGE_LIMIT_EXCEEDED | 월간 추출 한도에 도달했습니다. 플랜을 업그레이드하거나 다음 청구 기간까지 기다리세요. |
{
"error": {
"code": "USAGE_LIMIT_EXCEEDED",
"message": "Monthly extraction limit reached (25/25). Upgrade your plan for more extractions."
}
}서버 오류 (500)
서버 측에서 예기치 않은 일이 발생했을 때 반환됩니다.
| 코드 | 설명 |
|---|---|
INTERNAL_ERROR | 예기치 않은 서버 오류가 발생했습니다. 자동으로 기록되고 조사됩니다. |
EXTRACTION_FAILED | AI 처리 파이프라인이 문서에서 데이터를 추출하는 중 오류가 발생했습니다. 문서가 손상되었거나, 읽을 수 없거나, 지원되지 않는 형식일 수 있습니다. |
재시도 가능한 오류
모든 오류를 재시도해야 하는 것은 아닙니다. 다음은 분류입니다:
| 코드 | 재시도 가능 | 조치 |
|---|---|---|
INTERNAL_ERROR | 예 | 지수 백오프로 재시도 |
EXTRACTION_FAILED | 예 | 재시도 -- AI가 다른 결과를 생성할 수 있습니다. 지속되면 문서 품질을 확인하세요 |
USAGE_LIMIT_EXCEEDED | 아니오 | 플랜을 업그레이드하거나 다음 청구 기간까지 기다리세요 |
UNAUTHORIZED | 아니오 | 인증 자격 증명을 수정하세요 |
KEY_EXPIRED | 아니오 | 새 API 키를 생성하세요 |
FORBIDDEN | 아니오 | 소유하지 않은 리소스에 접근하고 있습니다 |
NOT_FOUND | 아니오 | 리소스가 존재하지 않습니다 -- ID를 확인하세요 |
VALIDATION_ERROR | 아니오 | 예상 스키마와 일치하도록 요청 본문을 수정하세요 |
MAX_KEYS_REACHED | 아니오 | 새 키를 생성하기 전에 기존 키를 폐기하세요 |
재시도 전략
재시도 가능한 오류의 경우 서버에 과부하를 주지 않도록 지수 백오프를 사용하세요. 다음은 재사용 가능한 구현입니다:
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");
}사용법:
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초)로 세 번 재시도하는 것이 서버 오류에 대한 합리적인 기본값입니다.
