사용자 등급별/IP별 요청량을 제어하는 Throttling 정책 설계

API 서버의 안정성을 위해 특정 기간 동안 API 호출 횟수를 제한하는 Throttling 기술을 구현하고, 사용자 등급에 따라 차등적인 서비스 품질을 제공하는 방법

Throttling의 필요성

  • 서버 안정성 확보: DDoS 공격 등 과도한 요청으로부터 서버 보호

  • 공정한 자원 분배: 모든 사용자에게 공평한 API 사용 기회 제공

  • 서비스 품질(QoS) 보장: 등급별 차등 서비스로 만족도 향상

  • 비용 관리: 외부 API 호출 비용 절약

  • 비즈니스 모델: 무료/유료 사용자 구분을 통한 수익 모델 구현

DRF 기본 Throttling 클래스

클래스명
대상
식별 기준
설명

AnonRateThrottle

비로그인 사용자

IP 주소

인증되지 않은 사용자 요청 제한

UserRateThrottle

로그인 사용자

User ID

인증된 사용자 요청 제한

ScopedRateThrottle

특정 API

뷰 scope

뷰마다 다른 제한 적용

기본 설정

# settings.py
REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_CLASSES': [
        'rest_framework.throttling.AnonRateThrottle',
        'rest_framework.throttling.UserRateThrottle',
    ],
    'DEFAULT_THROTTLE_RATES': {
        'anon': '100/day',
        'user': '1000/hour',
    }
}

사용자 등급별 커스텀 Throttling 구현

시나리오

  • 등급별 시간당 요청 제한

    • free: 100회/시간

    • bronze: 500회/시간

    • silver: 2,000회/시간

    • gold: 10,000회/시간

  • 비로그인 사용자: IP당 60회/시간

커스텀 Throttling 클래스

# throttling.py
from rest_framework.throttling import SimpleRateThrottle

class TierBasedRateThrottle(SimpleRateThrottle):
    scope = 'user'

    def get_cache_key(self, request, view):
        if request.user and request.user.is_authenticated:
            self.scope = getattr(request.user, 'tier', 'free')
            ident = request.user.pk
        else:
            self.scope = 'anon'
            ident = self.get_ident(request)

        return self.cache_format % {
            'scope': self.scope,
            'ident': ident
        }

설정 등록

# settings.py
REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_CLASSES': [
        'a_project.throttling.TierBasedRateThrottle',
    ],
    'DEFAULT_THROTTLE_RATES': {
        'anon': '60/hour',
        'free': '100/hour',
        'bronze': '500/hour',
        'silver': '2000/hour',
        'gold': '10000/hour',
    }
}

실무 고려사항

프록시 환경에서의 IP 식별

# settings.py
NUM_PROXIES = 1  # 프록시 서버가 1개 있는 경우

캐시 백엔드 설정

# settings.py
CACHES = {
    "default": {
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://127.0.0.1:6379/1",
    }
}

특정 뷰에서 Throttling 비활성화

class HealthCheckView(APIView):
    throttle_classes = []  # Throttling 적용 안함

Last updated