API 속도 제한: 알고리즘 및 구현

Security2026-09-20TryQuickToolBox

당신의 API가 공격을 받고 있습니다. 정교한 해커가 아니라, 초당 수천 번씩 엔드포인트를 두드려 리소스를 소모하고 정상 사용자의 서비스를 저하시키는 단순한 스크립트에 의해서입니다. 속도 제한이 없으면 단 하나의 잘못된 클라이언트가 전체 애플리케이션을 다운시킬 수 있습니다. 이 가이드는 핵심 알고리즘을 설명하고 Node.js와 Nginx에서 실용적인 구현을 보여줍니다.

속도 제한이 중요한 이유

속도 제한은 클라이언트가 주어진 시간 창에서 얼마나 많은 요청을 할 수 있는지 제어합니다. 이는 다음과 같은 공격에 대한 첫 번째 방어선입니다:

보안 외에도 속도 제한은 공정한 사용을 보장하고 계층형 가격 계획과 같은 비즈니스 규칙을 시행하는 데 도움이 됩니다.

속도 제한 알고리즘

네 가지 알고리즘이 API 속도 제한을 지배합니다. 각각 정확성, 메모리 사용량, 버스트 처리에서 트레이드오프가 있습니다.

1. 고정 윈도우

고정된 시간 간격(예: 분당 100 요청)으로 요청을 계산합니다. 윈도우가 재설정되면 카운터도 재설정됩니다.

2. 슬라이딩 윈도우

각 요청의 타임스탬프를 추적하고 지난 N초 내에 몇 개가 포함되는지 계산합니다. 이는 버스트를 완화합니다.

3. 토큰 버킷

버킷에 토큰이 들어 있습니다. 토큰은 고정된 속도로 추가됩니다. 각 요청은 토큰 하나를 소비합니다. 버킷이 비어 있으면 요청이 거부됩니다.

4. 리키 버킷

요청이 큐(버킷)에 들어가고 일정한 속도로 처리됩니다. 큐가 가득 차면 요청이 삭제됩니다.

알고리즘버스트 처리메모리사용 사례
고정 윈도우나쁨낮음간단한 API
슬라이딩 윈도우좋음중간범용
토큰 버킷탁월함중간버스트 허용 API
리키 버킷없음중간트래픽 완화

Node.js에서 속도 제한 구현

Express와 Redis를 사용하여 분산 상태를 가진 토큰 버킷 속도 제한기를 구현합니다. Redis는 여러 서버 인스턴스가 있을 때 필수적입니다.

1단계: 의존성 설치

npm install express redis

2단계: 속도 제한 미들웨어 생성

const redis = require('redis');
const client = redis.createClient();

async function tokenBucketLimiter(req, res, next) {
  const key = `rate_limit:${req.ip}`;
  const capacity = 10; // max tokens
  const refillRate = 1; // tokens per second

  const now = Date.now();
  const data = await client.hGetAll(key);
  let tokens = data.tokens ? parseFloat(data.tokens) : capacity;
  let lastRefill = data.lastRefill ? parseInt(data.lastRefill) : now;

  // Refill tokens based on elapsed time
  const elapsed = (now - lastRefill) / 1000;
  tokens = Math.min(capacity, tokens + elapsed * refillRate);

  if (tokens < 1) {
    return res.status(429).json({ error: 'Too many requests' });
  }

  tokens -= 1;
  await client.hSet(key, {
    tokens: tokens.toString(),
    lastRefill: now.toString()
  });
  await client.expire(key, 60); // auto-cleanup

  next();
}

app.use(tokenBucketLimiter);

이 미들웨어는 토큰 수를 원자적으로 확인하고 업데이트합니다. 프로덕션에서는 경쟁 조건을 피하기 위해 Redis 트랜잭션 또는 Lua 스크립트를 사용하세요.

Nginx에서 속도 제한 구현

Nginx는 limit_req 모듈로 내장 속도 제한을 제공합니다. 이는 리키 버킷 알고리즘을 사용합니다.

1단계: 속도 제한 영역 정의

nginx.conf의 http 블록에서:

limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;

이것은 api라는 10MB 영역을 생성하여 IP당 초당 10개 요청을 허용합니다.

2단계: 제한 적용

location 블록에서:

location /api/ {
    limit_req zone=api burst=20 nodelay;
    proxy_pass http://backend;
}

burst=20은 최대 20개 요청의 짧은 버스트를 허용합니다. nodelay는 버스트 요청을 대기열에 넣지 않고 즉시 처리합니다.

API 속도 제한 모범 사례

FAQ

속도 제한과 스로틀링의 차이점은 무엇인가요?

속도 제한은 임계값을 초과하는 요청을 차단하는 반면, 스로틀링은 요청을 느리게 합니다(예: 대기열에 넣거나 지연). 속도 제한은 이진적이고, 스로틀링은 점진적입니다.

어떤 속도 제한 알고리즘을 사용해야 하나요?

대부분의 API에는 토큰 버킷이 좋은 균형을 제공합니다: 버스트를 허용하면서 평균 속도를 적용합니다. 엄격한 완화가 필요하면 리키 버킷을 사용하세요. 단순성을 위해 고정 윈도우는 저트래픽 API에 적합합니다.

인증된 사용자와 익명 사용자의 속도 제한을 어떻게 처리하나요?

익명 사용자(예: IP별)에게는 더 엄격한 제한을, 인증된 사용자(예: API 키별)에게는 더 관대한 제한을 적용하세요. 구독 계획에 따라 계층형 제한을 구현할 수도 있습니다.

API 트래픽을 분석할 준비가 되셨나요? Nginx Log Analyzer를 사용하여 로그를 파싱하고, 속도 제한 위반을 발견하고, 임계값을 최적화하세요.