핵심 요약
Rate limiting은 버스트 허용 vs 평활화와 정확도 vs 메모리의 트레이드오프다. 대부분의 API에는 Token Bucket이 가장 실용적.
알고리즘 4종 비교
| 알고리즘 | 버스트 | 정확도 | 구현 |
|---|---|---|---|
| Fixed Window | 경계에서 2배 | 낮음 | 매우 쉬움 |
| Sliding Window Log | 없음 | 최고 | 메모리 큼 |
| Sliding Window Counter | 거의 없음 | 높음 | 쉬움 |
| Token Bucket | 제어 가능 | 높음 | 쉬움 |
Token Bucket (Redis Lua)
-- KEYS[1] = bucket key
-- ARGV[1] = capacity, ARGV[2] = refill_rate (tokens/sec)
-- ARGV[3] = now (ms), ARGV[4] = requested
local capacity = tonumber(ARGV[1])
local rate = tonumber(ARGV[2])
local now = tonumber(ARGV[3])
local want = tonumber(ARGV[4])
local state = redis.call('HMGET', KEYS[1], 'tokens', 'ts')
local tokens = tonumber(state[1]) or capacity
local last = tonumber(state[2]) or now
local delta = math.max(0, now - last) / 1000
tokens = math.min(capacity, tokens + delta * rate)
local allowed = 0
if tokens >= want then
tokens = tokens - want
allowed = 1
end
redis.call('HMSET', KEYS[1], 'tokens', tokens, 'ts', now)
redis.call('PEXPIRE', KEYS[1], 60000)
return {allowed, tokens}
Node.js 래퍼
const script = fs.readFileSync('tokenbucket.lua', 'utf8');
const sha = await redis.scriptLoad(script);
async function check(userId, capacity = 100, rate = 10) {
const [ok, remaining] = await redis.evalSha(sha, {
keys: [`rl:${userId}`],
arguments: [String(capacity), String(rate), String(Date.now()), '1'],
});
return { ok: ok === 1, remaining };
}
응답 헤더 규격
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 87
X-RateLimit-Reset: 1713234567
Retry-After: 13 # 429 응답 시
키 설계
- 개인화:
rl:user:{id} - IP 기반:
rl:ip:{ip}(프록시 X-Forwarded-For 주의) - 엔드포인트 분리:
rl:user:{id}:POST:/v1/transfer - 티어:
rl:user:{id}:tier:{pro}
분산 환경 주의
- 레디스 단일 인스턴스 병목 → 샤딩 or Cluster mode
- 원자성: 반드시 Lua script로 "읽기-수정-쓰기" 묶기
- clock skew: 서버 로컬 time 사용 금지, Redis TIME 또는 요청 시각 일원화
프레임워크별 구현
- Nginx: limit_req_zone (leaky bucket, 로컬)
- Envoy: RateLimitService(rls) 외부 참조
- Next.js middleware: Edge에서 Upstash Redis 연동
- FastAPI: slowapi + aiolimiter
자주 묻는 질문
429 vs 503 선택?
클라이언트의 호출량 과다 → 429. 서버 과부하 → 503. 혼용 금지.
DDoS 방어로 충분한가?
아니다. L7 rate limit은 합법 클라이언트 제어용. L3/L4 공격은 WAF/DDoS 보호 서비스가 필요.
댓글 0