노트

프리로드 스캐너

Preload Scanner

프론트엔드#browser#performance#html · 연결된 개념 5개

쉽게 말하면

프리로드 스캐너는 요리책을 읽는 사람 옆에서 뒤쪽 레시피까지 먼저 훑어보고 필요한 재료를 미리 주문해 두는 도우미예요. 앞에서 오븐을 기다리는 동안에도 재료가 오고 있으니 요리가 빨리 끝나요.

비유의 한계 도우미는 책에 적힌 재료만 볼 수 있어요. JS가 나중에 만들어 넣는 img, CSS 파일 안의 배경 이미지, data-src에 숨긴 주소는 찾지 못해서, 그런 리소스는 늦게 요청돼요.

프리로드 스캐너(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

연결된 개념

이 노트를 가리키는 문서

뜻이 가까운 노트

  • 이미지 loading 속성

    ==img·iframe의 loading 속성으로 리소스를 언제 내려받을지 정한다. eager(기본)는 태그를 만나자마자, lazy는 화면에 가까워질 때까지 미룬다.== JS(JavaScript) 없이 동작하는 브라우저 기본 기능이라 IntersectionObserver로 직접 만들 필요가 없다.

  • 스트리밍 SSR

    스트리밍 SSR(Streaming Server-Side Rendering)은 서버가 페이지 전체를 다 만들 때까지 기다리지 않고, 준비된 부분의 HTML(HyperText Markup Language)부터 먼저 보내고 느린 부분은 준비되는 대로 같은 응답에 이어서 흘려보내는 렌더링 방식이다. suspense 경계가 나눔의 단위다.

  • 부분 사전 렌더링(PPR)

    PPR(Partial Prerendering)은 한 라우트를 빌드 때 만든 정적 셸(Static Shell)과 요청 때 채우는 동적 구멍으로 나눠, 셸은 즉시 보내고 구멍은 스트리밍으로 채우는 렌더링 모델이다. Next.js 16에서 Cache Components(cacheComponents: true)를 켜면 기본 동작이다(2026 기준).

  • loading 경계와 레이아웃 끌어올리기

    Next.js App Router에서 loading.tsx는 같은 세그먼트의 page와 그 아래(하위 layout 포함)를 suspense로 감싸지만, **같은 세그먼트의 layout은 감싸지 않는다**. 그래서 데이터를 기다릴 필요가 없는 공통 UI(탭바, 필터 헤더)를 page에서 layout으로 끌어올리면 스켈레톤에 덮이지 않고 처음부터 실제 모습으로 보인다.

  • 브라우저 JS 메모리 누수 원인

    메모리 누수(Memory Leak)는 더 이상 쓰지 않는 메모리가 해제되지 않고 계속 남는 현상이다. 자바스크립트는 가비지 컬렉션(Garbage Collection, GC)으로 메모리를 자동으로 회수하므로, 누수는 대개 필요 없어진 객체를 가리키는 참조가 어딘가에 남아 있을 때 생긴다.

보기 옵션