대량 추적 시 API Rate Limiting 활용 가이드
하루에 수천 개의 택배를 추적해야 할 때, API Rate Limit을 이해하고 효율적으로 활용하는 것이 매우 중요합니다. 이 가이드에서는 Rate Limit 범위 내에서 처리량을 극대화하는 다섯 가지 전략을 소개합니다.
WhereParcel Rate Limit 구조
WhereParcel의 Rate Limit은 요금제에 따라 다르게 적용됩니다.
| 요금제 | 분당 요청 수 | 일일 요청 수 | 배치 크기 |
|---|---|---|---|
| Free | 10 | 500 | 1 |
| Starter | 60 | 10,000 | 10 |
| Business | 300 | 100,000 | 50 |
| Enterprise | 협의 | 협의 | 100 |
모든 API 응답 헤더에 현재 Rate Limit 상태가 포함됩니다.
X-RateLimit-Limit: 300
X-RateLimit-Remaining: 297
X-RateLimit-Reset: 1706200000
X-RateLimit-Remaining 값을 확인하면 남은 요청 횟수를 실시간으로 파악할 수 있습니다.
전략 1: 배치 추적 활용
택배를 하나씩 조회하는 대신 배치 엔드포인트를 사용하면 API 호출 수를 획기적으로 줄일 수 있습니다.
// ❌ 비효율적: 50건을 개별 요청으로 처리
for (const parcel of parcels) {
await fetch('/v2/track', {
body: JSON.stringify({
carrier: parcel.carrier,
trackingNumber: parcel.trackingNumber,
}),
});
}
// ✅ 효율적: 1번의 배치 요청으로 처리
await fetch('/v2/track/batch', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.WHEREPARCEL_API_KEY}:${process.env.WHEREPARCEL_SECRET_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
parcels: parcels.map(p => ({
carrier: p.carrier,
trackingNumber: p.trackingNumber,
})),
}),
});
배치 요청은 포함된 택배 수와 관계없이 1건의 요청으로 카운트됩니다 (요금제별 배치 크기 한도 내에서). 50건을 개별 요청하면 50회의 Rate Limit을 소모하지만, 배치로 보내면 단 1회만 소모됩니다.
전략 2: 스마트 캐싱
모든 추적 요청이 매번 API를 호출할 필요는 없습니다. 택배의 현재 상태에 따라 캐시 기간을 다르게 설정하세요.
function getCacheDuration(status) {
switch (status) {
case 'delivered':
return 24 * 60 * 60; // 24시간 - 상태가 바뀌지 않음
case 'out_for_delivery':
return 5 * 60; // 5분 - 곧 변경될 가능성 높음
case 'in_transit':
return 30 * 60; // 30분
case 'picked_up':
return 60 * 60; // 1시간
default:
return 15 * 60; // 15분
}
}
async function getTracking(carrier, trackingNumber) {
const cacheKey = `tracking:${carrier}:${trackingNumber}`;
const cached = await cache.get(cacheKey);
if (cached) return cached;
const result = await whereparcel.track(carrier, trackingNumber);
const ttl = getCacheDuration(result.status);
await cache.set(cacheKey, result, ttl);
return result;
}
핵심은 상태별로 캐시 기간을 차등 적용하는 것입니다. 이미 배송 완료된 택배는 24시간 캐싱해도 문제 없지만, 배송 출발 상태인 택배는 5분마다 갱신해야 고객에게 정확한 정보를 제공할 수 있습니다.
전략 3: Exponential Backoff 적용
Rate Limit에 도달하여 HTTP 429 응답을 받으면, Exponential Backoff를 적용하여 점진적으로 재시도합니다.
async function trackWithRetry(carrier, trackingNumber, maxRetries = 3) {
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
const response = await fetch('/v2/track', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.WHEREPARCEL_API_KEY}:${process.env.WHEREPARCEL_SECRET_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ trackingItems: [{ carrier, trackingNumber }] }),
});
if (response.status === 429) {
const retryAfter = response.headers.get('Retry-After') || 60;
const delay = Math.min(retryAfter * 1000, 2 ** attempt * 1000);
await new Promise(resolve => setTimeout(resolve, delay));
continue;
}
return await response.json();
} catch (error) {
if (attempt === maxRetries) throw error;
await new Promise(resolve =>
setTimeout(resolve, 2 ** attempt * 1000)
);
}
}
}
429 응답의 Retry-After 헤더를 우선 참고하되, 없는 경우 2의 거듭제곱으로 대기 시간을 늘려갑니다. 이렇게 하면 서버에 부담을 주지 않으면서 안정적으로 재시도할 수 있습니다.
전략 4: 폴링 대신 웹훅 사용
API 호출을 줄이는 가장 효과적인 방법은 폴링을 완전히 중단하는 것입니다. 웹훅을 등록하면 상태가 변경될 때 WhereParcel이 직접 알려줍니다.
// ❌ 비효율적: 1,000개 택배를 30분마다 폴링 = 일일 48,000건 요청
setInterval(async () => {
for (const parcel of activeParcels) {
await trackParcel(parcel);
}
}, 30 * 60 * 1000);
// ✅ 효율적: 웹훅 1회 등록 후 업데이트를 자동 수신
await fetch('/v2/webhooks', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.WHEREPARCEL_API_KEY}:${process.env.WHEREPARCEL_SECRET_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
url: 'https://yourapp.com/webhooks/tracking',
events: ['status_changed'],
}),
});
// 결과: 택배 하나당 배송 기간 동안 약 3~5건의 웹훅 호출
활성 택배 1,000건 기준으로 일일 API 사용량이 48,000건에서 약 500건으로 대폭 감소합니다. Rate Limit 걱정 없이 대규모 추적이 가능해집니다.
전략 5: 활성 택배 우선순위 관리
모든 택배에 동일한 폴링 주기를 적용할 필요가 없습니다. 발송 후 경과 일수와 현재 상태에 따라 조회 빈도를 차등 적용하세요.
function getPollingInterval(parcel) {
const daysSinceShipped = getDaysSince(parcel.shippedDate);
if (parcel.status === 'delivered') return null; // 폴링 중지
if (parcel.status === 'out_for_delivery') return 5; // 5분
if (daysSinceShipped <= 1) return 30; // 30분
if (daysSinceShipped <= 5) return 60; // 1시간
if (daysSinceShipped <= 14) return 180; // 3시간
return 720; // 12시간
}
배송 완료된 택배는 더 이상 조회하지 않고, 배송 출발 상태의 택배는 5분마다 조회하는 식으로 자원을 집중 배분합니다. 발송 후 오래된 택배일수록 상태 변경 빈도가 낮으므로, 폴링 간격을 늘려도 무방합니다.
API 사용량 모니터링
예상치 못한 Rate Limit 초과를 방지하려면, API 사용량을 실시간으로 모니터링하세요.
class RateLimitMonitor {
constructor() {
this.remaining = Infinity;
this.resetTime = 0;
}
updateFromResponse(headers) {
this.remaining = parseInt(headers['x-ratelimit-remaining']);
this.resetTime = parseInt(headers['x-ratelimit-reset']);
if (this.remaining < 10) {
console.warn(`Rate limit warning: ${this.remaining} requests remaining`);
}
}
canMakeRequest() {
if (this.remaining <= 0) {
const now = Math.floor(Date.now() / 1000);
return now >= this.resetTime;
}
return true;
}
}
잔여 요청 수가 임계값 이하로 떨어지면 경고를 발생시키고, 새로운 요청 전에 canMakeRequest()로 가능 여부를 확인하는 구조입니다.
전략별 절감 효과 요약
| 전략 | API 호출 절감률 |
|---|---|
| 배치 추적 | 최대 50배 |
| 스마트 캐싱 | 40~70% |
| 웹훅 (폴링 대체) | 95% 이상 |
| 우선순위 기반 폴링 | 50~80% |
이 전략들을 조합하면, 수만 건의 택배를 추적하면서도 Rate Limit 범위 내에서 충분히 운영할 수 있습니다. 대규모 물량을 처리해야 하는 경우에는 문의하기를 통해 Enterprise 요금제의 커스텀 한도를 상담 받으실 수 있습니다.