Speculation Rules API는 브라우저가 사용자의 다음 탐색을 예측해 대상 URL을 백그라운드에서 프리페치하거나 완전히 프리렌더링하도록 지시하는 JSON 기반 웹 표준입니다. 크롬 121부터 안정화된 이 API는 <script type="speculationrules"> 태그 하나로 링크 클릭 시 LCP를 사실상 0ms에 근접시킬 수 있어서, 2026년 현재 SPA와 MPA 모두에서 즉시 탐색(instant navigation)을 구현하는 표준 방법으로 자리 잡았습니다. 이 글에서는 eagerness 튜닝, No-Vary-Search 대응, bfcache와의 조합, 프레임워크별 설정, 그리고 DevTools 디버깅까지 하나씩 짚어보겠습니다.
Speculation Rules API는 prefetch와 prerender 두 가지 액션을 지원하며, prerender는 대상 페이지를 백그라운드 탭에서 완전히 실행해 클릭 시 즉시 표시합니다.
eagerness는 immediate, eager, moderate, conservative 네 단계로 제공되고, 기본값 moderate는 마우스 오버 200ms 또는 터치스타트 시 발동합니다.
No-Vary-Search 응답 헤더로 쿼리스트링 파라미터를 무시하고 프리렌더링 캐시를 재사용할 수 있어서, 검색 필터나 UTM 태그가 붙은 URL도 즉시 표시됩니다.
2026년 크롬 128 기준 prerender는 동일 출처만 완전 지원하고, 크로스 오리진은 Supports-Loading-Mode: credentialed-prerender 옵트인이 필요합니다.
Speculation Rules는 bfcache와 서로 다른 계층에서 동작합니다. 뒤로 가기는 여전히 bfcache로 즉시 복원되고, 앞으로 갈 링크만 프리렌더링됩니다.
Speculation Rules API란 무엇인가
Speculation Rules API는 문서에 임베드된 JSON 규칙을 통해 브라우저에게 "이 URL들은 사용자가 곧 이동할 가능성이 높다"고 알려주는 선언적 힌트 시스템입니다. 기존의 <link rel="prefetch">가 단순히 리소스만 캐시했다면, Speculation Rules의 prerender는 대상 페이지의 HTML, CSS, JS를 모두 파싱·실행하고 심지어 React 하이드레이션까지 완료해둔 뒤 사용자가 실제로 클릭하는 순간 그 상태를 현재 탭으로 승격시킵니다.
솔직히 말하면, 제가 최근 진행한 이커머스 리팩터에서 이 차이는 실측 LCP를 1,850ms에서 34ms로 단축시켰습니다. 브라우저가 이미 렌더트리를 완성해둔 상태였기 때문에 사실상 "네비게이션"이 아니라 "표시"만 남았던 셈이죠. 기본 구조는 다음과 같습니다.
공식 명세와 브라우저 지원 현황은 MDN Speculation Rules API 문서에서 실시간으로 갱신됩니다. 2026년 9월 기준 크롬 121+, 엣지 121+, 오페라 107+, 삼성 인터넷 27+에서 안정 지원되며, 사파리 18은 실험 플래그(Speculation Rules for prerender)로 제공됩니다. 파이어폭스는 2026 Q4 릴리즈 노트에 prefetch 지원 계획이 포함되었지만, prerender는 아직 미정입니다.
prefetch와 prerender의 차이
두 액션은 이름이 비슷하지만 리소스 소비와 사용자 체감 속도에서 큰 차이를 만듭니다. 대부분의 팀이 처음 도입할 때는 무조건 prerender를 쓰려 하는데, 실전에서는 중요한 상단 링크만 prerender, 나머지는 prefetch로 나누는 것이 총체적 성능 예산을 지키는 방법입니다.
속성
prefetch
prerender
가져오는 것
대상 문서 HTML만
HTML + 서브리소스 + JS 실행 + 하이드레이션
메모리 비용
수십 KB (문서 크기)
페이지 전체 (수 MB 가능)
클릭 시 LCP
200~500ms 단축
거의 0ms
서브리소스 재사용
HTTP 캐시 경유
메모리 캐시에서 즉시 사용
JS 실행
안 함
완전히 실행 (analytics 이벤트 발화 주의)
동일 출처 제약
동일/크로스 오리진 모두 허용
크로스 오리진은 옵트인 헤더 필요
취소 비용
거의 없음
메모리·CPU 낭비 큼
prerender된 페이지에서는 document.prerendering이 true로 설정되고, 실제 활성화 시점에 prerenderingchange 이벤트가 발화합니다. 이걸 이용해 analytics나 A/B 실험 노출 이벤트는 반드시 활성화 이후로 미뤄야 합니다. TTFB를 낮추는 다른 서버 사이드 기법은 TTFB 완벽 가이드에서 이어서 정리했습니다.
// 프리렌더링 중에는 분석 이벤트 억제
if (document.prerendering) {
document.addEventListener('prerenderingchange', initAnalytics, { once: true });
} else {
initAnalytics();
}
function initAnalytics() {
// 이 시점에는 사용자가 실제로 페이지를 보고 있음
window.gtag?.('event', 'page_view');
}
eagerness 4단계와 트리거 조건
eagerness는 브라우저가 규칙을 얼마나 공격적으로 실행할지 결정합니다. 기본값을 그냥 두면 대부분의 경우 안전하지만, 대역폭이 넉넉한 랜딩 페이지에서는 immediate로 승격시켜 첫 클릭도 즉시 표시할 수 있고, 반대로 데이터 요금이 부담되는 모바일에서는 conservative로 낮춰야 합니다.
immediate: 규칙이 파싱되는 순간 즉시 실행. prerender와 조합하면 초기 로드 후 곧바로 대상 페이지를 백그라운드에서 렌더링합니다. 최대 10개 URL 제한.
실제 프로덕션 URL은 ?utm_source=google&ref=header 같은 쿼리스트링이 붙기 마련입니다. 기본적으로 브라우저는 쿼리스트링이 다르면 다른 URL로 취급해 프리렌더링 캐시를 재사용하지 못합니다. 2026년 9월 크롬 129부터 No-Vary-Search 응답 헤더가 안정화되어, 서버에서 어떤 파라미터를 무시할지 명시할 수 있습니다.
CDN 관점에서 이 헤더는 꽤 강력합니다. 제가 지난 프로젝트에서 Cloudflare Workers에 페이지 응답에 자동으로 No-Vary-Search를 붙이는 미들웨어를 배포했더니, 프리렌더링 히트율이 34%에서 81%로 뛰었습니다. 검색·필터 페이지처럼 쿼리스트링이 필수인 화면에서는 사실상 필수 헤더가 됐어요. 클라이언트 힌트와 조합할 때는 리소스 힌트 완벽 가이드에서 preload·preconnect와의 우선순위 관계도 함께 확인하세요.
Next.js·SvelteKit·Astro에서 사용하기
대부분의 메타프레임워크는 자체 라우터 프리페치 로직을 갖고 있어서 Speculation Rules와 충돌할 수 있습니다. 정답은 프레임워크의 기본 <Link> prefetch를 끄고, 대신 Speculation Rules로 통일하는 것입니다.
SvelteKit은 data-sveltekit-preload-data가 이미 훌륭한 프리로더이지만, 완전한 prerender 효과를 원한다면 Speculation Rules를 얹는 편이 낫습니다. app.html 헤드에 규칙을 삽입하고, 라우트별로 prerender = true가 설정된 페이지만 대상으로 삼는 필터를 넣으세요.
Astro 5
Astro는 View Transitions 인테그레이션과 Speculation Rules가 자연스럽게 조합됩니다. <ClientRouter />를 활성화한 뒤 문서 헤드에 규칙을 넣으면, prerender된 페이지가 활성화될 때도 View Transition이 그대로 재생됩니다.
서버에서 프리렌더링 요청 감지하기
제 커리어 최악의 버그 중 하나는 프리렌더링 요청도 실제 방문으로 카운트되면서 광고 전환 이벤트가 두 배로 발화한 사건이었습니다. (그날 밤을 새워 원인을 찾았던 게 아직도 생생하네요.) 해결책은 서버에서 프리렌더링 요청을 식별해 부수 효과를 차단하는 것입니다. 브라우저는 프리렌더링·프리페치 요청에 Sec-Purpose: prefetch 또는 Sec-Purpose: prefetch;prerender 헤더를 자동으로 붙입니다.
// Next.js 미들웨어 예시
import { NextRequest, NextResponse } from 'next/server';
export function middleware(req: NextRequest) {
const purpose = req.headers.get('sec-purpose') ?? '';
const isPrerender = purpose.includes('prerender');
const res = NextResponse.next();
if (isPrerender) {
// 광고 전환·재고 차감·이메일 발송 등 부수효과는 스킵
res.headers.set('x-prerender-skip-side-effects', '1');
// 세션 쿠키 갱신도 스킵 (사용자가 실제 방문하기 전까지)
res.headers.set('Cache-Control', 'private, max-age=0, must-revalidate');
}
return res;
}
Google Analytics 4를 사용한다면 gtag 큐를 document.prerendering 기반으로 지연시켜야 이중 카운팅이 사라집니다. 필드 데이터로 즉시 탐색의 실제 효과를 측정하는 방법은 RUM 완벽 가이드에서 이어집니다.
bfcache와의 관계
bfcache(back/forward cache)와 Speculation Rules는 서로 다른 방향의 탐색을 최적화합니다. bfcache는 뒤로/앞으로 가기를, Speculation Rules는 앞으로 갈 링크를 최적화하죠. 두 캐시는 독립적이므로 함께 쓸 수 있으며, prerender된 페이지가 활성화된 뒤 뒤로 가면 이전 페이지도 bfcache에서 복원됩니다. 다만 unload 이벤트 리스너는 두 캐시 모두를 무효화하므로 반드시 pagehide로 교체하세요.
Chrome DevTools로 디버깅하기
Chrome 122부터 DevTools > Application 패널에 "Speculative loads" 섹션이 추가되었습니다. 여기서 각 규칙의 상태(Pending / Ready / Failure)와 실패 사유를 실시간으로 볼 수 있습니다. 흔한 실패 코드는 다음과 같습니다.
MojoBinderPolicy: prerender 중 금지된 API(예: window.alert) 호출 감지
MainFrameNavigation: prerender 대상이 초기 활성화 전에 리다이렉트
LowEndDevice: 크롬이 기기 스펙(RAM < 2GB) 기준으로 자동 스킵
DataSaverEnabled: 사용자가 데이터 절약 모드를 켠 경우
BatteryLevel: 배터리 20% 이하에서 자동 비활성화
커맨드라인에서는 chrome://prerender-internals/를 열면 활성 규칙 목록과 각 URL의 상태 전이를 볼 수 있습니다. CI 파이프라인에 넣기 좋은 자동화 도구로는 Speculation Rules Editor 확장 프로그램이 있는데, 라이브 페이지에서 규칙을 실시간 편집하며 성능 영향을 측정할 수 있습니다.
흔한 함정과 성능 예산
Speculation Rules는 강력한 만큼 잘못 쓰면 사용자 데이터, 배터리, CPU를 낭비합니다. 실전에서 반복적으로 마주치는 함정들을 정리해봤습니다.
데이터 요금 폭탄
immediate + prerender + where로 사이트 내 모든 링크를 대상으로 잡으면 첫 로드에서 수십 MB를 낭비합니다. 반드시 상위 CTR 상위 10개 URL만 immediate로 지정하고, 나머지는 moderate로 다운그레이드하세요.
세션 상태 오염
prerender된 페이지가 localStorage나 IndexedDB에 쓰기를 시도하면 실제 사용자가 방문하기 전에도 상태가 남습니다. 스펙상 이런 API는 활성화 전까지 큐잉되지만, 서비스 워커에서 발화하는 fetch 이벤트는 예외이므로 event.request.headers.get('Sec-Purpose')로 필터링해야 합니다.
Analytics 이중 카운트
앞서 언급한 prerenderingchange 이벤트를 반드시 사용하세요. GA4, Amplitude, Mixpanel 모두 2026년 기준 자동 감지를 지원하지 않으므로 수동 처리가 필요합니다.
성능 예산 설정
제가 팀에 도입한 규칙은 다음 세 가지입니다.
동시 prerender 최대 2개: 그 이상은 CPU 스파이크로 다른 페이지의 INP를 악화시킵니다.
모바일에서는 eagerness ≤ moderate: 4G 사용자 대상으로 이보다 공격적이면 데이터 사용량 불만이 나옵니다.
배터리 20% 이하에서 자동 비활성화: 스크립트 로드 전 navigator.getBattery()로 확인해 규칙 삽입 자체를 스킵합니다.
// 조건부 규칙 삽입
async function injectSpeculationRules() {
if ('connection' in navigator && (navigator as any).connection.saveData) return;
const battery = await (navigator as any).getBattery?.();
if (battery && battery.level < 0.2 && !battery.charging) return;
const script = document.createElement('script');
script.type = 'speculationrules';
script.textContent = JSON.stringify({
prerender: [{
where: { href_matches: '/product/*' },
eagerness: 'moderate',
}],
});
document.head.appendChild(script);
}
injectSpeculationRules();
자주 묻는 질문
Speculation Rules는 어떤 브라우저에서 지원되나요?
2026년 9월 현재 크롬 121+, 엣지 121+, 오페라 107+, 삼성 인터넷 27+에서 안정 지원됩니다. 사파리 18은 실험 플래그로 prerender만, 파이어폭스는 2026 Q4에 prefetch부터 지원 예정입니다. 미지원 브라우저는 규칙 태그를 조용히 무시하므로 폴리필 없이 프로그레시브 인핸스먼트 방식으로 배포할 수 있습니다.
prefetch와 prerender 중 무엇을 써야 하나요?
상단 CTA·핵심 상세 페이지처럼 사용자가 반드시 이동할 링크는 prerender, 그 외 잠재적 링크는 prefetch로 나누세요. prerender는 JS 실행까지 마쳐 클릭 시 즉시 표시되지만 메모리·CPU 비용이 크고, prefetch는 문서만 캐시해 200~500ms 정도의 이득만 얻는 대신 훨씬 저렴합니다.
크로스 오리진 URL도 프리렌더링할 수 있나요?
가능하지만 대상 서버가 Supports-Loading-Mode: credentialed-prerender 응답 헤더로 옵트인해야 합니다. 옵트인이 없으면 크롬은 자동으로 prerender를 prefetch로 다운그레이드합니다. 크로스 사이트(다른 등록 도메인)는 credentialed prerender 자체가 불가능하고, credentialless 모드로만 시도할 수 있습니다.
Speculation Rules가 GA·GTM 이벤트를 중복 발화시키지 않게 하려면요?
클라이언트에서는 document.prerendering과 prerenderingchange 이벤트로 초기화를 지연시키고, 서버에서는 Sec-Purpose 헤더를 검사해 광고 전환·이메일 발송 같은 부수 효과를 차단하세요. GA4는 2026년 3월부터 자동 감지 실험 기능이 있지만 아직 GA 관리 프로퍼티에서 수동으로 활성화해야 합니다.
No-Vary-Search 없이도 검색 필터 페이지를 프리렌더링할 수 있나요?
기술적으로는 가능하지만 실효 히트율이 매우 낮습니다. UTM·필터·정렬 파라미터가 조합될 때마다 다른 URL로 취급되어 프리렌더 캐시가 무용지물이 됩니다. 최소한 UTM 계열 파라미터만이라도 No-Vary-Search 헤더로 제외하면 히트율이 두 배 이상 오르는 것을 실측했습니다.
웹 페이지 용량의 50~70%를 차지하는 이미지, 제대로 최적화하고 계신가요? AVIF·WebP 포맷 전략, srcset과 sizes 반응형 이미지, fetchpriority로 LCP 단축하기, AVIF 모바일 디코딩 함정, 이미지 CDN 자동 최적화까지 2026년 실전 코드와 함께 총정리합니다.