FastAPI — 타입이 곧 계약이다 · 요청 제한의 경계 시각을 검증한다 · 실습
요청 제한의 경계 시각을 검증한다
목표
가상 시계로 슬라이딩 윈도와 Retry-After를 검사하고 사용자별 한도를 분리합니다.
왜 중요한가
트래픽이 늘자 서버는 모든 요청을 같은 목록에 기록하기 시작했다. 한 사용자의 연속 요청이 다른 사용자의 정상 요청까지 막았다. 창의 마지막 시각에서 항목을 지우는 비교 연산도 틀려 제한이 1초 더 유지됐다. 실제로 수십 초 기다리는 테스트는 이런 경계를 느리고 불안정하게 만든다.
단계
1. /root/work/fa-rate-window-lab/service.py에서 validate_limit(limit, window)는 bool 제외 양의 int limit와 양의 유한 int/float window만 허용해 (limit, float(window))를 반환합니다. 나머지는 ValueError입니다.
처음 한 번 준비하세요. 기존 파일은 덮어쓰지 않습니다.
mkdir -p /root/work/fa-rate-window-labtest -e /root/work/fa-rate-window-lab/service.py || cp /opt/fixtures/ten_labs/fa-rate-window-lab/service.py /root/work/fa-rate-window-lab/service.pycd /root/work/fa-rate-window-lab2. /root/work/fa-rate-window-lab/service.py에서 active(history, now, window)는 now-window보다 큰 시각만 원래 순서의 새 리스트로 반환합니다. history는 정렬된 비감소 시각입니다.
3. /root/work/fa-rate-window-lab/service.py에서 retry_after(history, now, window)는 이미 정리된 비어 있지 않은 history의 첫 시각+window-now를 ceil한 값과 0 중 큰 정수입니다. 빈 리스트는 0입니다.
4. /root/work/fa-rate-window-lab/service.py에서 history_for(state, key)는 없는 키면 빈 리스트, 있으면 그 기록의 사본을 반환합니다. 조회만으로 state를 수정하지 않습니다.
5. /root/work/fa-rate-window-lab/service.py에서 admit(state, key, now, limit, window)는 설정 검증 후 해당 키의 만료 기록을 정리합니다. 여유가 있으면 now를 추가하고 (True,0), 꽉 찼으면 추가하지 않고 (False,retry_after)를 반환합니다.
6. /root/work/fa-rate-window-lab/service.py에서 client_key(value)는 1~40자 ASCII 영문·숫자·하이픈 문자열을 그대로 반환하고 나머지는 ValueError입니다.
7. /root/work/fa-rate-window-lab/service.py에서 limited_response(wait)는 상태 429, 본문 {error:'rate_limited'}, Retry-After 헤더는 wait를 문자열로 만든 JSONResponse입니다.
8. /root/work/fa-rate-window-lab/service.py에서 create_app(clock, limit=2, window=10)는 GET /work에서 X-Client-ID를 검사해 잘못된 키는 400 {error:'invalid_client'}, 허용은 200 {ok:True}, 초과는 limited_response입니다. state는 앱별로 분리합니다.
참고
- 인터넷과 패키지 설치 없이 기존 lab-dev 환경에서 수행합니다.
- 각 단계는 45초 채점 예산 안에서 실행됩니다. 실제 sleep이나 네트워크 호출을 추가하지 마세요.
- 채점은 제출 모듈을 새로 불러오고 독립 입력과 임시 DB로 검사합니다. 예상값을 상수로 반환하는 대신 계약을 구현하세요.
- [FastAPI 공식 문서](https://fastapi.tiangolo.com/) · [pytest 공식 문서](https://docs.pytest.org/en/stable/) · [Python sqlite3](https://docs.python.org/3/library/sqlite3.html)
- 한계: 프로세스 메모리에 있는 단일 워커용 예제다. 여러 파드가 공유하는 전역 한도나 악의적인 클라이언트의 신원을 보장하지 않는다. X-Client-ID는 테스트용 키이므로 운영에서는 인증된 주체에서 키를 얻어야 한다. 지속적인 시계 역행은 단조 시계 사용으로 피해야 하며 이 실습의 clock은 비감소한다.
8단계
- 설정을 검증한다
- 창의 왼쪽 경계를 제외한다
- 대기 시간을 올림한다
- 키마다 기록을 나눈다
- 허용 요청만 기록한다
- 클라이언트 키를 검증한다
- 거절 응답을 만든다
- 가상 시간으로 요청 흐름을 끝낸다