스터디 노트

Chapter 04

처리율 제한 장치의 설계

Rate Limiter 알고리즘(토큰 버킷, 누출 버킷, 고정 윈도우, 이동 윈도우 로그/카운터)과 분산 환경에서의 동기화, Redis 활용.

개요

처리율 제한 장치(Rate Limiter)는 클라이언트나 서비스가 정해진 시간 동안 요청할 수 있는 횟수를 제한하는 컴포넌트다. 임계치를 넘어선 요청은 차단된다. 트위터(시간당 트윗 300개), 구글 docs(분당 쓰기 요청 300건) 같은 익숙한 제약이 모두 이런 형태.

이 장치를 두는 이유는 크게 세 가지다.

  • DoS(Denial of Service) 공격 방어 — 의도적·비의도적 과도 트래픽으로부터 서비스 보호
  • 비용 절감 — 외부 API 호출(LLM, SMS, 이메일 등)에 지불하는 돈 통제
  • 서버 과부하 방지 — 봇이나 잘못된 사용 패턴으로 인한 자원 고갈 차단

1단계: 문제 이해 및 설계 범위 확정

면접에서 먼저 확정해야 할 요구사항:

  • 클라이언트 측 vs 서버 측 제한기? (보통 서버 측)
  • 제한 기준은 IP? 사용자 ID? 다른 속성?
  • 시스템 규모 — 스타트업 수준 vs 거대 기업 규모?
  • 분산 환경에서 동작해야 하는가?
  • 독립 서비스 vs 애플리케이션 코드에 포함?
  • 제한된 요청에 사용자에게 알려야 하는가?

대표적인 요구사항 묶음 예시:

  • 설정된 임계치를 초과하는 요청을 정확하게 제한
  • 낮은 응답 지연 — 사용자 경험 저해 금지
  • 가능한 적은 메모리 사용
  • 분산 환경에서 여러 서버·프로세스 간 공유
  • 예외 처리 — 사용자에게 명확히 알림
  • 높은 결함 감내성 — 제한 장치 장애가 전체 시스템 장애로 이어지지 않게

2단계: 개략적 설계 — 어디에 둘까?

크게 세 가지 위치 중 선택할 수 있다.

  • 클라이언트 측 — 우회가 쉽고 클라이언트 구현을 통제할 수 없어 일반적으로 권장하지 않음.
  • 서버 측 — 가장 일반적. 애플리케이션 미들웨어 또는 전담 컴포넌트로 구현.
  • API 게이트웨이 — 인증·SSL·IP 화이트리스트와 함께 처리율 제한도 게이트웨이가 담당. 클라우드 환경의 표준.

"어디에 둘 것인가"의 결정은 기술 스택, 엔지니어링 인력, 우선순위, 사업적 필요에 따라 다르다. 중간 수준의 트래픽엔 게이트웨이 활용이 편하고, 대규모·세밀한 제어가 필요하면 직접 구축한다.

처리율 제한 알고리즘

이 장의 핵심. 5가지 알고리즘을 비교한다. 각각 메모리 사용량, 정확도, 구현 복잡도가 다르다.

① 토큰 버킷(Token Bucket)

정해진 용량의 버킷에 일정한 비율로 토큰이 채워진다. 요청이 오면 토큰 1개를 소모. 토큰이 없으면 거부. 가장 널리 쓰이는 알고리즘 (Amazon, Stripe).

  • 파라미터 2개: 버킷 용량, 토큰 보충률
  • 장점: 구현 쉬움, 메모리 적음, 짧은 트래픽 폭주(burst) 허용
  • 단점: 두 파라미터를 잘 튜닝해야 함
  • 🛠 실무 도구: AWS API Gateway throttling(burst + steady-state), Stripe, GitHub Primary Rate Limit, 라이브러리는 guava RateLimiter (Java), golang.org/x/time/rate (Go)

① 토큰 버킷

용량 4 · 보충 1/s
버킷
4 / 4
다음 토큰까지1.00s
요청 보내기를 눌러보세요

💡 빠르게 4번 누르면 버킷이 비어 다음 토큰이 채워질 때까지 거부됩니다. 잠시 기다리면 다시 모입니다 — 버스트 허용의 의미.

② 누출 버킷(Leaky Bucket)

