[Web] preload, prefetch, preconnect

2022. 4. 3. 22:24Frontend

 

 

 

Overview

아무리 복잡한 웹 애플리케이션이라도 모든 것은 HTML 문서를 로딩하는 것부터 시작됩니다. 로딩된 문서는 자바스크립트를 실행하기 위한 <script> 태그, CSS style이나 image 등을 가져오기 위한 <link> 태그들을 포함하는 경우가 대부분이며, 브라우저는 HTML 문서를 받아온 뒤 이를 파싱 하면서(혹은 파싱 한 후에) 해당 태그들이 포함하는 리소스들을 가져옵니다. 

 

 

youtube.com 에 접속하면 처음으로 가져오는 문서의 head 내용물. link와 각종 script들이 포함되어 있다.

 

 

기본적으로 브라우저는 Critical Rendering Path를 기준으로 삼아 fetch 해야 하는 리소스에 우선순위를 부여합니다. 브라우저의 경우 도메인마다 2~6개(Chrome브라우저의 경우 도메인마다 최대 6개)의 커넥션을 맺을 수 있고, 최대 커넥션의 개수를 넘어서는 요청은 이전 요청이 끝나야 이루어질 수 있습니다. 따라서 같은 도메인에 여러 개의 리소스를 요청해야 하는 경우 브라우저는 어떤 리소스를 먼저 가져올 지에 대해서 우선순위를 설정하게 되고, 그 우선순위에 따라 데이터를 가져옵니다.

 

 

하지만 경우에 따라서, 브라우저가 추측한 값이 항상 정답이 아닐 때가 있으며, 이 경우 브라우저에게 "이 리소스를 먼저 가져와줘"라고 알려줄 수 있는 방법이 필요합니다. 모던 브라우저들은 이러한 힌트에 대한 방법들을 제공하며, 이번 포스팅에서는 "link 태그에서" 사용하는 힌트들에 대해서 살펴보도록 하겠습니다.

 

 

preload

The preload value of the <link> element's rel attribute lets you declare fetch requests in the HTML's <head>, specifying resources that your page will need very soon, which you want to start loading early in the page lifecycle, before browsers' main rendering machinery kicks in. This ensures they are available earlier and are less likely to block the page's render, improving performance.

 

 

link에 rel="preload"를 추가해서 요청하게 되면 브라우저는 해당 리소스를 최우선순위로 요청하게 됩니다.(Highest Priority) Chrome에서 preload를 사용하면 Chrome의 Preload Scanner가 HTML 파서가 해당 태그를 파싱 하기도 전에 해당 리소스를 요청합니다. 따라서 첫 페이지 렌더링에 무조건 필요한 폰트나, css 등의 리소스들을 빠르게 요청할 때 preload를 사용하여 HTML이 파싱 되는 동안 리소스를 미리 가져올 수 있습니다.

 

 

실제로 아래 스크린 샷을 보면 preload로 font css 파일을 가져오는 요청은 HTML이 파싱 되기 이전 시점부터 진행되고 있음을 확인할 수 있습니다. 해당 리소스를 파싱 시점보다 이전에 가져오므로 적절한 preload의 사용은 Critical Rendering Path를 최적화하는데 도움이 된다는 것을 확인할 수 있습니다.

 

 

preloads declared in markup are optimized in Chrome by the preload scanner. This means that in many cases, the preload will be fetched (with the indicated priority) before the HTML parser has even reached the tag. This makes it a lot more powerful than a custom preload implementation.

 

 

preload를 제대로 사용하기 위해서는 다음 사항을 지켜야 합니다.

 

  1. 다른 도메인의 리소스를 요청하는 경우 crossorigin을 명시해야 합니다.
    아래의 예시에서 fonts.googleapis 라는 Cross Origin 요청을 보내는 것이므로 preload가 제대로 동작하기 위해서는 crossorigin을 명시해주어야 합니다. 그렇지 않으면 preload가 제대로 동작하지 않습니다.

  2. as를 요청하는 리소스의 타입에 맞게 잘 명시하고, 해당 리소스를 바로 아래의 link 태그에 명시합니다.
    요청하는 리소스의 타입이 stylesheet이므로 as를 'style'로 지정해주어야 합니다. 올바른 리소스 타입을 명시하지 않으면 브라우저는 preload 된 값을 사용하지 않습니다.
    <link
      crossorigin
      href="https://fonts.googleapis.com/css?family=Noto+Sans+KR:300,400,500,700&display=swap"
      rel="preload"
      as="style"
    />
    <link
      crossorigin
      href="https://fonts.googleapis.com/css?family=Noto+Sans+KR:300,400,500,700&display=swap"
      rel="stylesheet"
    />

 

