프리로드 스캐너(preload scanner)는 브라우저 안의 보조 HTML 파서다. 주 파서가 막혀 있는 동안 원본 마크업을 앞질러 읽고, 그 안에 적힌 리소스를 미리 요청한다. HTML 명세에서는 추측 HTML 파싱(speculative HTML parsing)이라는 선택적 최적화로 정의한다. "preload scanner"는 브라우저 구현에서 부르는 이름이다.
왜 필요한가
주 파서는 막는 리소스를 만나면 멈춘다. <link>로 불러오는 스타일시트, async나 defer 없는 <script>가 그렇다. 브라우저는 스크립트가 DOM(Document Object Model)을 바꿀지 미리 알 수 없어서, 스크립트 실행이 끝날 때까지 뒤를 해석하지 않는다. 그 사이 스캐너가 뒤쪽의 <img>, <link>, <script>를 먼저 찾아 요청을 띄워 둔다.
주 파서: <head> 해석 ── app.js 받고 실행할 때까지 멈춤 ── <img> 도달
스캐너: └─ <img src="/hero.jpg"> 발견, 바로 요청명세가 정한 규칙은 몇 가지뿐이고 무엇을 미리 받을지는 브라우저가 정한다.
- 스캐너는 주 파서의 상태나 문서를 바꾸지 않는다
- 막고 있는 스크립트가 아무 일도 하지 않는다고 가정했을 때 주 파서가 받지 않을 리소스는 미리 받지 않는다
- 같은 마크업을 스캐너와 주 파서가 둘 다 볼 수 있다. 중복 요청은 캐시 규칙으로 막는 것을 기대한다
스캐너가 못 보는 리소스
스캐너는 처음 받은 HTML 마크업만 읽는다. web.dev는 다음 패턴을 스캐너를 무력화하는 예로 든다.
| 패턴 | 왜 못 보나 | 대처 |
|---|---|---|
JS로 주입한 async 스크립트 | 실행 전에는 마크업에 없음 | <script src="…" async>로 마크업에 직접 쓴다 |
JS 지연 로딩(data-src) | 스캐너는 data-src를 src처럼 읽지 않음 | 첫 화면 이미지는 그냥 src로 쓴다 |
CSS background-image | 스캐너는 CSS 파일 안을 훑지 않음 | <img>로 바꾸거나 rel=preload로 알린다 |
| 클라이언트 렌더링 마크업 | JS가 만든 HTML 속 리소스는 보이지 않음 | 가능하면 서버 렌더링(SSR)이나 정적 생성 |
| 과한 인라인 | 문서가 커져 뒤쪽 리소스 발견이 늦어짐 | 인라인은 최소로, 작은 리소스만 |
<!-- 스캐너가 바로 찾는다 -->
<img src="/hero.jpg" alt="메인 이미지" width="1200" height="600">
<!-- 스크립트가 실행된 뒤에야 요청된다 -->
<img data-src="/hero.jpg" class="lazy" alt="메인 이미지">그래서 첫 화면의 중요한 이미지는 HTML에 직접 쓰는 것이 가장 빠르다. 스캐너가 찾을 수 없는 리소스만 preload로 알린다. preload는 스캐너를 켜는 설정이 아니라, 스캐너가 못 찾는 리소스를 개발자가 직접 알려 주는 수단이다. 너무 많이 preload하면 모든 것이 우선이 되어 아무것도 우선이 아니게 된다. 찾은 뒤 얼마나 급하게 받을지는 fetchpriority가 정한다.
리소스 발견이 늦어지면 LCP(Largest Contentful Paint) 분석에서 Resource Load Delay가 커진다 → LCP 하위 구간. 클라이언트 렌더링과 서버 렌더링의 차이는 CSR·SSR·SSG·ISR, 파싱 이후 화면을 그리는 단계는 브라우저 렌더링 파이프라인를 본다.
명세: WHATWG HTML §13.2.8 — Speculative HTML parsing 출처: web.dev — Don't fight the browser preload scanner