No history yet

속도 제한의 고급 개념

다양한 속도 제한 알고리즘

단순히 요청 횟수를 세는 것 이상으로, 시스템을 안정적으로 유지하기 위한 정교한 속도 제한 전략들이 있습니다. 각기 다른 장단점을 가진 여러 알고리즘을 살펴보겠습니다.

토큰 버킷 (Token Bucket)

이 알고리즘은 버킷에 주기적으로 토큰을 채워 넣는 방식입니다. 각 요청은 버킷에서 토큰 하나를 가져가야 처리될 수 있습니다. 만약 버킷에 토큰이 없다면 요청은 거부됩니다. 이 방식의 가장 큰 장점은 갑작스러운 트래픽 증가(버스트)를 유연하게 처리할 수 있다는 점입니다. 버킷에 쌓여있는 토큰만큼은 순간적으로 많은 요청을 허용할 수 있기 때문이죠. 하지만 버킷 크기와 토큰 충전 속도라는 두 가지 변수를 설정해야 해서, 시스템에 맞게 최적화하는 과정이 까다로울 수 있습니다.

누수 버킷 (Leaky Bucket)

이름처럼 물이 새는 양동이를 생각하면 쉽습니다. 요청이 들어오면 일단 양동이(큐)에 담깁니다. 그리고 양동이 바닥의 구멍을 통해 일정한 속도로 요청이 하나씩 빠져나가 처리됩니다. 요청이 아무리 많이 쏟아져 들어와도, 시스템은 항상 일정한 속도로만 처리하게 됩니다. 이 방식은 시스템에 가해지는 부하를 일정하게 유지하여 안정성을 높이는 데 효과적입니다. 하지만 모든 요청이 큐를 거쳐야 하므로 약간의 지연이 발생할 수 있고, 갑작스러운 버스트 트래픽으로 큐가 가득 차면 새로운 요청들은 처리되지 못하고 버려지게 됩니다.

윈도우 기반 알고리즘

시간을 일정한 간격(윈도우)으로 나누어 요청을 제어하는 방식도 널리 쓰입니다. 이 방식은 구현이 비교적 간단하지만, 몇 가지 함정을 가지고 있습니다.

고정 윈도우 카운터 (Fixed Window Counter)

가장 기본적인 방식으로, '1분당 100개'처럼 정해진 시간 단위(윈도우) 동안의 요청 횟수를 셉니다. 구현이 매우 간단하지만 치명적인 약점이 있습니다. 예를 들어, 제한이 1분당 100개일 때, 어떤 사용자가 00:00:59에 100개의 요청을 보내고, 00:01:01에 또 100개의 요청을 보낼 수 있습니다. 이 경우, 단 2초 동안 200개의 요청이 허용되어 순간적으로 시스템에 큰 부하를 줄 수 있습니다. 윈도우가 바뀌는 경계 시점에서 제한량의 두 배까지 요청이 몰릴 수 있는 것이죠.

슬라이딩 윈도우 (Sliding Window)

고정 윈도우의 문제를 해결하기 위해 등장했습니다. 이 방식은 현재 시간을 기준으로 과거의 특정 기간(예: 지난 60초)을 항상 확인합니다.

  • 슬라이딩 윈도우 로그 (Sliding Window Log): 가장 정확한 방식입니다. 각 요청의 타임스탬프를 모두 기록하고, 새 요청이 오면 현재 시간에서 윈도우 크기를 뺀 시간보다 오래된 기록을 삭제한 뒤, 남은 기록의 개수가 제한보다 적으면 요청을 허용합니다. 정확도는 높지만 모든 타임스탬프를 저장해야 하므로 메모리 사용량이 매우 큽니다.

  • 슬라이딩 윈도우 카운터 (Sliding Window Counter): 로그 방식의 메모리 문제를 개선한 하이브리드 방식입니다. 이전 윈도우의 요청 카운터와 현재 윈도우의 요청 카운터를 함께 사용합니다. 예를 들어, 1분 단위 윈도우를 사용하고 현재 시간이 1분 30초 지점이라면, 이전 1분의 카운터 값의 50%와 현재까지의 카운터 값을 더해 요청량을 계산합니다. 이는 정확성과 자원 효율성 사이의 균형을 맞춘 영리한 접근법입니다.

알고리즘장점단점
토큰 버킷버스트 트래픽에 유연함파라미터 튜닝이 필요함
누수 버킷출력 속도가 안정적임지연 발생, 버스트 처리 어려움
고정 윈도우구현이 간단함윈도우 경계에서 트래픽 집중 문제
슬라이딩 윈도우 로그매우 정확함메모리 사용량이 많음
슬라이딩 윈도우 카운터정확성과 효율성의 균형고정 윈도우보다 구현이 복잡함

실제 사례와 고려사항

이론적인 알고리즘을 실제 시스템에 적용할 때는 더 복잡한 문제들을 마주하게 됩니다. 몇 가지 사례를 통해 실전에서의 속도 제한을 살펴보겠습니다.

사례 1: 마이크로서비스 환경

수많은 마이크로서비스가 서로 통신하는 환경에서는 중앙 집중형 속도 제한 시스템이 필요합니다. 각 서비스가 개별적으로 속도 제한을 구현하면 전체 시스템의 요청 한도를 정확하게 관리하기 어렵기 때문입니다. Redis와 같은 인메모리 데이터 스토어를 사용하여 모든 서비스가 공유하는 카운터를 관리하는 것이 일반적인 해결책입니다. 하지만 이 경우 Redis 서버가 단일 장애점(SPOF)이 될 수 있으므로, 고가용성을 위한 이중화 구성이 필수적입니다.

사례 2: 비용 기반 속도 제한

모든 API 호출이 동일한 비용을 유발하지는 않습니다. 간단한 데이터 조회 요청은 비용이 저렴하지만, 복잡한 데이터 처리나 보고서 생성 요청은 훨씬 많은 서버 자원을 소모합니다. 이런 경우, 요청 횟수 기반이 아닌 '비용' 기반으로 속도 제한을 적용하는 것이 합리적입니다. 각 API 엔드포인트마다 가중치를 부여하고, 사용자는 정해진 시간 동안 할당된 총 '비용' 한도 내에서만 요청을 보낼 수 있습니다. 예를 들어, 간단한 조회는 1점, 복잡한 처리는 10점을 소모하게 하는 식입니다.

또한, 속도 제한에 걸린 사용자에게 어떤 정보를 반환할지도 중요합니다. HTTP 429 (Too Many Requests) 상태 코드와 함께, 언제 다시 요청할 수 있는지 알려주는 Retry-After 헤더를 포함하는 것이 좋습니다. 더 나아가, 현재 남은 요청 횟수나 제한이 초기화되는 시간 등의 정보를 X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset 같은 커스텀 헤더에 담아 제공하면 클라이언트 개발자가 훨씬 편리하게 API를 사용할 수 있습니다.

Quiz Questions 1/4

갑작스러운 트래픽 증가(버스트)를 유연하게 처리하는 데 가장 적합한 속도 제한 알고리즘은 무엇인가요?

Quiz Questions 2/4

고정 윈도우 카운터(Fixed Window Counter) 알고리즘의 가장 큰 단점은 무엇인가요?

올바른 속도 제한 전략은 단순히 시스템을 보호하는 것을 넘어, 사용자 경험과 서비스의 공정성을 보장하는 핵심 요소입니다. 각 전략의 특성을 이해하고 시스템의 요구사항에 맞는 최적의 방법을 선택하는 것이 중요합니다.