블로그(former-force)에서 Notion API를 실행, rate_limited / HTTP 429가 난 이유를, 지금 레포에 있는 캐시·동시성·웹훅 기준으로 정리한 문서다.
관련: Astro 블로그 · Astro 블로그 · Notion CMS 방향성 · 제작 과정 · 결정 로그 (종료) · 레포 cpt4567/former-force
Notion Public API는 연결(connection)당 평균 초당 약 3요청이다. 넘으면 code: rate_limited, HTTP 429, 헤더 Retry-After(초). 워크스페이스 플랜에 묶인 한도도 따로 있고, 그때도 같은 형태로 막힌다. 529(service_overload)도 잠깐 쉬었다가 같은 방식으로 재시도한다.
공식: Request limits
블로그 쪽이 특히 잘 터지는 지점은 본문 블록을 페이지마다 blocks.children.list로 재귀 fetch할 때다. Published가 200장 가까이 되면, 목록 쿼리만으로도 페이지네이션이 여러 번이고, 본문까지 붙이면 요청 수가 급격히 불어난다. NOTION_CACHE=0으로 디스크 캐시를 끄거나 .cache/notion을 지운 채 astro dev / astro build를 연속으로 돌리면 한도가 바로 보인다.
레포는 이 에러를 감싸서 이렇게 다시 던진다 (src/lib/notion.ts):
Notion API rate limited while …. Wait a minute, or reuse
.cache/notion(unsetNOTION_CACHE=0).
| 경로 | 하는 일 | Notion 호출량 |
|---|---|---|
목록 (getPublishedPostSummaries) |
Published 쿼리만 · bodyHtml 비움 |
작음 (dataSources.query 페이지네이션) |
전체 (getPublishedPosts) |
목록 + 각 페이지 블록→HTML | 큼 (페이지×블록 children) |
단건 (getPublishedPostBySlug) |
풀 캐시 히트 우선 · 없으면 단건 본문 | 중간 |
웹훅 POST /api/notion/webhook |
캐시 삭제 + 재배포 트리거 | 이벤트당 최소 · 본문 재fetch는 빌드 때 |
토큰(NOTION_TOKEN)은 서버/빌드 env만. PUBLIC_*로 올리지 않는다.
| 항목 | 값 |
|---|---|
| 디렉터리 | .cache/notion/ (gitignore) |
| 스키마 버전 | CACHE_SCHEMA = 3 (HTML 형태 바뀌면 버프 → 옛 파일 무효) |
| 파일 예 | posts-full.json · posts-summaries.json · tag-options.json |
| dev TTL | 30분 |
| build TTL | 무한 (빌드 한 번 안에서 재사용) |
| 무효화 | NOTION_CACHE=0 또는 false · 폴더 삭제 · 웹훅 clearNotionCache() |
| 저장 내용 | 포스트/태그 페이로드만 · 시크릿 없음 |
흐름은 단순하다. 같은 키로 이미 뜨는 요청이 있으면 inflight로 합치고, 메모리 → 디스크 순으로 보고, 둘 다 없거나 TTL이 지났을 때만 Notion을 친다.
NOTION_MAX_CONCURRENT = 3 으로 Notion 호출을 큐잉한다 (withNotionLimit). 공식 평균 초당 3요청과 맞춰, 블록 재귀가 한꺼번에 폭주하지 않게 막는다.