Server-side 캐시는 오래전부터 작동했어 - 같은 date/time/lat/lon에 대한 결정론적 차트 계산이 캐시에서 반환되고, 다시 계산하지 않아. 하지만 클라이언트는 이를 보지 못했어. 대시보드의 “cache hit %” 열은 – 를 표시했는데, 백엔드가 cache-outcome을 응답이 아니라 내부 메트릭에 기록했기 때문이야.
이제 모든 response는 세 가지 헤더 중 하나를 포함해:
X-Cache: HIT # обслужено з кешуX-Cache: MISS # обчислено з нуля, результат збереженоX-Cache: BYPASS # не кешується за дизайном작은 변화야 - 헤더 하나와 api_request_log에 한 컬럼이 추가됐지만, 이전에 눈에 보이지 않던 다양한 최적화 가능성을 열어줘.
어떻게 작동하는지: 예시
섹션 제목: “어떻게 작동하는지: 예시”curl -I -X POST https://api.astroway.info/v1/chart \ -H "X-Api-Key: aw_live_..." \ -H "Content-Type: application/json" \ -d '{ "date": "1990-05-15", "time": "14:30:00", "timezoneOffset": 3, "latitude": 50.45, "longitude": 30.52 }' | grep -i x-cache# X-Cache: MISS같은 요청 본문으로 두 번째 호출을 수행해:
# X-Cache: HIT/v1/transits/now 같은 엔드포인트(동적 시간)의 경우 - X-Cache: BYPASS야, 결과가 현재 Date.now()에 의존하니 캐시할 의미가 없어.
어떤 엔드포인트가 무엇을 반환하는지
섹션 제목: “어떤 엔드포인트가 무엇을 반환하는지”모든 엔드포인트가 캐시되는 건 아니야 - 의도된 설계야. 구분:
- 결정론적 차트 계산 (
/v1/chart,/v1/houses,/v1/aspects,/v1/synastry,/v1/dasha/*,/v1/vargas/*) - 완전히 캐시돼. 일반적인 MISS → HIT 패턴이야. - 시간 의존 (
/v1/transits/now,/v1/horoscope/today,/v1/moon/phase-now) -BYPASS. 캐시는 분이 끝날 때까지만 정확할 수 있어서, 아예 캐시하지 않는 게 더 쉬워. - AI 생성 콘텐츠 (
/v1/horoscope/personal,/v1/interpret/*):BYPASS. 같은 프롬프트라도 LLM 응답은 결정론적이지 않으니, 캐시 = 무작위성을 고정하는 것과 같아. - 렌더 엔드포인트 (
/v1/render/*): 일부는MISS/HIT, 큰 payload(예: 500 포인트의 eclipse-path)를 받는 경우는BYPASS.
특정 엔드포인트에 대한 마커는 response에서 바로 볼 수 있어 - 문서를 읽지 않아도 캐시되는지 알 수 있지.
가격 영향: 캐시된 요청도 여전히 비용이 들어
섹션 제목: “가격 영향: 캐시된 요청도 여전히 비용이 들어”이게 가장 중요한 포인트야 - 캐시된 요청도 같은 tier에서 credits를 차감해, MISS와 동일하게. 이유는:
- 우리 가격은 엔드포인트의 비즈니스 가치에 맞춰 조정돼, CPU 비용이 아니라.
/v1/chart는 차트를 다시 계산하든 캐시에서 가져오든 같은 비용이야 - 클라이언트는 같은 차트를 받으니까. - 투명성. 한 user-base는 MISS에, 다른 쪽은 HIT에 비용을 내는 상황을 원하지 않아 (이론적으로 “캐시가 운이 좋았다”는 이유). 가격이 예측 가능해야 해.
- 캐시 인프라: 이것은 부가가치가 아니라 인프라야. 우리는 tier에 포함해서 보조금처럼 제공하고 있어.
하지만 이게 X-Cache가 가격 컨텍스트에서 무의미하다는 뜻은 아니야 - 클라이언트에게 아키텍처적 가능성을 보여주는 역할을 해 (다음 섹션 참고).
클라이언트 측에서 어떻게 할까
섹션 제목: “클라이언트 측에서 어떻게 할까”네 가지 실용적인 패턴:
1. MISS에 대한 클라이언트 측 캐시 레이어
섹션 제목: “1. MISS에 대한 클라이언트 측 캐시 레이어”요청에 X-Cache: MISS가 표시되고, 같은 시나리오(예: 같은 사람의 출생 차트)에서 반복될 수 있다면 Redis/Memcached/IndexedDB에 로컬로 캐시해. 서버 측 캐시는 TTL과 evict 정책이 있어서, 제어된 TTL을 가진 클라이언트 레이어가 불필요한 credit 차감을 피할 수 있어.
import { Astroway } from "@astroway/sdk";
const cache = new Redis();const client = new Astroway({ apiKey: process.env.ASTROWAY_KEY, // optional: hook on response headers onResponse(req, res) { const cacheStatus = res.headers["x-cache"]; metrics.increment(`astroway.cache.${cacheStatus.toLowerCase()}`); },});
async function getChart(input: ChartInput) { const cacheKey = `chart:${hashChart(input)}`; const cached = await cache.get(cacheKey); if (cached) return JSON.parse(cached);
const chart = await client.chart.create(input); await cache.set(cacheKey, JSON.stringify(chart), "EX", 86400); return chart;}2. 백엔드에서 배치 + dedupe
섹션 제목: “2. 백엔드에서 배치 + dedupe”서비스가 동일한 birth-data에 대한 대량 요청을 받는 경우(예: 동료들이 같은 데모 데이터를 테스트하는 온보딩 캠페인), 프라미스 기반 dedupe를 사용해:
const inFlight = new Map<string, Promise<Chart>>();
function getChart(input: ChartInput) { const key = hashChart(input); if (inFlight.has(key)) return inFlight.get(key)!;
const promise = client.chart.create(input); inFlight.set(key, promise); promise.finally(() => inFlight.delete(key)); return promise;}이렇게 하면 credits를 절감하진 못해(각 호출마다 여전히 차감됨), 하지만 동시성 급증 시 병목을 줄여줘.
3. 중요한 경로 사전 워밍
섹션 제목: “3. 중요한 경로 사전 워밍”제품에 의식 차트(예: 인기 있는 daily-horoscope 별자리)가 있다면, 스케줄된 cron으로 미리 워밍해. 첫 호출은 MISS, evict 되기 전까지는 모두 HIT. 사용자는 서브초 응답을 받게 돼.
4. 대시보드 인사이트: 어디서 과다 지불하고 있나요
섹션 제목: “4. 대시보드 인사이트: 어디서 과다 지불하고 있나요”새로운 cache hit % 컬럼이 /dashboard/usage에서 엔드포인트별로 보여줘:
- HIT% = 90+ - 엔드포인트가 잘 캐시돼, 같은 차트가 여러 번 전송될 가능성이 높아. 클라이언트 측 dedupe(#2)를 고려해.
- HIT% = 0 그리고 BYPASS: 엔드포인트가 설계상 캐시되지 않아 (transits/now, AI). 정상적인 거야.
- HIT% = 50% 그리고 MISS: 절반은 고유 파라미터, 절반은 반복 호출. 클라이언트 측 캐시를 적용할 가치가 있어.
- HIT% 낮고 엔드포인트가 결정론적: 의심스러워. 클라이언트가 payload에 무작위 필드(timestamp, request-id 등)를 추가해 캐시 키를 깨뜨리지 않는지 확인해.
기술 구현: 궁금한 사람들을 위해
섹션 제목: “기술 구현: 궁금한 사람들을 위해”Tracking은 api_request_log.cache_status에 한 컬럼을 추가해(enum: MISS|HIT|BYPASS, migration 030). 이 컬럼은 캐시 조회 결정을 내리는 동일한 핸들러에서 채워지니, DB에 추가 쿼리를 하지 않아.
GET /v1/me/usage/endpoints는 이제 각 엔드포인트에 대해 null 대신 실제 cache_hit_pct를 반환해. SDK 메서드 client.me.usage.endpoints()도 자동으로 해당 필드를 받게 되고(다음 codegen 릴리즈에 타입 포함).
다음은 무엇을 할까
섹션 제목: “다음은 무엇을 할까”Cache instrumentation은 “no caching”과 “full edge cache” 사이의 중간 최적화 레벨을 열어줘. 로드맵에 다음과 같은 개선이 예정돼:
- 캐시된 엔드포인트에 실제 TTL을 담은
Cache-Control응답 헤더 - 클라이언트 측 CDN/프록시 캐시를 가능하게 해. If-None-MatchETags: 전체 payload 없이 반복적인 MISS 검증.- 대시보드에 사용자별 캐시 통계와 invalidate 기능 제공(예: birth-time 수정 후 특정 차트를 강제로 재계산).
문서 - /docs/api/ → Performance & Caching. 구체적인 X-Cache 레퍼런스는 각 엔드포인트의 headers 섹션에 있어.
Solar Fire에 사용된 것과 동일한 Swiss Ephemeris - 단 4줄의 코드로.
카드 없이 무료 키. 첫 결제 전까지 월 5,000 API 호출.