12.4
Lazy Loading
Lazy Loading(지연 로딩)은 필요한 시점에만 데이터를 불러오는 기술입니다. 이미지 관점에서의 Lazy Loading도 있지만, 여기에서는 데이터 관점에서의 Lazy Loading에 대해 다뤄보도록 하겠습니다. 데이터 관점에서의 Lazy Loading은 웹페이지의 모든 콘텐츠를 한 번에 로드하지 않고, 사용자가 해당 콘텐츠에 접근할 때 로드하는 방식이라고 이해하면 됩니다.
예를 들면, 어떤 페이지에 다이얼로그 컴포넌트가 포함되어 있다고 할 경우, 사용자가 다이얼로그를 열 때까지 로딩을 지연시키는 것이죠. 이렇게 Lazy Loading을 적용하면 초기 로딩 시간을 줄일 수 있으며, 사용자 경험과 성능을 향상시킬 수 있습니다.
Next.js에서 Lazy Loading을 구현하는 방법은 아래와 같이 두 가지가 있습니다.
next/dynamic을 사용한 동적 importReact.lazy()와 Suspense 사용NOTE. 서버 컴포넌트와 Lazy Loading
기본적으로 서버 컴포넌트는 자동으로 Code Splitting되며, 스트리밍을 사용해 서버에서 클라이언트로 UI 조각을 점진적으로 전송할 수 있습니다. 그래서 Lazy Loading은 클라이언트 컴포넌트에 적용된다고 이해하면 됩니다.
next/dynamicnext/dynamic은 React.lazy()와 Suspense를 합친 것입니다. incremental migration을 가능케 하기 위해 App Router 및 Pages Router에서 동일한 방식으로 작동합니다.
아래는 Next.js에서 Lazy Loading을 적용하는 다양한 방법을 나타낸 것입니다.
아래 코드와 같이 next/dynamic을 사용하여 클라이언트 컴포넌트를 동적으로 로드할 수 있습니다.
'use client';
import { useState } from 'react';
import dynamic from 'next/dynamic';
// 클라이언트 컴포넌트
const ComponentA = dynamic(() => import('../components/A'));
const ComponentB = dynamic(() => import('../components/B'));
const ComponentC = dynamic(() => import('../components/C'), { ssr: false });
function ClientComponentExample() {
const [showMore, setShowMore] = useState(false);
return (
<div>
{/* 즉시 로드되며, 별도의 클라이언트 번들로 분리 됨 */}
<ComponentA />
{/* 컴포넌트가 실제로 필요한 경우 로드 됨 */}
{showMore && <ComponentB />}
<button onClick={() => setShowMore(!showMore)}>Toggle</button>
{/* 클라이언트측에서만 로드 됨 */}
<ComponentC />
</div>
);
}
export default ClientComponentExample;
next/dynamic을 사용하여 클라이언트 컴포넌트를 동적으로 로드할 경우, 클라이언트 컴포넌트는 기본적으로 서버 사이드 렌더링(SSR) 됩니다. 클라이언트 컴포넌트에 대해 서버 사이드 렌더링을 비활성화하려면, 아래 코드와 같이 ssr 옵션을 false로 설정하면 됩니다.
const MyComponent = dynamic(() => import('../MyComponent'), { ssr: false });
NOTE.
ssr: false옵션 사용 시 유의사항ssr: false옵션은 클라이언트 컴포넌트에서만 작동합니다. 그래서 클라이언트 Code Splitting이 올바르게 작동하도록 하려면,ssr: false옵션을 클라이언트 컴포넌트에서만 사용해야 합니다. 만약 이 옵션을 서버 컴포넌트에서 사용하려고 하면 오류가 발생하게 됩니다.
만약 서버 컴포넌트를 동적으로 가져오면, 해당 서버 컴포넌트의 자식인 클라이언트 컴포넌트들만 지연 로딩되고 서버 컴포넌트 자체는 지연 로딩되지 않습니다.
import dynamic from 'next/dynamic';
// 서버 컴포넌트
const ServerComponent = dynamic(() => import('../components/ServerComponent'));
function ServerComponentExample() {
return (
<div>
<ServerComponent />
</div>
);
}
export default ServerComponentExample;
외부 라이브러리는 import() 함수를 사용하여 필요할 때 로드할 수 있습니다. 아래 예제 코드에서는 퍼지 검색을 위해 외부 라이브러리인 fuse.js를 사용합니다. 이 모듈은 사용자가 검색 입력란에 글씨를 입력할 때 클라이언트에서만 로드됩니다.
'use client';
import { useState } from 'react';
const names = ['Tim', 'Joe', 'Bel', 'Lee'];
function Page() {
const [results, setResults] = useState();
return (
<div>
<input
type='text'
placeholder='Search'
onChange={async (e) => {
const { value } = e.currentTarget;
// 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>
);
}
export default Page;
아래 코드와 같이 dynamic() 함수의 옵션에 loading 속성을 사용하면 커스텀 로딩 컴포넌트를 보여줄 수 있습니다.
import dynamic from 'next/dynamic';
const WithCustomLoading = dynamic(
() => import('../components/WithCustomLoading'),
{
loading: () => <p>Loading...</p>,
}
);
function Page() {
return (
<div>
{/* 컴포넌트가 완전히 로딩 될 때까지 커스텀 로딩 컴포넌트가 렌더링 됨 */}
<WithCustomLoading />
</div>
);
}
export default Page;
named export한 모듈을 동적으로 로드하기 위해서는 아래 코드와 같이 import() 함수가 반환하는 Promise에서 해당 모듈을 반환하면 됩니다.
'use client';
// Named Export
export function Hello() {
return <p>Hello!</p>;
}
import dynamic from 'next/dynamic'
const ClientComponent = dynamic(() =>
import('../components/hello').then((mod) => mod.Hello)
)
마지막 업데이트: 2025년 10월 24일 02시 49분
이 문서의 저작권은 이인제(소플)에 있습니다. 무단 전재와 무단 복제를 금합니다.
On this page