Throttle with DRF

Haks.·2025년 3월 29일
0

Study

목록 보기
62/65

Throttling

왜 쓰는가 ?

  • API 남용 방지(과도한 요청)
  • 비즈니스 정책 반영(무료/유료 티어)
  • 서버 자원 보호
  • 보안 목적으로는 사용 X(IPsppofing 쉽게 가능함)

Why Use?

  • 비인증 사용자에게 낮은 제한, 인증 사용자에게 더 높은 제한 주고 싶을 때
  • 특정 API가 리소스를 많이 써서, 해당 API만 따로 제한 걸고 싶을 때
  • 짧은 시간(버스트)긴 시간(지속적) 요청 제한을 함께 쓰고 싶을 때

설정 방법

  1. settings.py 전역 설정
REST_FRAMEWORK = {
	# 전역설정
    'DEFAULT_THROTTLE_CLASSES' : [
        'rest_framework.throttling.AnonRateThrottle', # 비인증
        'rest_framework.throttling.UserRateThrottle',
    ]
    # 설정 값
    'DEFAULT_THROTTLE_RATES' : {
        'anon': '100/day',
        'user': '1000/day', 
    }
}
  • DEFAULT_THROTTLE_CLASSES 를 설정해놓으면 전역에 설정된다.
  • DEFAULT_THROTTLE_RATES 를 설정하고 개인 API에 적용시키면 덮어써 진다
  • 전역 설정을 하지 않고 API마다 따로 설정하고 싶으면 아래의 RATES 부분만 settings에 작성한 후
    개인 API에 throttle_class 를 지정해 주면 된다.
  1. 개별 View에서 설정
from rest_framework.throttling import UserRateThrottle

class MyView(APIView):
    throttle_classes = [UserRateThrottle]
  1. @api_view와 데코레이터
@api_view(['GET'])
@throttle_classes([UserRateThrottle])
def my_view(request):
    ...

주요 Throttle 클래스들

클래스설명
AnonRateThrottle인증 안 된 사용자에게 IP 기준으로 제한
UserRateThrottle인증된 사용자 ID 기준 제한(없으면 IP로 fallback)
ScopedRateThrottleView 별로 throttle scope 설정해 제한 가능
CustomThrottle직접 throttle 로직 상성 가능(BaseThrottle 상속)

사용자 구분 기준

  • X-Forwarded-For 헤더 -> 프록시를 거친 경우에 사용됨
    • X-Forwarded-For 헤더는 클라이언트의 원래 IP주소를 알기 위해 사용하는 HTTP 헤더이다. 이 헤더는 프록시나 로드밸런서 같은 중간 서버를 거쳐서 들어온 요청에 추가된다.
    • ex) X-Forwarded-For: 10.0.0.1, 123.123.123.1
      • 123.123.123.1 은 실제 클라이언트 IP
      • 나머지는 프록시나 로드밸런스 IP 이다 장고는 이걸 기준으로 클라이언트 IP를 뽑으려 한다.
  • 없으면 Remote_ADDR 사용
    • X-Forwarded-For가 없으면 장고는 기본적으로 WSGI 환경 변수인 REMOTE_ADDR을 사용한다
      이건 실제 서버에 도달한 요청의 IP인데, 프록시를 거쳤다면 프록시의 IP 일 수 있어서 정확하지 않을 수 있음
  • NUM_PROXIES 설정으로 실제 클라이언트 IP 추출 가능
    • X-Forwarded-For에서 실제 클라이언트 IP를 뽑을 ㄸ'ㅐ, 프록시를 몇개 거치는지 알려주는 설정
REST_FRAMEWORK = {
    'NUM_PROXIES': 2

# X-Forwarded-For: 123.123.123.1, 10.0.0.1, 10.0.0.2
# 앞에 1번째 = 실제 클라이언트 , 나머지 2개 NUM_PROXIES proxy ip 로 보는 것이다.
}

💻 예시: Burst + Sustained 이중 제한

  • Burst, Sustained
class BurstRateThrottle(UserRateThrottle):
    scope = 'burst'
class SustainedRateThrottle(UserRateThrottle):
    scope = 'sustained'

REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_CLASSES': [
        'my_app.throttles.BurstRateThrottle',
        'my_app.throttles.SustainedRateThrottle',
    ],
    'DEFAULT_THROTTLE_RATES': {
        'burst: '60/min',
        'sustained' : '1000/day',
    }
}

# 이중 제한 적용
class myapp_view(APIView):
    throttle_classes = [BurstRateThrottline, SustainedRateThrottle]

burst(버스트)

  • 짧은 시간 동안 허용되는 최대 요청 수
  • Ex) 60/min -> 1분에 최대 60번 요청 허용

목적
"잠깐동안 급하게 많은 요청을 보내는 건 허용하되,
너무 오래 그렇게 보내지는 못 하게 하자"

비유
손님이 순간적으로 몰려드는건 괜챃지만,
계속 몰리면 주방이 터지는 거니까 그런 걸 막는 장치

sustained(지속)

  • 긴 시간 동안 허용되는 총 요청 수를 의미
  • Ex) 1000/day -> 하루에 최대 1000번 요청 가능

목적
"서버 자원을 하루 단위로 적절히 분배해서 남용 방지"

비유
하루에 너무 많이 오는 단골손님을 쫓아낼 수는 없지만
일정 이상은 못 사게 제한하는 것

burst + sustained

  • 사용자가 1분 안에 60번 넘게 요청 -> burst 초과로 차단
  • 사용자가 하루 종일 천천히 요청해서 1000번 도달 -> sustained 초과로 차단

결론

Throttle를 사용하는데 하나의 제한이 아닌 burst + sustained로 나눠서 두개의 제한을 둘수 있다.
또한 인증된 User와 그렇지 않은 유저의 제한을 나눌수 있다.
UserRateThrottle -> 인증된 유저
AnonRateThrottle -> 인증되지 않은 유저

각각을 상속받아 스코프를 나누어 제한을 나눌수도 있다.

API 프로젝트를 하다 Throttle에 대한 개념을 정리해 보았다.

0개의 댓글

관련 채용 정보