요청이 큐(고정 크기)에 들어가고, 일정한 비율로 빠져나가 처리된다. 큐가 가득 차면 새 요청 거부. 보통 FIFO 큐로 구현 (Shopify).

  • 파라미터: 버킷 크기(큐 크기), 처리율(누수 속도)
  • 장점: 처리 속도가 일정해 안정적
  • 단점: 트래픽 폭주를 못 흡수 (오래된 요청도 처리 못함)
  • 🛠 실무 도구: Nginx limit_req(가장 유명한 구현체 — 사실상 표준), Shopify, 메시지 큐 워커 자체가 누출 버킷 형태(Kafka consumer with rate limit, AWS SQS)

② 누출 버킷

큐 4 · 누수 1/s
↓ 입력
↓ 1/s 누수
0 / 4
다음 누수까지1.00s
요청 보내기를 눌러보세요

💡 토큰 버킷과 달리 처리 속도가 일정합니다. 빠르게 보내도 한꺼번에 통과하지 않고 1/s로 천천히 빠져나갑니다. 그래서 버스트 허용 X.

🤔 그럼 누출 버킷은 사용자 입장에선 답답한데 왜 쓰나?

맞다. 그래서 사용자가 화면에서 직접 응답을 기다리는 API에는 거의 다 토큰 버킷을 쓴다. 누출 버킷은 사용자가 직접 기다리지 않는 곳, 즉 받는 쪽 시스템이 한꺼번에 몰리는 트래픽을 못 받아주는 경우에 쓴다. 예를 들어:

  • Stripe API 호출 — Stripe는 1초에 100건만 받음. 토큰 버킷처럼 적립해뒀다 한꺼번에 200건 보내면 외부에서 막힌다. 누출 버킷이 1초에 100건씩 천천히 흘려보내야 안전.
  • SMS / 이메일 발송 (AWS SES 14/s 등) — 초과 시 throttle. 비동기로 처리하니 사용자도 즉시 응답 안 기다림.
  • 핫 로우가 많은 DB 쓰기 워커 — 한꺼번에 몰리면 락 경합/데드락. 평탄화하면 경합 빈도 감소.
    ※ 단, DB 동시 쓰기의 정확성 문제(lost update 등)는 처리율 제한이 아니라 트랜잭션·락으로 풀어야 한다. 누출 버킷은 경합 "빈도"만 낮춰주는 부수 효과.

→ 토큰 버킷 = "사용자가 빠를 때 빠르게"
→ 누출 버킷 = "받는 쪽이 못 견디는 트래픽 평탄화"

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

타임라인을 고정 길이 윈도우(예: 1분)로 나누고, 각 윈도우의 카운터를 증가시켜 임계치 초과 시 거부.

  • 장점: 메모리 효율 좋음, 이해·구현 단순
  • 단점: 윈도우 경계에서 트래픽이 몰리면 임계치의 2배까지 허용될 수 있음. 예: 분당 5건 한도에서 12:00:59에 5건 + 12:01:00에 5건 가능.
  • 유리한 상황: 윈도우가 길어 경계 2배 문제가 무시할 만할 때. 시간/일/월 단위 쿼터 — GitHub API 시간당 5,000회, SaaS "월 1만 호출" 같은 과금성 한도. 사용자당 (count, window_start) 두 값만 저장하면 돼서 메모리 초저렴(INCR + EXPIRE 원자 1회).
  • 🛠 실무 도구: GitHub 시간당 쿼터, Reddit, 대부분의 SaaS 월 쿼터. 직접 만들 땐 Redis INCR key; EXPIRE key 60 두 줄이면 끝.

③ 고정 윈도우 카운터

한도 5/3s
W-3
0 / 5
W-2
0 / 5
W-1
0 / 5
W0
0 / 5
현재 진행: 0%
요청 보내기를 눌러보세요

경계 문제: 현재 윈도우 끝부분에서 5번 빠르게 보내고, 다음 윈도우 시작에서 또 5번 보내보세요. 1초 안에 10개가 통과합니다 — 한도(5/3s)의 거의 2배.

④ 이동 윈도우 로그(Sliding Window Log)

요청 타임스탬프를 시간 정렬 집합(Redis Sorted Set 등)에 저장. 요청이 오면 윈도우 밖 타임스탬프는 제거하고 집합 크기로 임계치 비교.

  • 장점: 매우 정확. 윈도우 경계 문제 없음
  • 단점: 거부된 요청도 타임스탬프를 저장하므로 메모리 사용 큼
  • 🛠 실무 도구: 보안 시스템(로그인 시도 5회 잠금), 과금 정확성 중요한 LLM/결제 호출 한도. Redis Sorted Set (ZADD + ZREMRANGEBYSCORE) 패턴.
✅ 유리한 상황
짧은 윈도우 + 낮은 한도 + 정확성 중요
  • 로그인 시도 "60초 내 5회 실패 시 잠금"
    → 사용자당 5 타임스탬프
  • 결제 / LLM 호출 "분당 10회"
    → 사용자당 10 타임스탬프
  • 어뷰징 탐지 정확한 시간 분포가 의사결정에 직접 영향
❌ 불리한 상황
긴 윈도우 + 높은 한도 (시간/일/월 쿼터)
왜? 두 알고리즘이 저장하는 게 근본적으로 다름:
이동 윈도우 로그 — 모든 요청 타임스탬프 저장
[12:34:56.123,
 12:34:56.789,
 12:35:01.234,
 ...
 13:33:59.999]   ← 5,000개
5,000 × 8B = 40 KB / 사용자
고정 윈도우 — 숫자 두 개만
{ count: 3247,
  window_start: 13:00:00 }
16 B / 사용자 (한도값과 무관)
한 줄 차이: 이동 윈도우 로그는 "언제"를 다 기억 → 한도만큼 메모리. 고정 윈도우는 "몇 건"만 기억 → 두 숫자로 끝.
그래서 100만 명일 때:
이동 윈도우 로그40 GB
고정 윈도우16 MB
2,500배 차이
게다가 시간 단위 한도에서 1~2건 오차를 누가 신경 쓰나 → 정확도 이득도 0

④ 이동 윈도우 로그

한도 5/5s
-5s
now
윈도우 안: 0 / 5
요청 보내기를 눌러보세요

💡 매 요청마다 5초 밖 타임스탬프가 자동으로 빠져나갑니다. 정확하지만 거부된 요청도 저장하므로 메모리가 큽니다.

⑤ 이동 윈도우 카운터(Sliding Window Counter)

고정 윈도우 카운터와 이동 윈도우 로그의 절충안. 현재 윈도우 카운터와 이전 윈도우의 일부(가중평균)로 추정.

요청 수 ≈ 현재 윈도우 카운터 + 이전 윈도우 카운터 × (윈도우에 겹치는 비율)

  • 장점: 메모리 효율 + 정확도 절충
  • 단점: 직전 윈도우의 요청이 균등 분포라는 가정에 의존
  • 🛠 실무 도구: Cloudflare Rate Limiting이 실제로 이걸 씀 (실측 오차 0.003%로 발표). Nginx Plus의 일부 구현, 다수의 API 게이트웨이.

⑤ 이동 윈도우 카운터

한도 5/3s
이전 윈도우 W-1
0
가중치 × 1.00
현재 윈도우 W0
0
추정 = 0 + 0 × 1.00 = 0.00 / 5
요청 보내기를 눌러보세요

💡 이전 윈도우가 균등 분포라고 가정합니다. 윈도우가 진행될수록 이전 윈도우의 영향(가중치)이 줄어듭니다.

비교: 경계 공격에 세 알고리즘은 어떻게 다른가

한도 5/3s
현재 윈도우 W0, 다음 경계까지 3.00s

시나리오: 윈도우 경계 직전에 5개, 직후에 5개 → 0.5초 안에 10개. 각 알고리즘이 어떻게 반응하는지 보세요.

③ 고정 윈도우
통과
0
거부
0
W0 카운트: 0 / 5
W-1 카운트: 0 / 5
메모리: ~16 B
단순하지만 경계 취약
④ 이동 윈도우 로그
통과
0
거부
0
윈도우 안 타임스탬프: 0 / 5
저장 배열 크기: 0
메모리: 0 B
✅ 정확하지만 메모리↑
⑤ 이동 윈도우 카운터
통과
0
거부
0
현재 0 + 이전 0 × 1.00
= 추정 0.00 / 5
메모리: ~32 B
✅ 정확 + 메모리 적음
이동 윈도우 카운터의 이득: 고정 윈도우의 경계 문제를 해결하면서, 이동 윈도우 로그의 메모리 폭발도 피한다. 통과 수는 로그와 같고, 메모리는 고정 윈도우와 비슷한 수준. 단점은 이전 윈도우의 요청이 균등 분포라는 가정이 어긋나면 약간의 오차.

🤔 그럼 이동 윈도우 카운터가 최강인가?

