DRF Throttling에서 “scope” 쓰는 두 가지 방식 비교: ScopedRateThrottle vs UserRateThrottle 상속
DRF에서 scope 기반 throttling을 걸 때 자주 나오는 두 패턴이 있습니다.
- 방식 A:
UserRateThrottle(또는SimpleRateThrottle)을 상속해 scope가 박힌 커스텀 클래스를 만들고, 뷰에throttle_classes로 직접 붙인다. - 방식 B:
DEFAULT_THROTTLE_CLASSES에ScopedRateThrottle을 등록해두고, 뷰에는throttle_scope = "login"만 적는다.
질문처럼 “결국 동일한 효과 아닌가?”에 대한 답부터 하면:
단일 속도 제한을 ‘유저ID(또는 비로그인 IP) + scope’ 기준으로 걸겠다는 수준에서는, 결과가 거의 동일합니다. 다만 적용 방식(실수 방지), 확장성(복수 정책/커스텀 기준), 코드 구조(설정 중심 vs 코드 중심)에서 차이가 나서 선택 기준이 생깁니다.
공통점: 둘 다 “scope + (user id or IP)”로 키를 만든다
ScopedRateThrottle은 뷰에throttle_scope가 있으면 scope + 유저ID/IP로 키를 구성하고,DEFAULT_THROTTLE_RATES에서 해당 scope rate를 찾아 적용합니다.UserRateThrottle도 기본적으로 인증 유저는 user id, 비인증은 IP를 기준으로 제한하며, 여러 개를 쓰려면 클래스를 상속해 scope를 다르게 두는 형태를 DRF가 공식 예시로 제시합니다.
차이 1) “어디에 scope를 적느냐”: 뷰 속성 vs 클래스 속성
방식 A: 클래스에 scope를 고정(명시적)
from rest_framework.throttling import UserRateThrottle
from rest_framework.views import APIView
class CustomDomainSaveThrottle(UserRateThrottle):
scope = "custom_domain_save"
class SaveCustomDomainView(APIView):
throttle_classes = [CustomDomainSaveThrottle]
- scope가 클래스에 고정됩니다.
- 뷰는 “이 클래스 쓰겠다”만 선언하면 끝.
- 실수로 scope 문자열을 뷰마다 오타내는 케이스가 줄어듭니다(클래스명이 사실상 문서 역할).
방식 B: 뷰에 scope만 달고, 공용 throttle이 해석(설정 중심)
REST_FRAMEWORK = {
"DEFAULT_THROTTLE_CLASSES": [
"rest_framework.throttling.ScopedRateThrottle",
],
"DEFAULT_THROTTLE_RATES": {
"login": "5/min",
}
}
from rest_framework.views import APIView
class LoginView(APIView):
throttle_scope = "login"
- scope가 뷰에 직접 들어갑니다.
- 공통 throttle(
ScopedRateThrottle)이 “이 뷰는 login 스코프네?” 하고 설정에서 rate를 찾아 씁니다. - 코드가 짧고, settings에서 정책을 한눈에 관리하기 좋습니다.
차이 2) “적용을 누가 보장하느냐”: 기본 등록 vs 뷰별 장착
방식 B의 포인트(오해가 많음)
DEFAULT_THROTTLE_CLASSES에 ScopedRateThrottle을 넣어도, throttle_scope가 없는 뷰에는 적용되지 않습니다.
즉 “전역에 넣으면 전체 API가 다 막힐까?” 걱정은 보통 필요 없습니다.
반대로 방식 A의 강점
전역 설정을 건드리지 않고도, 특정 뷰에만 원하는 throttle을 “장착”할 수 있습니다. (팀/서비스 구조상 settings를 크게 못 만지는 경우나, 특정 앱/엔드포인트만 강제하고 싶을 때 편합니다.)
차이 3) 확장성: “단일 제한”을 넘어가면 A가 유리해진다
둘이 거의 같아 보이는 구간은 “한 뷰에 한 스코프, 한 속도 제한”까지입니다. 그 다음부터는 차이가 벌어집니다.
1) “버스트 + 지속” 같이 복수 throttle을 한 뷰에 걸고 싶을 때
DRF 문서도 여러 UserRateThrottle을 동시에 쓰는 패턴(버스트/서스테인)을 예시로 듭니다.
class Burst(UserRateThrottle):
scope = "burst"
class Sustained(UserRateThrottle):
scope = "sustained"
class SearchView(APIView):
throttle_classes = [Burst, Sustained]
ScopedRateThrottle만으로는 “한 뷰에 스코프를 2개”를 자연스럽게 표현하기가 어렵고,
결국 클래스가 필요해집니다(또는 커스텀 구현).
2) “누구를 기준으로 제한할지”가 바뀌는 순간
ScopedRateThrottle은 기본적으로 user id / IP 축에서 움직입니다.
그런데 실무에서는 이런 요구가 금방 나옵니다.
- 로그인: IP + username 조합으로 제한하고 싶다(계정 타겟 공격 대응)
- 조직/테넌트: tenant_id 기준으로 묶어서 제한하고 싶다
- API Key: 키 단위로 제한하고 싶다
이건 결국 get_cache_key() 같은 로직 커스텀이 필요해져서 방식 A(상속/커스텀 클래스)로 자연스럽게 이동합니다. (DRF도 커스텀 throttle을 권장하는 지점이 이쪽입니다.)

그래서 뭘 쓰면 좋을까?
대부분의 “뷰별 rate limit”이면 → 방식 B(ScopedRateThrottle) 추천
- 코드 최소화
- settings에서 정책 일괄 관리
- 뷰에는
throttle_scope만 적으면 끝 - scope 없는 뷰에는 적용 안 됨
아래 중 하나라도 해당하면 → 방식 A(상속/커스텀 클래스) 추천
- 한 뷰에 버스트+지속 등 복수 제한을 동시에 걸고 싶다
- “user/IP”가 아닌 커스텀 기준(username+IP, tenant, API key 등)이 필요하다
- 전역 설정을 건드리기 어렵고 특정 뷰에만 강제 장착하고 싶다
- 클래스명으로 정책을 “문서화”하고 싶다(읽는 사람이 바로 의도를 파악)
관련 글: