안정적인 서비스를 위해서는 올바른 에러 처리가 필수입니다. DeliveryAPI의 에러 코드를 이해하고 적절히 대응하는 방법을 알아봅니다.
HTTP 상태 코드
200- 성공400- 잘못된 요청 (파라미터 오류)401- 인증 실패 (잘못된 API 키)403- 권한 없음404- 리소스 없음429- Rate Limit 초과500- 서버 에러
에러 응답 형식
{
"error": {
"code": "invalid_tracking_number",
"message": "유효하지 않은 송장번호입니다",
"details": {
"trackingNumber": "ABC123",
"carrier": "cj"
}
}
}
재시도 로직 구현
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) {
const retryAfter = error.response.headers['retry-after'];
console.log(`Rate limit 초과. ${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,
code: error.response?.data?.error?.code,
message: error.response?.data?.error?.message,
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);
}
}
}