Lazy Loading
Next.js의 Lazy loading (opens in a new tab)은 경로를 렌더링하는 데 필요한 JavaScript의 양을 줄여 애플리케이션의 초기 로딩 성능을 향상시키는 데 도움이 됩니다
이는 Client Components 및 가져온 라이브러리의 로딩을 연기하고, 필요할 때만 클라이언트 번들에 포함시키는 것을 허용합니다. 예를 들어, 사용자가 모달을 열기 위해 클릭할 때까지 모달 로딩을 연기할 수 있습니다.
Next.js에서 lazy loading을 구현하는 방법에는 두 가지가 있습니다:
- Using Dynamic Imports with
next/dynamic
- Using
React.lazy()
(opens in a new tab) with Suspense (opens in a new tab)
기본적으로, Server Components는 자동으로 코드 분할 (opens in a new tab)되며, 서버에서 클라이언트로 UI 조각을 점진적으로 전송하는 스트리밍을 사용할 수 있습니다. Lazy loading은 Client Components에 적용됩니다.
next/dynamic
next/dynamic
은 React.lazy()
(opens in a new tab)와 Suspense (opens in a new tab)의 복합체입니다. 이는 app
및 pages
디렉토리에서 점진적인 마이그레이션을 허용하도록 동일하게 작동합니다.
Examples
next/dynamic
을 사용하면 헤더 컴포넌트는 페이지의 초기 JavaScript 번들에 포함되지 않습니다. 페이지는 먼저 Suspense fallback
을 렌더링한 후, Suspense
경계가 해결되면 Header
컴포넌트를 렌더링합니다.
import dynamic from 'next/dynamic'
const DynamicHeader = dynamic(() => import('../components/header'), {
loading: () => <p>Loading...</p>,
})
export default function Home() {
return <DynamicHeader />
}
알아두면 좋은 점:
import('path/to/component')
에서 경로는 명시적으로 작성해야 합니다. 템플릿 문자열이나 변수가 될 수 없습니다. 또한import()
는dynamic()
호출 내에 있어야 Next.js가 웹팩 번들 / 모듈 ID를 특정dynamic()
호출과 일치시켜 렌더링 전에 미리 로드할 수 있습니다.dynamic()
은 React 렌더링 내에서 사용할 수 없으며, 미리 로드가 작동하려면 모듈의 최상위에 표시되어야 합니다. 이는React.lazy
와 유사합니다.
With named exports
명명된 내보내기를 동적으로 가져오려면, import()
(opens in a new tab) 함수에서 반환된 Promise (opens in a new tab)에서 반환할 수 있습니다:
export function Hello() {
return <p>Hello!</p>
}
// pages/index.js
import dynamic from 'next/dynamic'
const DynamicComponent = dynamic(() =>
import('../components/hello').then((mod) => mod.Hello),
)
With no SSR
클라이언트 측에서 컴포넌트를 동적으로 로드하려면 ssr
옵션을 사용하여 서버 렌더링을 비활성화할 수 있습니다. 이는 window
와 같은 브라우저 API에 의존하는 외부 종속성 또는 컴포넌트에 유용합니다.
import dynamic from 'next/dynamic'
const DynamicHeader = dynamic(() => import('../components/header'), {
ssr: false,
})
With external libraries
이 예제는 퍼지 검색을 위해 외부 라이브러리 fuse.js
를 사용합니다. 모듈은 사용자가 검색 입력란에 입력한 후에만 브라우저에서 로드됩니다.
import { useState } from 'react'
const names = ['Tim', 'Joe', 'Bel', 'Lee']
export default function Page() {
const [results, setResults] = useState()
return (
<div>
<input
type="text"
placeholder="Search"
onChange={async (e) => {
const { value } = e.currentTarget
// Dynamically load fuse.js
const Fuse = (await import('fuse.js')).default
const fuse = new Fuse(names)
setResults(fuse.search(value))
}}
/>
<pre>Results: {JSON.stringify(results, null, 2)}</pre>
</div>
)
}