안정적인 서비스를 위해서는 올바른 에러 처리가 필수입니다. DeliveryAPI의 에러 코드를 이해하고 적절히 대응하는 방법을 알아봅니다.

HTTP 상태 코드

에러 응답 형식

{
  "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 헤더 확인
• 모든 에러 로그 기록

에러 로깅

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);
    }
  }
}