Next.js에서 동적 가져오기를 사용한 코드 분할

코드 분할 및 스마트 로드 전략으로 Next.js 앱의 속도를 높이는 방법

이 게시물에서는 다양한 유형의 코드 분할과 동적 가져오기를 사용하여 Next.js 앱의 속도를 높이는 방법을 설명합니다.

기본적으로 Next.js는 JavaScript를 각 경로별로 별도의 청크로 분할합니다. 사용자가 애플리케이션을 로드하면 Next.js는 초기 경로에 필요한 코드만 전송합니다. 사용자는 애플리케이션을 탐색할 때 다른 경로와 연결된 청크를 가져옵니다. 경로 기반 코드 분할은 한 번에 파싱하고 컴파일해야 하는 스크립트의 양을 최소화하므로 페이지 로드 시간이 단축됩니다.

경로 기반 코드 분할은 좋은 기본값이지만 구성요소 수준에서 코드 분할을 사용하여 로드 프로세스를 추가로 최적화할 수 있습니다. 앱에 큰 구성요소가 있는 경우 이를 별도의 청크로 분할하는 것이 좋습니다. 이렇게 하면 중요하지 않거나 특정 사용자 상호작용(예: 버튼 클릭)에서만 렌더링되는 대규모 구성요소를 지연 로드할 수 있습니다.

Next.js는 JavaScript 모듈(React 구성요소 포함)을 동적으로 가져오고 각 가져오기를 별도의 청크로 로드할 수 있는 동적 import()를 지원합니다. 이를 통해 구성요소 수준의 코드 분할을 실행하고 리소스 로드를 제어하여 사용자가 보고 있는 사이트 부분에 필요한 코드만 다운로드하도록 할 수 있습니다. Next.js에서 이러한 구성요소는 기본적으로 서버 측 렌더링(SSR)됩니다.

동적 가져오기의 실제 작동 방식

이 게시물에는 버튼이 하나 있는 간단한 페이지로 구성된 샘플 앱의 여러 버전이 포함되어 있습니다. 버튼을 클릭하면 귀여운 강아지가 표시됩니다. 앱의 각 버전을 살펴보면 동적 가져오기가 정적 가져오기와 어떻게 다른지, 그리고 이를 사용하는 방법을 확인할 수 있습니다.

앱의 첫 번째 버전에서는 강아지가 components/Puppy.js에 살고 있습니다. 페이지에 강아지를 표시하기 위해 앱은 정적 가져오기 문으로 index.jsPuppy 구성요소를 가져옵니다.

import Puppy from "../components/Puppy";

Next.js가 앱을 번들로 묶는 방법을 보려면 DevTools에서 네트워크 트레이스를 검사하세요.

  1. 사이트를 미리 보려면 앱 보기를 누른 다음 전체 화면전체 화면을 누릅니다.

  2. `Control+Shift+J`(Mac의 경우 `Command+Option+J`)를 눌러 DevTools를 엽니다.

  3. 네트워크 탭을 클릭합니다.

  4. 캐시 사용 중지 체크박스를 선택합니다.

  5. 페이지를 새로고침합니다.

페이지를 로드하면 Puppy.js 구성요소를 비롯한 필요한 모든 코드가 index.js에 번들로 묶입니다.

index.js, app.js, webpack.js, main.js, 0.js, dll (동적 링크 라이브러리) 파일 등 6개의 JavaScript 파일을 보여주는 DevTools Network 탭

Click me 버튼을 누르면 강아지 JPEG 요청만 네트워크 탭에 추가됩니다.

버튼을 클릭한 후 DevTools Network 탭. 동일한 JavaScript 파일 6개와 이미지 1개가 표시됩니다.

이 접근 방식의 단점은 사용자가 강아지를 보려고 버튼을 클릭하지 않더라도 index.js에 포함되어 있으므로 Puppy 구성요소를 로드해야 한다는 점입니다. 이 작은 예에서는 큰 문제가 아니지만 실제 애플리케이션에서는 필요한 경우에만 큰 구성요소를 로드하는 것이 큰 개선사항이 되는 경우가 많습니다.

이제 정적 가져오기가 동적 가져오기로 대체된 앱의 두 번째 버전을 살펴보겠습니다. Next.js에는 Next의 모든 구성요소에 동적 가져오기를 사용할 수 있는 next/dynamic가 포함되어 있습니다.

import Puppy from "../components/Puppy";
import dynamic from "next/dynamic";

// ...

const Puppy = dynamic(import("../components/Puppy"));

첫 번째 예의 단계에 따라 네트워크 트레이스를 검사합니다.

앱을 처음 로드하면 index.js만 다운로드됩니다. 이번에는 Puppy 구성요소에 관한 코드가 없으므로 0.5KB로 더 작아집니다 (37.9KB에서 37.4KB로 감소).

동일한 6개의 JavaScript 파일을 보여주는 DevTools 네트워크. 단, index.js가 0.5KB 더 작습니다.

이제 Puppy 구성요소는 버튼을 누를 때만 로드되는 별도의 청크 1.js에 있습니다.

버튼을 클릭한 후 DevTools Network 탭. 추가된 1.js 파일과 파일 목록 하단에 추가된 이미지가 표시됩니다.

실제 애플리케이션에서는 구성요소가 훨씬 더 큰 경우가 많으며 지연 로드로 인해 초기 JavaScript 페이로드가 수백 KB 정도 줄어들 수 있습니다.

맞춤 로드 표시기가 있는 동적 가져오기

리소스를 지연 로드할 때는 지연이 발생할 경우 로드 표시기를 제공하는 것이 좋습니다. Next.js에서는 dynamic() 함수에 추가 인수를 제공하여 이를 실행할 수 있습니다.

const Puppy = dynamic(() => import("../components/Puppy"), {
  loading: () => <p>Loading...</p>
});

로드 표시기의 작동 모습을 보려면 DevTools에서 느린 네트워크 연결을 시뮬레이션합니다.

  1. 사이트를 미리 보려면 앱 보기를 누른 다음 전체 화면전체 화면을 누릅니다.

  2. `Control+Shift+J`(Mac의 경우 `Command+Option+J`)를 눌러 DevTools를 엽니다.

  3. 네트워크 탭을 클릭합니다.

  4. 캐시 사용 중지 체크박스를 선택합니다.

  5. 제한 드롭다운 목록에서 빠른 3G를 선택합니다.

  6. Click me 버튼을 누릅니다.

이제 버튼을 클릭하면 구성요소를 로드하는 데 잠시 시간이 걸리고 그동안 앱에 'Loading…'(로드 중) 메시지가 표시됩니다.

텍스트가 적힌 어두운 화면

SSR이 없는 동적 가져오기

클라이언트 측에서만 구성요소를 렌더링해야 하는 경우 (예: 채팅 위젯) ssr 옵션을 false로 설정하면 됩니다.

const Puppy = dynamic(() => import("../components/Puppy"), {
  ssr: false,
});

결론

동적 가져오기를 지원하는 Next.js를 사용하면 구성요소 수준의 코드 분할을 통해 JavaScript 페이로드를 최소화하고 애플리케이션 로드 시간을 개선할 수 있습니다. 모든 구성요소는 기본적으로 서버 측에서 렌더링되며 필요한 경우 언제든지 이 옵션을 사용 중지할 수 있습니다.