import한 components나 함수는 보통 코드 상단에 존재한다. 이것의 의미는 유저가 해당 페이지를 실행할 시 코드에 적힌 모든 import 요소를 다운로드한다는 의미이다. 평소에는 다운로드의 순서나 여부가 크게 상관 없지만, 때에 따라 특정 조건에서만 요소를 로드할 필요가 있다.
Next.js
는 Dynamic Import
를 통해 선별적으로 components를 로드할 수 있다.
// tokoopne1.tsx
export default function Tokoopne1() {
console.log("I'm Dynamic");
return <h1>Dynamic</h1>;
}
이러한 component가 있다고 하자. 로그인 페이지에서 B버튼을 눌렀을 때만 보여주고 싶다면 B버튼 활성화 분기 아래에 해당 component를 삽입하고 import할 것이다.
// enter.tsx
import type { NextPage } from "next";
import Tokoopne1 from "@components/tokoopne1";
const Enter: NextPage = () => {
return (
<div>
<button>Email</button>
<button>Phone</button>
{button === Email ? (
<h1>Email Plz</h1>
) : (
<Tokoonpne1 />
)
</div>
);
};
보여지는 것은 아래와 같다.
언뜻 보기에 Phone
버튼을 눌렀을 때만 호출되는 듯 보인다. 하지만 네트워크에서 해당 페이지를 확인해 보면 버튼을 누르기 전에 로드했음을 알 수 있다.
새로고침 후 Phone
버튼을 누르기 전임에도 이미 현재 페이지는 Tokoopne1
컴포넌트를 다운받아 놓은 상태다.
Dynamic Import
는 다음과 같이 사용한다.
// enter.tsx
import type { NextPage } from "next";
import dynamic from "next/dynamic";
const Tokoopne1 = dynamic(() => import("@components/tokoopne1"));
const Enter: NextPage = () => {
return (
<div>
<button>Email</button>
<button>Phone</button>
{button === Email ? (
<h1>Email Plz</h1>
) : (
<Tokoonpne1 />
)
</div>
);
};
겉으로 보기에는 Dynamic Import
역시 큰 차이가 없어 보인다. 하지만 네트워크를 확인하면 전혀 다르다.
Normal Import의 네크워크 이미지와 비교해보면, Tokoopne1
가 검색은 되지만 import가 된 상태는 아니다. 즉, 해당 버튼을 누르기 전에는 유저의 브라우저에 다운로드하지 않는다.
Phone
버튼을 눌러야 해당 컴포넌트가 다운로드된다.
dynamic()
은 사전 로드가 작동하도록 모듈의 최상위에 표시되어야 하기 때문에 렌더링 내부에서는 사용할 수 없다.
또한 SSR
과 함께 작동하므로 dynamic(() => import("@components/tokoopne1"), { ssr: false })
과 같이 서버 사이드 렌더링 여부를 설정할 수 있다.
유저의 네트워크 속도가 느리거나 component의 크기가 매우매우 커서 로드가 빠르게 되지 않으면 유저는 빈 화면을 보고 있어야 한다. 그런 상황을 방지하기 위해 loading component를 추가할 수 있다.
const Tokoopne1 = dynamic(
() =>
new Promise((resolve) =>
setTimeout(() => resolve(import("@components/tokoopne1")), 10000)
),
{
ssr: false,
loading: () => <span>Loading a big Component</span>
}
);
인위적으로 10초 후에 컴포넌트를 불러오는 코드이다. loading
프로퍼티는 해당 component가 로드될 때까지 보여줄 화면을 구성한다. 또다른 방법으로는 suspense
를 사용하는 것이다.
// enter.tsx
import type { NextPage } from "next";
import dynamic from "next/dynamic";
const Tokoopne1 = dynamic(
() =>
new Promise((resolve) =>
setTimeout(() => resolve(import("@components/tokoopne1")), 10000)
),
{
ssr: false,
suspense: true,
}
);
const Enter: NextPage = () => {
return (
<div>
<button>Email</button>
<button>Phone</button>
{button === Email ? (
<h1>Email Plz</h1>
) : (
<Suspense fallback="Loading a big component">
<Tokoopne1 />
</Suspense>
)
</div>
);
};
Next.js
에서 제공하는 <Suspense>
를 이용하면 컴포넌트 형식으로 로딩 화면을 구성할 수 있다.
참고
노마드 코더 - 캐럿마켓 클론코딩
Next.js Docs - dynamic-import#with-custom-loading-component