고용량 재고 API에 토큰 버킷 속도 제한 적용
Source: Dev.to
공개 프론트엔드나 서드파티 웹훅에 재고 또는 결제 엔드포인트를 노출할 때, 무차별 스크립트, 스크래핑 봇, 재고 독점 알고리즘으로부터 API를 보호하는 것이 필수적인 요구사항이 됩니다. 방어적인 속도 제한이 없으면, 단일 협조 스크립트가 데이터베이스 연결을 쉽게 압도할 수 있습니다.
Simple Counter Reset의 문제점
기본 API 보호를 설정할 때 흔히 저지르는 실수는 고정된 “Fixed Window” 카운터를 사용하는 것입니다(예: 1분당 100 요청 허용, 정확히 시계가 바뀔 때마다 초기화). 이 방식은 11시 59분 59초에 100개의 요청을, 12시 00분 01초에 또 다른 100개의 요청을 보내 서버에 폭풍 트래픽을 두 배로 발생시켜 허용 가능한 버스트 트래픽을 두 배로 늘리고 심각한 성능 저하를 초래하는 큰 결함을 만들게 됩니다.
데이터베이스를 크래시 없이 불균형한 버스트 트래픽을 안전하게 처리하려면, 표준적인 접근 방식은 토큰 버킷 알고리즘을 구현하는 것입니다.
토큰 버킷 패턴
토큰 버킷 알고리즘은 최대 용량을 가진 중앙 버킷을 유지합니다. 토큰은 일정하고 예측 가능한 속도로 버킷에 다시 채워집니다. 들어오는 각 API 요청은 정확히 하나의 토큰을 소비합니다. 버킷이 완전히 비어 있으면 요청은 즉시 429 Too Many Requests 상태 코드와 함께 거부되어 핵심 서버 스레드를 보호합니다.
// 빠른 Redis 기반 토큰 버킷 속도 제한기 개념
async function isRateLimited(userId) {
const key = `rate:${userId}`;
const now = Date.now();
// Redis multi-exec 트랜잭션을 사용해 토큰을 원자적으로 확인하고 업데이트
const [tokens, lastRefill] = await redis.hmget(key, 'tokens', 'lastRefill');
// 경과된 시간에 따라 토큰 재보충을 계산...
// 토큰이 0 이하이면 true 반환, 그렇지 않으면 토큰을 감소시키고 타임스탬프 업데이트
}
Enter fullscreen mode
Exit fullscreen mode
