요청 제한(rate limiting, throttling)은 일정 시간 동안 한 클라이언트가 보낼 수 있는 요청 수를 묶어 두는 장치다. 한도를 넘으면 429 Too Many Requests로 거절하고, 보통 Retry-After 헤더로 언제 다시 시도하면 되는지 알려 준다.
왜 필요한가
- 무차별 대입(brute force) 로그인, 스크래핑, 실수로 생긴 무한 재시도로부터 서버와 다운스트림 자원을 보호한다
- 사용자·요금제별로 공정하게 자원을 나눈다
- 권한(permission)이 "할 수 있나"를 정한다면, 요청 제한은 "얼마나 자주"를 정한다(인증과 인가)
기준과 알고리즘
- 누구를 셀까: 비로그인은 IP, 로그인은 사용자 ID, 외부 고객은 API 키
- 고정 윈도(fixed window): 1분 단위로 카운터를 센다. 단순하지만 경계에서 순간적으로 두 배가 몰릴 수 있다
- 슬라이딩 윈도(sliding window): 최근 N초를 기준으로 센다. 경계 문제를 줄인다(슬라이딩 윈도우)
- 토큰 버킷(token bucket): 일정 속도로 토큰이 차고 요청마다 하나씩 쓴다. 짧은 버스트를 허용하면서 평균 속도를 제한한다
서버가 여러 대면 카운터를 공유해야 하므로 Redis (인메모리 저장소)의 INCR과 TTL로 구현하는 경우가 많다.
DRF에서
REST_FRAMEWORK = {
"DEFAULT_THROTTLE_CLASSES": [
"rest_framework.throttling.AnonRateThrottle",
"rest_framework.throttling.UserRateThrottle",
],
"DEFAULT_THROTTLE_RATES": {"anon": "100/day", "user": "1000/day", "review-create": "5/minute"},
}
class ReviewCreate(generics.CreateAPIView):
throttle_classes = [ScopedRateThrottle]
throttle_scope = "review-create"AnonRateThrottle(IP 기준)·UserRateThrottle(사용자 ID 기준)은 API 전체에 걸쳐 클라이언트당 카운터 하나를 쓴다. 특정 엔드포인트만 따로 제한하려면ScopedRateThrottle과throttle_scope를 쓴다- DRF 문서도 말하듯 이것은 남용 방지 수준이지 강력한 보안 장치가 아니다. 공격 방어는 게이트웨이·WAF(Web Application Firewall) 수준에서 함께 한다
클라이언트 쪽에서 재시도할 때는 지수 백오프(exponential backoff)로 간격을 늘리고, 재시도되는 요청은 멱등하게 만든다.
출처: RFC 6585: 429 Too Many Requests · DRF Throttling · ScopedRateThrottle