모듈 미리 로드

Sérgio Gomes

게시일: 2024년 11월 23일

모듈 기반 개발은 캐시 가능성 측면에서 몇 가지 실질적인 이점을 제공하므로 사용자에게 제공해야 하는 바이트 수를 줄일 수 있습니다. 코드의 세밀한 정밀도는 애플리케이션에서 중요한 코드의 우선순위를 지정할 수 있으므로 로드 스토리에도 도움이 됩니다.

그러나 모듈 종속 항목은 브라우저가 모듈이 로드될 때까지 기다려야 종속 항목을 파악할 수 있다는 점에서 로드 문제를 일으킵니다. 이를 해결하는 한 가지 방법은 브라우저가 모든 파일을 미리 알고 연결을 사용 중으로 유지할 수 있도록 종속 항목을 미리 로드하는 것입니다.

<link rel="preload">는 브라우저에 리소스가 필요해지기 전에 리소스를 선언적으로 미리 요청하는 방법입니다.

<head>
  <link rel="preload" as="style" href="critical-styles.css">
  <link rel="preload" as="font" crossorigin type="font/woff2" href="myfont.woff2">
</head>

이는 CSS 파일 내에 숨겨져 있고 때로는 여러 수준으로 깊이 있는 글꼴과 같은 리소스에 특히 유용합니다. 이 경우 브라우저는 대용량 글꼴 파일을 가져와야 한다는 것을 알기 전에 여러 번 왕복해야 합니다. 그 시간에 다운로드를 시작하고 전체 연결 대역폭을 활용할 수 있었습니다.

<link rel="preload"> 및 이에 상응하는 HTTP 헤더는 간단한 선언적 방식으로 브라우저에 현재 탐색의 일부로 필요한 중요한 파일을 즉시 알릴 수 있도록 합니다. 브라우저가 미리 로드를 감지하면 리소스에 대한 우선순위가 높은 다운로드를 시작합니다. 따라서 리소스가 실제로 필요할 때 이미 가져왔거나 부분적으로 가져와 있습니다. 하지만 모듈에는 작동하지 않습니다.

여기서부터 일이 복잡해집니다. 리소스에는 여러 가지 사용자 인증 정보 모드가 있으며 캐시 히트를 얻으려면 이러한 모드가 일치해야 합니다. 그렇지 않으면 리소스를 두 번 가져오게 됩니다. 두 번 가져오는 것은 좋은 이유 없이 사용자의 대역폭을 낭비하고 더 오래 기다리게 만들기 때문에 좋지 않습니다.

<script><link> 태그의 경우 crossorigin 속성으로 사용자 인증 정보 모드를 설정할 수 있습니다. 그러나 crossorigin 속성이 없는 <script type="module"><link rel="preload">에 존재하지 않는 omit의 사용자 인증 정보 모드를 나타냅니다. 즉, <script><link> 모두에서 crossorigin 속성을 다른 값 중 하나로 변경해야 하며, 미리 로드하려는 항목이 다른 모듈의 종속 항목인 경우 이를 쉽게 변경할 수 없습니다.

또한 파일을 가져오는 것은 코드를 실제로 실행하는 첫 번째 단계일 뿐입니다. 먼저 브라우저에서 파싱하고 컴파일해야 합니다. 모듈이 필요할 때 코드를 실행할 준비가 되도록 미리 실행하는 것이 좋습니다. 그러나 V8 (Chrome의 JavaScript 엔진)은 다른 JavaScript와 다르게 모듈을 파싱하고 컴파일합니다. <link rel="preload">는 로드되는 파일이 모듈임을 나타내는 방법을 제공하지 않으므로 브라우저에서 할 수 있는 일은 파일을 로드하여 캐시에 저장하는 것뿐입니다. <script type="module"> 태그를 사용하여 스크립트가 로드되거나 다른 모듈에서 로드되면 브라우저는 코드를 파싱하고 JavaScript 모듈로 컴파일합니다.

간단히 말씀드리면 그렇습니다. 모듈 미리 로드용으로 특정 link 유형을 사용하면 사용 중인 사용자 인증 정보 모드를 신경 쓰지 않고 간단한 HTML을 작성할 수 있습니다. 기본값이 바로 작동합니다.

<head>
  <link rel="modulepreload" href="super-critical-stuff.mjs">
</head>
[...]
<script type="module" src="super-critical-stuff.mjs">

이제 브라우저는 미리 로드하는 항목이 모듈임을 알 수 있으므로 모듈이 실행하려고 할 때까지 기다리는 대신 가져오기가 완료되는 즉시 모듈을 파싱하고 컴파일할 수 있습니다.

브라우저 지원

  • Chrome: 66
  • Edge: ≤79
  • Firefox: 115.
  • Safari: 17

소스

하지만 모듈의 종속 항목은 어떨까요?

그걸 물어보다니 재밌군 이 도움말에서는 다루지 않은 내용이 하나 있습니다. 재귀입니다.

<link rel="modulepreload"> 사양은 실제로 요청된 모듈뿐만 아니라 모든 종속 항목 트리도 선택적으로 로드할 수 있습니다. 브라우저는 이렇게 할 필요가 없지만 할 수 있습니다.

앱을 실행하려면 전체 종속 항목 트리가 필요하므로 모듈과 종속 항목 트리를 미리 로드하는 데 가장 적합한 크로스브라우저 솔루션은 무엇일까요?

종속 항목을 재귀적으로 미리 로드하는 브라우저는 모듈의 중복 삭제가 강력해야 하므로 일반적으로 모듈과 종속 항목의 플랫 목록을 선언하고 브라우저가 동일한 모듈을 두 번 가져오지 않도록 신뢰하는 것이 좋습니다.

<head>
  <!-- dog.js imports dog-head.js, which in turn imports
       dog-head-mouth.js, which imports dog-head-mouth-tongue.js. -->
  <link rel="modulepreload" href="dog-head-mouth-tongue.mjs">
  <link rel="modulepreload" href="dog-head-mouth.mjs">
  <link rel="modulepreload" href="dog-head.mjs">
  <link rel="modulepreload" href="dog.mjs">
</head>

모듈을 미리 로드하면 성능이 향상되나요?

미리 로드하면 브라우저가 긴 왕복 시간 동안 할 일이 없어 멈추지 않도록 가져와야 하는 항목을 브라우저에 알려 대역폭 사용량을 극대화하는 데 도움이 됩니다. 모듈을 실험하고 있는데 깊은 종속 항목 트리로 인해 성능 문제가 발생하는 경우 플랫 미리 로드 목록을 만드는 것이 도움이 될 수 있습니다.

하지만 모듈 성능은 아직 개발 중이므로 개발자 도구를 사용하여 애플리케이션에서 발생하는 상황을 자세히 살펴보고 그동안 애플리케이션을 여러 청크로 번들로 묶는 것이 좋습니다. 하지만 Chrome에서 진행 중인 모듈 작업이 많으므로 번들러가 쉴 수 있는 날이 점점 가까워지고 있습니다.