프론트엔드 성능 최적화의 화두가 조용히 바뀌고 있다. 이미지 압축이나 번들 사이즈 줄이기 같은 '있는 것을 줄이는' 접근에서, '사용자가 요청하기 전에 미리 준비해두는' 방향으로 무게 중심이 이동하는 중이다. 두 개의 서로 다른 사례가 이 흐름을 동시에 보여준다. 하나는 78개의 단위 데이터로 7,681개의 정적 페이지를 빌드타임에 생성한 Astro SSG 프로젝트이고, 다른 하나는 브라우저가 사용자 클릭 전에 다음 페이지를 백그라운드에서 렌더링하게 만드는 Speculation Rules Prerender다. 두 전략의 공통 전제는 단순하다. 미리 준비해두면 빠르다. 하지만 진짜 설계 고민은 그 다음에 시작된다.
78개로 7,681페이지를 만드는 법
dev.to에 공개된 사례를 보면, 단위 변환 사이트 metriqcon.co는 길이·무게·온도 등 8개 카테고리, 78개 단위 데이터를 기반으로 카테고리 허브 페이지, 단위 쌍 페이지(n×(n-1) 조합), 그리고 자주 쓰는 13개 값을 각 쌍에 대입한 값 페이지까지 세 층위로 URL을 펼쳐낸다. 최종 숫자가 7,681이다. 기술 스택은 Astro + React Islands. 핵심 판단은 명확하다. 페이지의 90%는 빌드타임에 정적 HTML로 생성하고, 나머지 10%인 인터랙티브 입력 필드만 React island로 하이드레이션한다. 구글 크롤러는 JavaScript 실행 없이 전체 콘텐츠를 읽고, 사용자는 즉각적인 응답을 받는다.
흥미로운 건 SEO 전략이다. 값 페이지의 <title>에 계산 결과 자체를 박아넣는다. "15 Meters to Feet = 49.2126 ft" 형식으로. 역설적이게도 이 방식이 CTR을 올렸다고 한다. 정답을 제목에서 이미 보여줬는데도 사람들이 클릭하는 이유는 그 스니펫이 '이 사이트가 정확한 답을 가지고 있다'는 신뢰 신호로 작동하기 때문이다.
그런데 이 전략이 초기에 한 번 좌초했다. 7,681페이지를 배포했더니 구글이 약 200페이지만 색인하고 나머지 7,639페이지를 '발견됨-현재 미색인' 상태로 묶어버린 것이다. 이유는 얇은 콘텐츠(thin content) 판정. 구조는 같은데 텍스트 변화가 적다고 본 것이다. 해결책은 각 단위 쌍마다 실제로 다른 문장을 생성하는 콘텐츠 엔진을 만드는 것이었다. 단위별 실생활 예시, 역사적 사실, FAQ 7개, JSON-LD 구조화 데이터까지 조합해 페이지마다 고유한 텍스트 블록을 렌더링한다. 대규모 프로그래매틱 페이지 생성은 '수량'이 아니라 '각 페이지가 독립적인 가치를 가지는가'로 평가받는다는 교훈이다.
브라우저가 클릭 전에 렌더링한다는 것의 의미
Speculation Rules API는 브라우저에게 "이 URL은 곧 이동할 가능성이 높으니 미리 렌더링해도 된다"고 힌트를 주는 기능이다. 사용자가 링크를 클릭하는 순간 이미 백그라운드에서 완성된 페이지가 기다리고 있으니, LCP와 체감 전환 속도가 드라마틱하게 개선된다. velog에 정리된 분석에 따르면 prerender 경로에서 LCP가 680ms, 일반 경로에서 1,450ms로 측정된 실사례가 있다. 숫자만 보면 적용하지 않을 이유가 없다.
하지만 SSG가 '모든 페이지를 미리 생성'하는 게 아니라 '가치 있는 페이지를 선별해 미리 생성'하는 것이듯, Prerender도 어떤 URL을 미리 준비해도 되는지 분류하는 작업이 핵심이다. 그리고 이 분류를 잘못하면 성능 최적화가 보안 사고로 이어진다.
경계 설계: 어떤 URL은 절대 미리 열면 안 된다
문제는 GET 요청처럼 보이지만 실제로는 서버 상태를 바꾸는 URL들이 존재한다는 점이다. GET /coupon/apply?id=SUMMER 같은 경우, 브라우저가 prerender 과정에서 이 URL에 접근하면 사용자가 실제로 클릭하기도 전에 쿠폰이 소진될 수 있다. 로그인 콜백, 결제 승인, 로그아웃, 장바구니 처리—이런 경로들은 화면 조회처럼 보여도 내부에서 세션이나 재고, 결제 상태를 건드린다.
따라서 Speculation Rules를 도입할 때 가장 먼저 해야 할 작업은 URL을 세 유형으로 분류하는 것이다.
| URL 유형 | 예시 | Prerender 여부 |
|---|---|---|
| 공개 조회 | /videos/123, /courses/456 |
허용 |
| 사용자별 조회 | /me, /cart |
보류 |
| 상태 변경 가능 | /checkout/confirm, /auth/callback, /coupon/apply |
제외 |
그리고 이 분류를 코드로 명시하고, PR 체크리스트와 자동 테스트로 강제해야 한다. "이 URL은 speculation 대상인가?"를 라우트 추가 PR마다 확인하지 않으면, 예외 목록은 서비스가 커질수록 걷잡을 수 없이 늘어난다.
SSG와 Prerender, 같은 철학의 두 레이어
두 전략을 나란히 놓으면 같은 철학이 보인다. SSG는 빌드타임에 '미리 HTML을 만든다'. Prerender는 런타임에 '미리 다음 페이지를 브라우저 메모리에 올린다'. 둘 다 사용자가 요청하는 순간을 최대한 앞당기는 시도다.
그런데 둘 다 분류 설계 없이 전체에 적용하면 오히려 역효과가 난다. Astro SSG 사례처럼 7,681페이지를 무차별 생성하면 thin content 필터에 걸린다. Speculation Rules를 모든 링크에 켜면 결제 흐름이 오염된다. 결국 핵심 설계 질문은 동일하다. "이 URL을 미리 준비해도 되는가?"
시사점: '빠르다'는 말의 책임 범위
성능 최적화는 측정 없이 운영할 수 없다. Astro SSG 사례는 구글 색인 수와 CTR로 효과를 확인했고, Prerender 도입 팀은 RUM으로 activation 비율과 LCP 차이, 그리고 오류율을 동시에 모니터링해야 한다. "빨라졌을 것 같다"로 끝내면 실제로 무슨 일이 벌어지는지 알 수 없다.
특히 Next.js나 React Router 기반 프로젝트에서는 라우팅 구조 자체가 성능 정책의 기준이 된다. URL을 잘 설계하면 Speculation Rules 예외 목록이 단순해지고, URL에 상태 변경 로직이 뒤섞여 있으면 예외 처리가 끝없이 복잡해진다. URL 설계는 SEO와 성능과 보안을 동시에 결정하는 아키텍처 레이어다.
전망: 프리렌더 경계는 점점 정교해진다
Chrome은 현재 prerender_until_script Origin Trial을 실험 중이다. 페이지를 특정 스크립트 실행 지점까지만 준비하고 멈추는 방식으로, 성능 이득은 챙기되 민감한 JavaScript 실행을 더 조심스럽게 다루려는 방향이다. 브라우저 자체가 prerender의 경계를 더 세밀하게 제어하는 쪽으로 진화하고 있다는 신호다.
SSG 역시 빌드타임 전체 생성에서 ISR(Incremental Static Regeneration)이나 Partial Prerendering처럼 '필요한 부분만 적시에 정적화'하는 방향으로 이동하고 있다. 결국 '미리 준비하는 프론트엔드'의 진화 방향은 전체를 미리 준비하는 것이 아니라, 무엇을 언제 얼마나 준비할지 정밀하게 제어하는 능력을 갖추는 쪽이다. 그리고 그 제어의 기반은 항상 URL 분류와 경계 설계에서 시작된다.