안정적인 서비스를 위해서는 올바른 에러 처리가 필수입니다. DeliveryAPI의 에러 코드를 이해하고 적절히 대응하는 방법을 알아봅니다.
HTTP 상태 코드
200- 성공400- 잘못된 요청 (파라미터 오류)401- 인증 실패 (잘못된 API 키)403- 권한 없음404- 리소스 없음429- Rate Limit 초과500- 서버 에러
에러 응답 형식
모든 에러는 { isSuccess: false, errorCode, error } 형태로 반환됩니다. errorCode로 에러 유형을 프로그래밍적으로 구분하세요.
{
"isSuccess": false,
"errorCode": "INVALID_PARAMS",
"error": "유효하지 않은 송장번호입니다"
} 주요 errorCode
UNAUTHORIZED(401) - API 키 누락 또는 유효하지 않음FORBIDDEN(403) - 유효한 키지만 접근 권한 없음MISSING_PARAMS/INVALID_PARAMS(400) - 필수 파라미터 누락 또는 형식 오류NOT_FOUND(404) - 요청한 리소스가 없음RATE_LIMITED(429) - 요청 횟수 초과 (응답 바디의retryAfterSeconds확인)INTERNAL_ERROR(500) - 서버 내부 오류
재시도 로직 구현
Exponential Backoff
async function retryRequest(fn, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (error) {
// 재시도 불가능한 에러
if (error.response.status === 400 ||
error.response.status === 401) {
throw error;
}
// 마지막 시도면 에러 throw
if (i === maxRetries - 1) {
throw error;
}
// 대기 시간 계산 (1초, 2초, 4초)
const delay = Math.pow(2, i) * 1000;
await new Promise(resolve => setTimeout(resolve, delay));
}
}
} Rate Limit 처리
async function handleRateLimit(error) {
if (error.response.status === 429) {
// retryAfterSeconds는 HTTP 헤더가 아니라 응답 바디에 포함됩니다.
const retryAfter = error.response.data?.retryAfterSeconds ?? 60;
console.log(`Rate limit 초과 (${error.response.data?.errorCode}). ${retryAfter}초 후 재시도`);
await new Promise(resolve =>
setTimeout(resolve, retryAfter * 1000)
);
// 재시도
return await retryRequest(apiCall);
}
} 타임아웃 설정
const axios = require('axios');
const api = axios.create({
baseURL: 'https://api.deliveryapi.co.kr/v1',
timeout: 10000, // 10초
headers: {
'Authorization': `Bearer ${API_KEY}`
}
});
api.interceptors.response.use(
response => response,
async error => {
if (error.code === 'ECONNABORTED') {
console.log('타임아웃 발생');
// 재시도 로직
}
return Promise.reject(error);
}
); ✅ 베스트 프랙티스
• 400/401 에러는 재시도하지 마세요
• 500/503 에러는 재시도하세요
• Exponential Backoff 사용
• Rate Limit 헤더 확인
• 모든 에러 로그 기록
• 400/401 에러는 재시도하지 마세요
• 500/503 에러는 재시도하세요
• Exponential Backoff 사용
• Rate Limit 헤더 확인
• 모든 에러 로그 기록
에러 로깅
function logError(error, context) {
console.error({
timestamp: new Date().toISOString(),
context: context,
status: error.response?.status,
errorCode: error.response?.data?.errorCode,
message: error.response?.data?.error,
stack: error.stack
});
} Circuit Breaker 패턴
연속된 에러 발생 시 API 호출을 일시 중단합니다.
class CircuitBreaker {
constructor(threshold = 5, timeout = 60000) {
this.failureCount = 0;
this.threshold = threshold;
this.timeout = timeout;
this.state = 'CLOSED';
}
async call(fn) {
if (this.state === 'OPEN') {
throw new Error('Circuit breaker is OPEN');
}
try {
const result = await fn();
this.onSuccess();
return result;
} catch (error) {
this.onFailure();
throw error;
}
}
onSuccess() {
this.failureCount = 0;
}
onFailure() {
this.failureCount++;
if (this.failureCount >= this.threshold) {
this.state = 'OPEN';
setTimeout(() => {
this.state = 'CLOSED';
this.failureCount = 0;
}, this.timeout);
}
}
}