게시일: 2024년 11월 23일
모듈 기반 개발은 캐시 가능성 측면에서 몇 가지 실질적인 이점을 제공하므로 사용자에게 제공해야 하는 바이트 수를 줄일 수 있습니다. 코드의 세밀한 정밀도는 애플리케이션에서 중요한 코드의 우선순위를 지정할 수 있으므로 로드 스토리에도 도움이 됩니다.
그러나 모듈 종속 항목은 브라우저가 모듈이 로드될 때까지 기다려야 종속 항목을 파악할 수 있다는 점에서 로드 문제를 일으킵니다. 이를 해결하는 한 가지 방법은 브라우저가 모든 파일을 미리 알고 연결을 사용 중으로 유지할 수 있도록 종속 항목을 미리 로드하는 것입니다.
<link rel="preload">
<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 헤더는 간단한 선언적 방식으로 브라우저에 현재 탐색의 일부로 필요한 중요한 파일을 즉시 알릴 수 있도록 합니다. 브라우저가 미리 로드를 감지하면 리소스에 대한 우선순위가 높은 다운로드를 시작합니다. 따라서 리소스가 실제로 필요할 때 이미 가져왔거나 부분적으로 가져와 있습니다. 하지만 모듈에는 작동하지 않습니다.
<link rel="preload">
가 모듈에 작동하지 않는 이유는 무엇인가요?
여기서부터 일이 복잡해집니다. 리소스에는 여러 가지 사용자 인증 정보 모드가 있으며 캐시 히트를 얻으려면 이러한 모드가 일치해야 합니다. 그렇지 않으면 리소스를 두 번 가져오게 됩니다. 두 번 가져오는 것은 좋은 이유 없이 사용자의 대역폭을 낭비하고 더 오래 기다리게 만들기 때문에 좋지 않습니다.
<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 rel="modulepreload">
는 모듈의 <link rel="preload">
에 불과한가요?
간단히 말씀드리면 그렇습니다. 모듈 미리 로드용으로 특정 link
유형을 사용하면 사용 중인 사용자 인증 정보 모드를 신경 쓰지 않고 간단한 HTML을 작성할 수 있습니다. 기본값이 바로 작동합니다.
<head>
<link rel="modulepreload" href="super-critical-stuff.mjs">
</head>
[...]
<script type="module" src="super-critical-stuff.mjs">
이제 브라우저는 미리 로드하는 항목이 모듈임을 알 수 있으므로 모듈이 실행하려고 할 때까지 기다리는 대신 가져오기가 완료되는 즉시 모듈을 파싱하고 컴파일할 수 있습니다.
하지만 모듈의 종속 항목은 어떨까요?
그걸 물어보다니 재밌군 이 도움말에서는 다루지 않은 내용이 하나 있습니다. 재귀입니다.
<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에서 진행 중인 모듈 작업이 많으므로 번들러가 쉴 수 있는 날이 점점 가까워지고 있습니다.