컴포넌트, 이미지 등 리소스가 필요해지는 순간까지 로딩을 미루는 것이다.
페이지를 처음 로드할 때 당장 필요하지 않은 리소스까지 한 번에 다 불러오게 되면 시간이 오래 걸리고 낭비의 가능성이 높기 때문이다.
React에선 Code-splitting을 통해 Lazy-loading을 한다. Code-splitting은 말 그대로 코드를 쪼개는 것이다. React App 전체가 단 하나의 번들로 컴파일된다면 App의 크기가 커질수록 로딩 속도가 느려지기 때문에 여러 번들로 나눠 각각의 번들을 필요한 순간에 불러오는 것이다.
React Component에 Lazy-loading을 적용하는 방법은 다음과 같다.
적용 전:
import OtherComponent from './OtherComponent';
function MyComponent() {
return (
<div>
<OtherComponent />
</div>
);
}
적용 후:
import React, { Suspense } from 'react';
const OtherComponent = React.lazy(() => import('./OtherComponent'));
function MyComponent() {
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
<OtherComponent />
</Suspense>
</div>
);
}
Lazy-loading을 적용하고자 하는 Component를 React.lazy()
안에서 import()
로 불러와 변수에 저장하고, <Suspense>
태그로 감싼 후, Component가 로드되기 전에 화면에 표시하고자 하는 내용을 fallback
에 담아주면 된다.
페이지 내부의 Modal 컴포넌트와 같은 요소에 Lazy-loading을 적용한다면, 사용자가 Modal을 클릭한 후 화면에 렌더되기 전까지 딜레이가 발생해 UX적으로 악영향을 끼친다.
Lazy-loading과 반대로 리소스를 미리 로드하는 것이다. 예를 들면 Modal이 존재하는 페이지에 진입했을 때, 페이지 로드가 완료되면 Modal 컴포넌트를 미리 로드해서 사용자가 Modal의 로딩 시간 이후에 Modal을 클릭하게 된다면 바로 보여줄 수 있도록 하는 것이다.
React는 자체적으로 preload 기능을 지원하지 않기 때문에 직접 구현해야 한다. 간단하지만 일회성인 방법과 조금 복잡하지만 코드 재사용이 가능한 방법 두 가지가 있다.
간단한 방법:
const modalPromise = import("./Modal");
const Modal = React.lazy(() => modalPromise);
이런 식으로 React.lazy()
를 호출하기 전에 import()
를 미리 실행하면 된다.
복잡한 방법은 팩토리 패턴을 사용해 Lazy-loading되는 Component에 preload 기능을 부여하는 함수를 만드는 것이다.
복잡한 방법:
// useLazyComponent.ts
import { lazy, ComponentType, LazyExoticComponent } from 'react';
export type ReactLazyFactory<T = any> = () => Promise<{ default: ComponentType<T> }>;
export type ComponentPreloadTuple<T = any> = [component: LazyExoticComponent<ComponentType<T>>, preloadFn: () => void];
export function getLazyComponentWithPreload<T = any>(componentPath: string): ComponentPreloadTuple<T>;
export function getLazyComponentWithPreload<T = any>(factory: ReactLazyFactory<T>): ComponentPreloadTuple<T>;
export function getLazyComponentWithPreload<T = any>(input: string | ReactLazyFactory<T>): ComponentPreloadTuple<T> {
const factory = () => (typeof input === 'string' ? import(input) : input());
return [lazy(factory), factory];
}
// MyComponent.tsx
import { Suspense, useEffect } from 'react';
import { getLazyComponentWithPreload } from './useLazyComponent';
const [Modal, preload] = getLazyComponentWithPreload(() => import('./Modal'));
function MyComponent() {
useEffect(() => {
preload();
}, []);
return (
<div>
<Suspense fallback={null}>
<Modal />
</Suspense>
</div>
);
}
이 방법의 장점은 preload를 필요로 하는 Lazy-loading Component가 많은 경우에 getLazyComponentWithPreload
함수를 재사용해 코드 효율성을 높일 수 있다는 것이다.
- Lazy-loading을 통해 초기 로딩 속도를 향상시킬 수 있다.
- Modal과 같은 Component에 Lazy-loading을 적용한다면 preload를 통해 UX를 개선할 수 있다.
- React는 자체적으로 preload를 지원하지 않으니 직접 구현해야 한다.
이해가 너무 잘 돼요! 잘 보고 갑니다~^_^✨