슬라이딩 윈도우란 무엇인가
API 요청 제한(Rate Limiting)은 한 사용자가 정해진 시간 안에 너무 많은 요청을 보내지 못하도록 막는 기법입니다. 이를 구현하는 대표적인 방법 중 하나가 ‘슬라이딩 윈도우(Sliding Window)’ 알고리즘입니다. 이름 그대로 시간이라는 창(윈도우)을 고정하지 않고, 현재 시각을 기준으로 계속 움직이면서 그 범위 안의 요청 수를 세는 방식입니다.
비유하자면 은행 창구에서 ‘최근 1분 동안 몇 명이 다녀갔는지’를 계속 확인하는 것과 같습니다. 시계가 정각을 기준으로 딱딱 끊기는 게 아니라, 지금 이 순간부터 1분 전까지를 항상 기준으로 삼는다는 점이 핵심입니다.
동작 원리와 예시
가장 단순한 구현은 사용자별로 요청 시각을 타임스탬프로 저장해두는 것입니다. 새로운 요청이 들어오면 다음과 같은 순서로 처리합니다.
- 현재 시각에서 윈도우 크기(예: 60초)를 뺀 시점을 계산한다
- 저장된 타임스탬프 중 그 시점보다 오래된 기록은 제거한다
- 남은 타임스탬프 개수가 제한 값(예: 100건) 미만이면 요청을 허용하고 현재 시각을 기록한다
- 제한 값 이상이면 요청을 거부하고 ‘Too Many Requests’ 같은 응답을 반환한다
예를 들어 1분에 5회로 제한한 API가 있다고 가정해봅시다. 사용자가 0초, 10초, 20초, 30초, 40초에 요청을 보냈다면 이미 5회를 채운 상태입니다. 45초에 또 요청이 오면, 현재 시각 45초에서 60초를 뺀 -15초 이후의 기록을 모두 세게 되므로 5건이 그대로 걸려 요청이 거부됩니다. 하지만 61초가 되면 0초의 기록이 윈도우 밖으로 밀려나므로 다시 요청이 허용됩니다.
실제 서비스에서는 매 요청마다 모든 타임스탬프를 비교하면 비효율적이므로, 윈도우를 여러 개의 작은 구간(버킷)으로 쪼개 카운트를 관리하는 ‘슬라이딩 윈도우 카운터’ 방식을 많이 사용합니다. 이전 구간과 현재 구간의 비율을 가중 평균으로 계산해 정확도와 성능을 동시에 확보하는 방식입니다.
실무에서 왜 쓰는가
고정 윈도우(Fixed Window) 방식은 구현이 쉽지만 경계 구간에서 문제가 생깁니다. 예를 들어 0~59초, 60~119초로 윈도우를 나누면, 59초와 60초 사이 짧은 순간에 두 배의 요청이 몰려도 각 윈도우 기준으로는 제한을 넘지 않은 것처럼 보일 수 있습니다. 슬라이딩 윈도우는 시간을 연속적으로 바라보기 때문에 이런 순간적인 폭주(버스트)를 훨씬 정확하게 잡아냅니다.
이런 이유로 Redis 같은 인메모리 저장소와 결합해 API 게이트웨이, 로그인 시도 제한, 결제 API 보호 등 실시간성이 중요한 곳에서 슬라이딩 윈도우가 널리 쓰입니다. 정확도와 성능 사이의 균형을 잘 맞출 수 있다는 점이 실무에서 선호되는 가장 큰 이유입니다.