API
API rate limit에 자꾸 걸릴 때
Rate limit 에러는 응답 헤더에 힌트가 있다. Retry-After를 읽고, 요청 빈도를 조절하거나 캐시 전략을 바꾸자.
API 호출이 자꾸 429 Too Many Requests를 내보낸다. 또는 다른 API의 rate limit에 계속 걸린다. 문제를 좁혀보자.
첫 번째: 응답 헤더를 본다
# 응답 헤더 확인
curl -i https://api.example.com/data
# 관련 헤더
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 5
X-RateLimit-Reset: 1672502400
Retry-After: 60
Rate limit 관련 헤더를 보면, 초당 몇 개의 요청을 허용하는지, 남은 요청이 몇 개인지, 언제 리셋되는지 알 수 있다.
두 번째: 자신의 요청 빈도를 측정한다
# 최근 요청 로그를 본다
grep "GET /api" access.log | tail -20
# 또는 애플리케이션 로그
grep "API call" app.log | tail -50
실제로 몇 초에 몇 개의 요청을 보내고 있는지 확인한다. 특히 병렬 요청이나 배치 작업에서는 의도치 않게 limit을 초과할 수 있다.
세 번째: 캐시를 활용한다
// Node.js 예제: 메모리 캐시
const cache = new Map();
async function fetchData(url) {
const cached = cache.get(url);
if (cached && Date.now() - cached.timestamp < 60000) {
return cached.data; // 1분 내 캐시는 재사용
}
const response = await fetch(url);
const data = await response.json();
cache.set(url, { data, timestamp: Date.now() });
return data;
}
Rate limit은 보통 같은 요청을 반복 호출할 때 걸린다. 결과를 캐시해서 중복 요청을 줄인다.
네 번째: 요청을 큐에 넣고 지연 실행한다
// 간단한 큐 구현
class RequestQueue {
constructor(maxPerSecond = 5) {
this.queue = [];
this.maxPerSecond = maxPerSecond;
}
async add(fn) {
return new Promise((resolve) => {
this.queue.push({ fn, resolve });
this.process();
});
}
async process() {
while (this.queue.length > 0) {
const batch = this.queue.splice(0, this.maxPerSecond);
const results = await Promise.all(batch.map(item => item.fn()));
results.forEach((result, i) => batch[i].resolve(result));
await new Promise(r => setTimeout(r, 1000));
}
}
}
const queue = new RequestQueue(5); // 초당 5개
await queue.add(() => fetch(url1));
await queue.add(() => fetch(url2));
요청을 큐에 넣고, 시간 단위로 조절해서 limit을 초과하지 않게 한다.
다섯 번째: Retry-After를 존중한다
async function fetchWithRetry(url, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
const response = await fetch(url);
if (response.status === 429) {
const retryAfter = response.headers.get('Retry-After');
const delay = parseInt(retryAfter) * 1000 || 5000;
console.log(`Rate limited. Retrying after ${delay}ms`);
await new Promise(r => setTimeout(r, delay));
continue;
}
return response;
}
throw new Error('Max retries exceeded');
}
429 응답을 받으면, 서버가 제시한 Retry-After 시간만큼 기다렸다가 다시 시도한다.
체크리스트
- 응답 헤더에서
X-RateLimit-*값을 본다 - 실제 요청 빈도를 측정한다
- 캐시를 통해 중복 요청을 줄인다
- 요청을 큐에 넣고 시간당 개수를 제한한다
- 429 응답을 받으면
Retry-After를 존중한다 - 외부 API를 사용한다면, 상위 계획(higher tier)으로 업그레이드 고려
Rate limit은 서버 리소스 보호의 정당한 메커니즘이다. 최대한 친절하게 대응하자.