아니다. 비교 시각화에선 깔끔해 보이지만 근사치다. "이전 윈도우 요청이 균등 분포"라는 가정 위에 서 있어서, 트래픽이 한쪽에 쏠리면 어긋난다.

A. 이전 윈도우 시작에 몰림
이전 [0~3s]의 0.1s에 5건
현재 4.5s (50% 진행)
진짜 윈도우 [1.5~4.5s] 안: 0건
카운터 추정: 5 × 0.5 = 2.5
→ 과대평가 · 불필요한 거부
B. 이전 윈도우 에 몰림
이전 [0~3s]의 2.9s에 5건
현재 4.5s (50% 진행)
진짜 윈도우 [1.5~4.5s] 안: 5건
카운터 추정: 5 × 0.5 = 2.5
→ 과소평가 · 한도 초과 통과

실측에선 0.003% 수준의 오차라(Cloudflare 데이터) 대부분의 일반 API엔 충분. 단, burst-heavy 트래픽이나 보안 정확성이 중요한 곳엔 부적합.

각 알고리즘이 이기는 영역이 따로 있다:

  • 장기 쿼터 (시간/일/월) → 고정 윈도우 (더 단순, 경계 문제 무시할 만함)
  • 보안 (로그인 시도, 어뷰징 탐지) → 이동 윈도우 로그 (정확값 필요)
  • 사용자 UX, 버스트 허용 → 토큰 버킷 (적립 개념이 정책)
  • 외부 API/DB 보호, 순간율 엄수 → 누출 버킷 (평탄화)
  • "보통의 API 한도" (요구사항 모호) → 이동 윈도우 카운터 (안전한 기본값)

→ 트레이드오프 좋음 ≠ 최선. 시스템마다 알고리즘이 다른 게 정상.

알고리즘 비교 한눈에

알고리즘정확도메모리버스트구현
토큰 버킷적음허용쉬움
누출 버킷흡수 X
고정 윈도우낮음 (경계 문제)적음허용쉬움
이동 윈도우 로그높음
이동 윈도우 카운터중상적음

실무에선 직접 만들지 않는다

여기까지 알고리즘과 분산 처리를 다뤘지만, 실제로 회사에서 대부분의 개발자는 이걸 처음부터 구현하지 않는다. 이미 만들어진 도구를 고르고·설정·조립한다. 이론을 배우는 진짜 이유는 그 도구를 의심·비교·디버깅할 수 있기 위해서.

계층별 도구 매핑

실제 시스템은 여러 층의 보호막이 겹쳐 있다. 바깥에서 안쪽으로:

계층대표 제품주 역할
🌐 CDN / WAFCloudflare, AWS WAF, FastlyIP 거친 한도, DDoS 방어
🚪 API 게이트웨이Kong, AWS API Gateway, ApigeeAPI 키별·사용자별 한도
🔄 리버스 프록시Nginx, HAProxy, EnvoyIP 한도 + 부하 분산
⚙ 앱 미들웨어express-rate-limit, Bucket4j, Flask-Limiter비즈니스 로직 연동 (+ Redis)
🔌 외부 호출 클라이언트bottleneck, Resilience4j다운스트림 보호 (Stripe, OpenAI)

회사 규모별 표준 조합

규모일반적인 스택직접 구현하는 것
스타트업 / 사이드Cloudflare(무료) + 앱 미들웨어 + Redis거의 없음. 라이브러리 설치·설정만
중소Cloudflare + Nginx + 앱 미들웨어 + Redis비즈니스 정책 매핑 (사용자 티어별 한도)
중대규모+ API 게이트웨이(Kong/AWS) + ElastiCache게이트웨이 플러그인 / Lua 스크립트
하이퍼스케일+ 자체 분산 한도 서비스 (Lyft ratelimit 등)핵심 부품 직접 빌드 (Doorman, Stripe Async)

실제 설정은 이렇게 짧다

Nginx (누출 버킷, IP당 10 req/s, 버스트 20)

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

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

Express (Node.js, 토큰 버킷, 사용자당 100 req/min)

import { RateLimiterRedis } from 'rate-limiter-flexible';
import Redis from 'ioredis';

const limiter = new RateLimiterRedis({
  storeClient: new Redis(),
  keyPrefix: 'api',
  points: 100,        // 한도
  duration: 60,       // 60초당
});

app.use(async (req, res, next) => {
  try {
    await limiter.consume(req.user.id);
    next();
  } catch {
    res.status(429).send('Too Many Requests');
  }
});

