django

Django - Throttling

Jueuunn7 2025. 1. 18. 16:41

스로틀링은 클라이언트가 api에 요청할 수 있는 속도를 제한하는 데 사용된다. 

django에서는 권한에 따라 인증되지 않는 사용자에게는 제한적으로 스로틀링을 적용할 수 있고, 분당 몇 회의 요청을 제한하는 bust throttling과 하루 요청 제한을 제한하는 sustained throttling을 적용할 수 있다.

또한 각각 api 마다 다른 스로틀링을 적용할 수 있고, burst와 sustained 스로틀링을 동시에 적용할 수도 있다. 예시로 분당 30회, 하루 1000회로 동시에 제한이 가능하다.

애플리케이션단의 스로틀링은 브루트포스 공격이나 ddos 공격을 막기 위한 방법으로 사용하면 안된다. 공격자는 항상 ip스푸핑이 가능하고 django의 스로틀링은 원자성이 보장되게 속도가 계산되지 않기 때문에 항상 정확하게 작동하지는 않는 상황이 발생할 수도 있다.

내장 스로틀링의 구현은 레이스 컨디션에 취약하다. 동시성이 높은 요청의 경우 정확하게 요청을 제한하지는 못한다. 만약 동시 요청에 의존하고있는 서비스에 대해서는 스로틀링을 직접 구현하는 게 좋다.

 

1. 정책 설정

rest_framework에선 기본적으로 2개의 스로틀링 클래스를 제공한다.

1-1. AnonRateThrottle

인증 요청에는 제한을 두지 않고 비인증 요청은 ip를 기준으로 제한한다.

1-2. UserRateThrottle

인증 요청에는 유저 단위로 제한하고 비인증 요청은 ip를 기준으로 제한한다.

1-3. ScopedRateThrottle

UserRateThrottle과 비슷하지만 각 view 별로 각각 스로틀링을 적용할 수 있다.

 

settings.py 에서 전역적으로 스로틀링을 설정할 수 있다.

REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_CLASSES': [
        'rest_framework.throttling.AnonRateThrottle',  # 인증요청 제한 X, 비인증 제한
        'rest_framework.throttling.UserRateThrottle'  # 유저 단위 제한, 비인증은 ip
    ],
    'DEFAULT_THROTTLE_RATES': {
        'anon': '100/day',
        'user': '1000/day'
    }
}

각 view에서 스로틀링을 적용할 수 있다.

class ExampleView(APIView):
    throttle_classes = [UserRateThrottle]

    def get(self, request):
        return Response(content)

다른 방식이 필요하다면 BaseThrottling 클래스를 상속받아서 직접 구현하는 방법도 있다.

class RandomRateThrottle(throttling.BaseThrottle):
    def allow_request(self, request, view):
        return random.randint(1, 10) != 1

 

2. 클라이언트를 식별하는 방법

HTTP X-Forward-For 헤더와 wsgi 의 REMOTE_ADDR 변수를 통해서 클라이언트의 ip를 고유하게 식별한다. 애플리케이션으로 들어온 패킷에 X-Forward-For 헤더가 설정되어 있으면 해당 헤더에 있는 ip를 기반으로 식별하고 만약 없다면 REMOTE_ADDR 변수를 통해서 식별한다.

Django는 was가 없기 때문에 보통 직접 was를 구성한다. 이때 was에서 X-Forward-For 헤더를 설정해주지 않으면 REMOTE_ADDR로 식별하게 되는데 해당 변수는 wsgi에서 초기화되기 때문에 클라이언트를 식별하는 ip가 was의 private ip로 설정될 가능성이 있다. 이렇게 되면 모든 요청을 차단하는 상황이 발생할 수도 있다.

 

3. cache

스로틀링은 DEFAULT_BACKEND_CACHED를 사용하며 cache에서 list를 get, set 한다. 그래서 성능이 좋은 캐시 저장소를 사용하는게 필요하다. 물론 스로틀별로 캐시 저장소를 다르게 사용할 수도 있다.

스로틀링은 키를 ip로 두고 value를 timestemp를 list로 저장한다. 요청에 대한 값을 변수에 불러오고 해당 변수의 값을 바꾼 후에 다시 캐시에 set을 하는 방식으로 작동하기 때문에 동시성이 높거나 병렬처리 상황에서는 2개의 요청이 한 개의 요청으로 처리되어서 더 많은 요청을 허용하는 상황이 발생할 수 있다.

django-ratelimit 같은 라이브러리를 대용을 사용할 수 있다.

보통은 스로틀링 요청 수가 정확하게 계산될 필요가 없기 때문에 특별한 상황이 아니면 그냥 사용해도 괜찮을 것 같긴 하다.

 

4. 결론

django rest framework의 내장 스로틀링 기능은 간단한 스로틀링이 필요한 서비스에 쉽게 사용이 가능해서 좋은것 같다.

'django' 카테고리의 다른 글

개발시에 발생하는 DRF의 몇몇 문제점들  (0) 2025.02.04
Django - Inverted Index로 검색하기  (0) 2024.12.29
Django - squashmigrations  (0) 2024.12.16
Django - Trailing Slash  (1) 2024.12.02
Django - DB Connection  (0) 2024.10.27