
블로그 이탈률이 높다는 걸 알면서도 뾰족한 해결책을 찾지 못하고 계신 분들, 저도 그랬습니다. 그러다 "검색 쿼리에 맞춰 포스트 도입부를 실시간으로 바꿔주면 어떨까?"라는 아이디어가 떠올랐고, Cloudflare Workers와 AI API를 조합해 직접 구현해봤습니다. 결과는 기대 반, 현실 반이었습니다.
AI Gateway와 KV 캐싱으로 응답 속도 잡기
처음 Workers에서 OpenAI API를 직접 호출했을 때 가장 먼저 맞닥뜨린 문제는 콜드 스타트가 아니었습니다. Workers 자체의 구동 시간은 체감상 거의 0에 가까웠습니다. 진짜 병목은 외부 AI API 호출이었는데, 평균 800ms에서 길게는 1.5초까지 소요됐습니다. 방문자 입장에서 1.5초면 이미 뒤로 가기를 누르고도 남을 시간입니다.
이를 해결하기 위해 Cloudflare AI Gateway를 앞단에 세웠습니다. AI Gateway란 외부 AI API 호출을 프록시하면서 캐싱, 로깅, 레이트 리밋(Rate Limiting)을 일괄 처리해주는 Cloudflare의 관리형 미들웨어 레이어입니다. 여기서 레이트 리밋이란 특정 시간 동안 허용되는 API 호출 횟수를 제한하는 기능으로, 갑작스러운 트래픽 스파이크 때 과금이 폭발하는 사태를 막아줍니다.
AI Gateway의 캐싱만 켜는 것으로는 부족했습니다. 같은 의미의 검색 쿼리라도 "클라우드플레어"와 "Cloudflare"처럼 표기가 다르면 캐시가 따로 잡히기 때문입니다. 그래서 저는 입력 쿼리를 소문자로 통일하고 조사·불용어를 제거하는 정규화(Normalization) 단계를 Workers 코드 안에 추가했습니다. 정규화란 서로 다르게 표현된 동일한 의미의 입력값을 하나의 표준 형태로 통일하는 처리 과정입니다. 이 두 가지를 병행하고 나서 평균 응답 시간이 300ms 아래로 내려왔습니다. 솔직히 이건 예상 밖의 쾌적한 결과였습니다.
두 번째 캐시 레이어로는 Cloudflare KV를 사용했습니다. KV는 키-값(Key-Value) 구조의 엣지 스토리지로, 전 세계 엣지 서버에 데이터를 분산 저장해 빠르게 읽어올 수 있는 저장소입니다. 다만 KV는 Eventually Consistent 모델을 따릅니다. 이는 데이터를 업데이트해도 전 세계 모든 엣지 노드에 즉시 반영되는 것이 아니라, 최대 60초 정도의 전파 지연이 발생할 수 있다는 의미입니다. 즉각적인 캐시 무효화가 필요한 상황이라면 KV 대신 Durable Objects를 써야 한다는 것도 이 과정에서 직접 배웠습니다.
TTL 설계에서 저는 콘텐츠 성격에 따라 아래와 같이 만료 시간을 나눴습니다.
- 뉴스성 콘텐츠: TTL 1시간
- 에버그린(Evergreen) 콘텐츠: TTL 24시간
- SEO 목적 메타 콘텐츠: TTL 7일
에버그린 콘텐츠란 시간이 지나도 정보 가치가 유지되는 콘텐츠를 뜻합니다. 이런 글에 짧은 TTL을 설정하면 AI API를 쓸데없이 반복 호출하게 되고, 그게 곧 비용으로 직결됩니다. Cloudflare의 공식 문서에 따르면 Workers 무료 티어는 일 10만 요청을 제공합니다(출처: Cloudflare Workers AI 공식 문서). 소규모 블로그라면 이 범위 안에서 충분히 돌아가지만, AI API 호출 비용은 별개 문제이므로 캐시 히트율을 높이는 설계가 핵심입니다.
SEO 리스크: 캐싱이 해결책이자 한계선
Workers에서 AI가 생성한 HTML을 바로 반환하면 Googlebot은 이를 정적 페이지와 동일하게 처리합니다. JavaScript 렌더링을 기다릴 필요가 없는 서버사이드 렌더링(SSR) 방식이기 때문입니다. SSR이란 서버 측에서 HTML을 완전히 완성한 뒤 브라우저에 전달하는 방식으로, 검색 엔진 크롤러가 별도의 스크립트 실행 없이 콘텐츠를 바로 읽을 수 있습니다. 구글은 공식 가이드에서 JavaScript 의존도가 낮은 SSR 페이지가 크롤링과 인덱싱에 유리하다고 밝히고 있습니다(출처: Google Search Central — Dynamic Rendering).
그런데 여기서 구조적인 딜레마가 생깁니다. "AI 개인화 콘텐츠"의 매력은 방문자마다 다른 내용을 보여준다는 점인데, 바로 그 특성이 SEO 관점에서는 위험 요소가 됩니다. 구글의 스팸 정책은 클로킹(Cloaking)을 명시적으로 금지하고 있습니다. 클로킹이란 Googlebot과 일반 사용자에게 서로 다른 콘텐츠를 보여주는 행위를 말합니다. 캐싱으로 크롤러에게는 고정된 콘텐츠를 제공하더라도, 구글의 품질 평가 알고리즘은 "같은 URL에서 재크롤링 때마다 내용이 달라지는 페이지"에 대해 신뢰도를 낮게 평가할 가능성이 있습니다. 제가 직접 써봤는데, 이 부분이 가장 찜찜하게 남는 지점이었습니다.
Hono.js를 라우팅 레이어로 활용하면 미들웨어 체인에서 크롤러 감지와 캐시 헤더 제어를 구조적으로 분리할 수 있습니다. 제 경험상 이건 코드 가독성을 높이는 데도 꽤 유효했습니다. 하지만 크롤러에게 다른 캐시 키를 쓰는 순간 클로킹 경계선에 아슬아슬하게 걸리게 됩니다. 기술적으로 가능하더라도 정책적으로 회색지대라는 점을 반드시 감안해야 합니다.
비용 예측 문제도 빼놓을 수 없습니다. Workers와 AI API 모두 요청 단위 과금이라, 트래픽 스파이크가 발생하면 예상치 못한 청구서를 받을 수 있습니다. Workers의 CPU 시간 제한도 변수입니다. 무료 플랜은 요청당 10ms, 유료 플랜도 최대 30초로 제한되는데, AI 응답을 스트리밍으로 처리하다 보면 이 제한이 생각보다 빨리 걸립니다. 프로덕션에 올리기 전에 부하 테스트와 비용 시뮬레이션을 반드시 거쳐야 한다고 느꼈습니다.
"실시간 AI 개인화가 이탈률을 낮춘다"는 명제를 맹신하는 분들도 있는데, 저는 이 부분에 좀 더 신중한 입장입니다. 인과관계는 아직 대규모 A/B 테스트로 검증되지 않았습니다. 기술적으로 작동한다는 것과 실제로 광고 수익이 오른다는 것은 전혀 다른 이야기입니다.
이 아키텍처는 분명 가능성이 있지만, 지금 당장 프로덕션 블로그 전체에 적용하기에는 리스크가 큽니다. 저라면 전체 페이지가 아닌 트래픽이 낮은 2~3개 포스트에서 파일럿 테스트를 먼저 돌리고, 이탈률과 크롤링 빈도 변화를 최소 3개월 이상 관찰한 뒤 확장 여부를 판단하겠습니다. 기술 실험은 재미있었지만, 검증 없는 전면 적용은 블로그 도메인 신뢰도에 돌이키기 어려운 흠집을 낼 수 있습니다.
참고:
[1] Cloudflare Workers AI — Getting Started https://developers.cloudflare.com/workers-ai/get-started/
[2] Cloudflare AI Gateway Documentation https://developers.cloudflare.com/ai-gateway/
[3] Cloudflare KV — How KV Works https://developers.cloudflare.com/kv/concepts/how-kv-works/
[4] Google Search Central — Dynamic Rendering https://developers.google.com/search/docs/crawling-indexing/javascript/dynamic-rendering
[5] Hono.js Documentation — Cloudflare Workers https://hono.dev/getting-started/cloudflare-workers
[6] Google Search Central — Cloaking Policy https://developers.google.com/search/docs/essentials/spam-policies#cloaking