google font를 사용하는 경우 조금 특수한 경우로 위와 같이 preload로 선언한 뒤에 바로 아래에서 동일한 값을 preload 없이 다시 선언하고 있습니다. 이는 실제로 해당 요청을 통해 받아오는 css의 값이 폰트가 아닌 아래와 같은 css 파일 이기 때문입니다. 브라우저는 preload를 통해 style파일을 미리 받아오더라도, 해당 css 파일을 파싱 한 이후에 나오는 fonts.gstatic.com으로 폰트 파일을 다시 요청하기 때문에 실제로 css 파일을 파싱 할 때까지 폰트 파일의 load는 미뤄지게 됩니다.

 

 

 

 

 

이러한 "시점의 문제"를 해결하기 위해 Nextjs의 font optimizer는 아예 fonts.googleapis에 요청하지 않고 해당 css 값을 data-href로 style 태그에 주입하는 방식을 사용합니다. 

 

 

 

Browser Support

 

 

preconnect

preconnect는 현재 페이지에서 외부 도메인의 리소스(e.g font.gstatic.com)를 참고하는 것을 미리 알려주겠다는 의미입니다. 실제로 브라우저는  DNS Lookup, Initial Connection(TCP왕복), SSL Handshake(TLS왕복)등의 단계를 먼저 거치고, 그 뒤에 리소스를 받아오게 됩니다. preconnect는 리소스를 받아오기 이전의 DNS Lookup, TCP, TLS 왕복을 사전에 수행함으로써, 나중에 해당 도메인에서 데이터를 받아올 일이 생겼을 경우, 해당 연결을 사용해서 리소스만 빠르게 가져오도록 도와줍니다.

 

 

preconnect는 특정 리소스 서버의 주소, 즉 origin은 알지만 정확한 경로는 예상할 수 없는 상황에 주로 사용하며, 폰트, 미디어 스트리밍등에 주로 사용합니다. 위의 preload 예시에서 fonts.googleapis.com로부터 css를 가져오면 해당 css 파일은 폰트의 소스로서 fonts.gstatic.com을 참조하는데, 이 경우 preconnect로 fonts.gstatic.com과의 연결을 미리 설정해두면, 실제로 css를 파싱 하면서 path를 알게 되는 시점에서 미리 설정된 연결(connetion)을 사용하여 빠르게 리소스를 가져올 수 있습니다.

 

<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />

 

 

Browser Support

 

 

 

prefetch

prefetch는 첫 페이지가 아닌, "미래에" 사용될 것이라고 예상되는 리소스들을 미리 가져와서 "캐시에" 저장하는 기능을 합니다. prefetch는 재귀적으로 동작하지 않는 다는 특징이 있으며, 예를 들어 리스트 페이지를 로딩할 때 해당 페이지에 상세 페이지로 로딩할 수 있는 링크나 버튼이 있으면 해당 상세페이지의 리소스를 미리 가져와 캐시에 저장하는 등의 역할을 수행합니다.

 

실제로 next/link, next/router를 사용하면, 이러한 prefetch를 사용하는 것을 확인할 수 있습니다.

 

 

Browser support

 

Reference

https://developer.mozilla.org/en-US/docs/Web/Performance/Critical_rendering_path

 

Critical rendering path - Web Performance | MDN

The Critical Rendering Path is the sequence of steps the browser goes through to convert the HTML, CSS, and JavaScript into pixels on the screen. Optimizing the critical render path improves render performance.The critical rendering path includes the Docum

developer.mozilla.org

https://developer.mozilla.org/en-US/docs/Web/HTML/Link_types

 

Link types - HTML: HyperText Markup Language | MDN

In HTML, link types indicate the relationship between two documents, in which one links to the other using an <a>, <area>, <form>, or <link> element.

developer.mozilla.org

https://medium.com/reloading/preload-prefetch-and-priorities-in-chrome-776165961bbf

 

Preload, Prefetch And Priorities in Chrome

Today we’ll dive into insights from Chrome’s networking stack to provide clarity on how web loading primitives (like <link rel=“preload”> &…

medium.com

 

반응형

'Frontend' 카테고리의 다른 글

[Web.dev] Fast (2) - Performance Budget  (0) 2022.04.22
[Web.dev] Fast (1) - Introduction  (0) 2022.04.15
[Javascript] script async & defer  (1) 2022.04.02
[Nextjs] How getInitialProps Works  (0) 2022.03.28
Strict Mode  (0) 2022.03.27