[React+TypeScript] Lazy-loading & preload로 Component 성능 최적화하기

olwooz·2022년 12월 11일
4

React

목록 보기
1/8

Lazy-loading이란?

컴포넌트, 이미지 등 리소스가 필요해지는 순간까지 로딩을 미루는 것이다.

Lazy-loading을 하는 이유

페이지를 처음 로드할 때 당장 필요하지 않은 리소스까지 한 번에 다 불러오게 되면 시간이 오래 걸리고 낭비의 가능성이 높기 때문이다.

React에서의 Lazy-loading

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에 담아주면 된다.

Lazy-loading의 단점

페이지 내부의 Modal 컴포넌트와 같은 요소에 Lazy-loading을 적용한다면, 사용자가 Modal을 클릭한 후 화면에 렌더되기 전까지 딜레이가 발생해 UX적으로 악영향을 끼친다.

해결 방안 - preload란?

Lazy-loading과 반대로 리소스를 미리 로드하는 것이다. 예를 들면 Modal이 존재하는 페이지에 진입했을 때, 페이지 로드가 완료되면 Modal 컴포넌트를 미리 로드해서 사용자가 Modal의 로딩 시간 이후에 Modal을 클릭하게 된다면 바로 보여줄 수 있도록 하는 것이다.

React에서의 preload

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 함수를 재사용해 코드 효율성을 높일 수 있다는 것이다.

세 줄 요약

  1. Lazy-loading을 통해 초기 로딩 속도를 향상시킬 수 있다.
  2. Modal과 같은 Component에 Lazy-loading을 적용한다면 preload를 통해 UX를 개선할 수 있다.
  3. React는 자체적으로 preload를 지원하지 않으니 직접 구현해야 한다.

2개의 댓글

comment-user-thumbnail
2022년 12월 12일

이해가 너무 잘 돼요! 잘 보고 갑니다~^_^✨

1개의 답글