Cloudflare (대시보드 클릭만)

  • Rules → Rate limiting rules
  • "같은 IP에서 1분에 100건 넘으면 차단" 입력 → 저장 끝
  • 알고리즘은 내부적으로 이동 윈도우 카운터(2,500개 데이터센터에 분산)

그럼 이론은 어디서 쓰나?

  • 도구 비교/선택 — "Nginx로 충분한가, Kong까지 가야 하나?"
  • 설정 의미 이해 — Nginx burst=20이 누출 버킷의 큐 크기란 걸 알아야 정확히 튜닝
  • 장애 디버깅 — "윈도우 경계마다 모니터링이 튐" → 고정 윈도우 경계 문제로 추정 가능
  • 특수 요건 직접 구현 — "사용자 티어×엔드포인트×시간대별 한도" 같은 비표준 요건. 1~5%의 경우.
  • 인프라 팀과 소통 — 같은 언어로 이야기 가능

💡 책이 가르치는 원리 ↔ 실무가 다루는 도구. 둘은 같은 것을 다른 추상화 수준에서 본다. 원리를 모르면 도구를 영업 자료대로 믿고, 도구를 모르면 원리만 알고 못 만든다. 양쪽을 오갈 수 있어야 진짜 시스템을 책임질 수 있음.

3단계: 상세 설계

처리율 제한 규칙

규칙은 보통 설정 파일(YAML/JSON)로 디스크에 저장하고 메모리에 캐시. 예시:

domain: messaging
descriptors:
  - key: message_type
    value: marketing
    rate_limit:
      unit: day
      requests_per_unit: 5

처리율 한도 초과 트래픽 처리

거부할 때의 선택지:

  • HTTP 429 Too Many Requests를 즉시 반환하거나
  • 중요 요청은 큐에 보관 후 나중에 처리(소프트 한도) 가능

응답에 다음 헤더를 함께 반환하면 클라이언트가 적응할 수 있다.

  • X-Ratelimit-Limit — 윈도우당 최대 요청 수
  • X-Ratelimit-Remaining — 윈도우 안 남은 요청 수
  • X-Ratelimit-Reset — 한도가 리셋되는 시각
  • Retry-After — 클라이언트가 재시도까지 기다려야 할 시간

전체 아키텍처

Redis가 표준 저장소인 이유: 초고속, 만료 시간(TTL) 지원, 원자적 연산 (INCR, EXPIRE). 카운터를 디스크 DB에 두면 매 요청마다 디스크 I/O가 들어가 너무 느리다.

분산 환경의 두 가지 도전

경쟁 조건(Race Condition)

"GET counter → +1 → SET counter" 패턴은 두 요청이 동시에 들어오면 둘 다 같은 값을 읽고 같은 값을 쓰게 돼 카운트 누락 발생. 해법:

  • Lua 스크립트 — Redis는 Lua 스크립트를 원자적으로 실행. 여러 명령을 한 단위로 묶음.
  • 정렬 집합(Sorted Set) — 이동 윈도우 로그 구현 시 자연스러운 원자성 제공.
  • 락(Lock) — 가능하지만 성능 저하 큼. 마지막 수단.

동기화(Synchronization)

처리율 제한 서버가 1대뿐이면 카운터를 메모리에 두면 끝. 그런데 트래픽이 커지면 처리율 제한 서버 자체도 여러 대로 늘려야 한다. 이때 문제:

서버1이 사용자 A의 카운트를 4로 알고 있어도, 다음 요청이 서버2로 가면 서버2는 그 사실을 모른다. 한도가 사용자당 5건인데 서버 N대 있으면 최대 5N건까지 통과한다. 해법:

해법 1: 고정 세션 (Sticky Session)

로드밸런서가 "사용자 A는 무조건 서버1로" 같은 규칙을 적용. 카운터는 각 서버 메모리에 둠.

  • 장점: 단순. Redis 없어도 됨.
  • 단점: 서버1이 죽으면 사용자 A의 카운트도 같이 사라짐 (결함 감내 약함). 트래픽 분포 불균형 발생 가능 (인기 사용자가 한 서버에 몰림).

해법 2: 중앙 저장소 (Redis) ⭐ 표준

모든 처리율 제한 서버가 공통의 한 곳에 카운터를 저장. 어느 서버로 요청이 가도 같은 데이터를 읽고 쓴다.

