Rate Limits
logi는 2중 레이어로 rate limit을 적용합니다:
- Cloudflare 엣지 — IP 기반, 앱이 받기 전 차단 (배포 환경에서 활성)
- Rails 서버 (rack-attack) — 정교한 키(client_id/user_id/IP) 기반, JSON 429 반환
Rails 레이어는 Rails.cache(production 환경에서는 SolidCache)를 store로 사용하므로 별도 Redis 인프라 없이 동작합니다.
엔드포인트별 한도
| 엔드포인트 | 한도 | 키 | 레이어 | 비고 |
|---|---|---|---|---|
POST /session (로그인) | 10 / 3min | IP | Rails | brute-force 방어 |
POST /oauth/authorize | 30 / min | IP | Rails + Cloudflare | |
POST /oauth/token | 20 / min | client_id | Rails | client_id 누락 시 IP fallback |
POST /api/v1/me/otp/* | 10 / min | session/user | Rails | SMS 비용 폭증 방어 |
POST /api/v1/me/passkeys/* | 20 / min | session/user | Rails | |
POST /api/v1/devices | 10 / min | IP | Rails | device bootstrap brute-force 방어 |
POST /api/v1/users/:sub/identity_verified | 100 / hour | client_id (Bearer hash) | Rails | reporter 앱당 |
기타 /api/* | 60 / min | IP | Rails | 글로벌 fallback |
정적/Health (/up, /healthz) | 무제한 | — | safelist |
초과 시 응답
http
HTTP/2 429 Too Many Requests
Content-Type: application/json
{"error":"rate_limited"}일부 엔드포인트는 Retry-After 헤더를 포함할 수 있습니다.
클라이언트 권장사항
- 지수 백오프: 429 수신 시
Retry-After헤더 우선, 없으면 1 → 2 → 4 → 8초로 간격 늘려 재시도 - 정상 흐름에서는 거의 안 부딪힘 — 지속적 429는 구현 오류(리트라이 루프) 의심
- Burst 예상되는 워크로드는 사전에 상담 (Phase 2에서 앱별 커스텀 한도 지원 예정)
구현 세부
서버측 구현은 server/config/initializers/rack_attack.rb에 있습니다. 한도가 변경되면 이 파일과 본 문서를 함께 업데이트해야 합니다.
테스트 환경에서는 Rails.cache가 null_store라 throttle이 비활성화됩니다. rate limit 자체를 테스트하려면 spec/requests/rate_limit_spec.rb처럼 MemoryStore로 swap하세요.