잠깐, Redis가 뭔데?

메모리(RAM)에 데이터를 저장하는 데이터베이스. 디스크 기반인 MySQL/PostgreSQL과 달리 모든 데이터를 RAM에 둬서 매우 빠르다.

MySQL/PostgreSQLRedis
저장 위치디스크메모리
속도~수 ms~수십 µs (1000배)
데이터 모델테이블 (행/열)키-값 (단순)
영속성강함약함 (서버 죽으면 일부 손실 가능)

비유: MySQL = 창고(안전, 크지만 느림), Redis = 책상 위 메모지(빠르지만 작고 휘발성). 처리율 제한처럼 매 요청마다 카운터 한 번 증가 같은 작업엔 메모지가 딱 맞음.

왜 Redis가 처리율 제한의 표준인가

  1. 속도 — 디스크 DB로 카운터 관리하면 매 요청에 5ms × 초당 1만 요청 = 디스크 폭주. Redis는 1/1000 시간이라 사용자 응답 지연에 거의 영향 없음.
  2. 원자적 연산INCR 한 줄이 "읽고 +1 해서 쓰기"를 한 번에 처리. 두 요청이 동시에 들어와도 카운트 누락 없음 (race condition 해결).
    # 사용자 1234의 분당 카운터를 1 증가
    INCR ratelimit:user:1234:202604261430
    # 결과로 새 카운트가 반환됨, 5보다 크면 거부
  3. 자동 만료(TTL) — 윈도우가 끝나면 키가 자동 삭제. 청소 코드 불필요.
    INCR ratelimit:user:1234:14:30
    EXPIRE ratelimit:user:1234:14:30 60   # 60초 뒤 자동 삭제
  4. 풍부한 자료구조 — 알고리즘별로 딱 맞는 자료구조 제공:
    • 고정 윈도우 → 단순 키 + INCR
    • 이동 윈도우 로그 → Sorted Set (시간순 정렬)
    • 토큰 버킷 → Lua 스크립트로 복잡한 원자 연산
  5. 적당한 영속성 손실 허용 — 서버 죽었을 때 처리율 카운터가 잠깐 사라져도 시스템 안 망함 (몇 초간 한도 초과 통과될 뿐). 반대로 결제·주문 데이터는 Redis에 두면 안 됨.

실제 코드 예시 (고정 윈도우, Node.js)

import Redis from 'ioredis';
const redis = new Redis();  // 보통 매니지드 (ElastiCache 등)

async function checkRateLimit(userId: string): Promise<boolean> {
  const window = Math.floor(Date.now() / 60000);  // 1분 윈도우
  const key = `ratelimit:${userId}:${window}`;

  const count = await redis.incr(key);     // ① 원자적 증가
  if (count === 1) {
    await redis.expire(key, 60);            // ② 첫 요청에만 TTL 설정
  }

  return count <= 100;                      // ③ 한도 100/분
}

서버가 100대든 1000대든 똑같이 동작. 왜냐하면 모든 서버가 같은 Redis를 보니까.

Redis도 죽으면?

이건 분산 환경의 항상 따라오는 질문이다. 보통:

  • 매니지드 Redis(AWS ElastiCache, GCP Memorystore) + 복제본 → 자동 페일오버
  • fail-open 정책 — Redis 죽었을 때 일단 모두 통과시킴 (가용성 우선). 잠시 한도 초과해도 시스템은 살아있음.
  • 로컬 캐시 폴백 — Redis 응답 안 오면 각 서버 메모리 카운터로 임시 동작

4단계: 마무리 — 추가 논의

  • 하드 vs 소프트 처리율 제한
    • 하드 — 임계치 초과 시 무조건 거부
    • 소프트 — 짧은 시간 임계치 초과 허용 (대신 평균은 지킴)
  • OSI 다른 계층의 처리율 제한 — IPTables(L3 IP 기반), 웹 애플리케이션(L7 사용자/엔드포인트 기반) 등 계층별로 적용 가능.
  • 클라이언트 측 회피 방법
    • 캐시로 API 호출 자체를 줄임
    • 임계치를 이해하고 짧은 시간 안에 너무 많이 보내지 않음
    • 예외/에러를 우아하게 처리(지수 백오프)
    • 재시도 로직에 충분한 backoff와 jitter 추가
  • 결함 감내 — 처리율 제한 장치가 죽었을 때 fail-open (모두 통과) vs fail-closed(모두 차단) 정책 결정.