태그
목차

lazy

lazy API 문서

생성일: 2024-01-30

수정일: 2024-01-30

lazy 를 사용하면 컴포넌트의 코드가 처음 렌더링될 때까지 로딩을 지연시킬 수 있다.

const SomeComponent = lazy(load);

레퍼런스

lazy(load)

컴포넌트 외부에서 lazy 를 호출하여 지연 로드된 React 컴포넌트를 선언한다:

import { lazy } from 'react';

const MarkdownPreview = lazy(() => import('./MarkdownPreview.js'));

파라미터

파라미터 설명
load Promise나 다른 thenable (then 메소드를 가진 Promise와 유사한 객체)을 반환하는 함수다. React는 컴포넌트가 처음 렌더링될 때까지 load 함수를 호출하지 않는다. React가 처음으로 load를 호출하면 해당 Promise가 리졸브될 때까지 기다린 다음, 리졸브된 값의 .default 를 React 컴포넌트로 렌더링한다. 리턴된 Promise과 Promise의 리졸브된 값은 모두 캐시되므로 React는 load 를 한 번 이상 호출하지 않는다. Promise가 거부되면 React는 거부된 이유를 처리할 가장 가까운 에러 바운더리에 전달한다.

리턴

lazy 는 트리에서 렌더링할 수 있는 React 컴포넌트를 리턴한다. lazy 컴포넌트의 코드가 로딩되는 동안에는 렌더링을 시도하면 서스펜드된다. 그동안 로딩 표시기를 표시하려면 <Suspense> 를 사용한다.

load 함수

파라미터

load 함수는 파라미터를 받지 않는다.

리턴

Promise나 다른 thenable (then 메소드를 가진 Promise와 유사한 객체)를 반환해야 한다. 이는 최종적으로 .default 프로퍼티가 함수, memo, 또는 forwardRef 컴포넌트와 같은 유효한 React 컴포넌트 타입인 객체로 리졸브되어야 한다.

사용법

서스펜스를 사용한 지연 로딩 컴포넌트

일반적으로 정적 import 선언을 사용하여 컴포넌트를 가져온다:

import MarkdownPreview from './MarkdownPreview.js';

MarkdownPreview 불러오기를 컴포넌트가 렌더링 될 때까지 연기하려면 import 를 다음과 같이 변경한다:

import { lazy } from 'react';

const MarkdownPreview = lazy(() => import('./MarkdownPreview.js'));

이 코드는 dynamic import() 를 기반으로 하며, 때문에 번들러나 프레임워크에서 지원이 필요할 수 있다. 이 패턴을 사용하려면 가져오는 lazy 컴포넌트가 default export를 사용해야 한다.

이제 컴포넌트의 코드가 온디맨드 방식으로 로드되므로 로드되는 동안 표시할 내용도 지정해야 한다. 지연 컴포넌트나 그 부모 컴포넌트를 <Suspense> 바운더리로 감싸면 된다:

<Suspense fallback={<Loading />}>
  <h2>Preview</h2>
  <MarkdownPreview />
</Suspense>

이 예제에서는 렌더링 전까지는 MarkdownPreview 의 코드가 로드되지 는다. MarkdownPreview 가 아직 로드되지 않은 경우, 그 자리에 Loading 컴포넌트가 표시된다.

import { useState, Suspense, lazy } from 'react';
import Loading from './Loading.js';

const MarkdownPreview = lazy(() =>
  delayForDemo(import('./MarkdownPreview.js')),
);

export default function MarkdownEditor() {
  const [showPreview, setShowPreview] = useState(false);
  const [markdown, setMarkdown] = useState('Hello, **world**!');
  return (
    <>
      <textarea
        value={markdown}
        onChange={(e) => setMarkdown(e.target.value)}
      />
      <label>
        <input
          type="checkbox"
          checked={showPreview}
          onChange={(e) => setShowPreview(e.target.checked)}
        />
        Show preview
      </label>
      <hr />
      {showPreview && (
        <Suspense fallback={<Loading />}>
          <h2>Preview</h2>
          <MarkdownPreview markdown={markdown} />
        </Suspense>
      )}
    </>
  );
}

// 로딩 상태를 확인할 수 있도록 2초간 지연을 추가한다.
function delayForDemo(promise) {
  return new Promise((resolve) => {
    setTimeout(resolve, 2000);
  }).then(() => promise);
}
export default function Loading() {
  return (
    <p>
      <i>Loading...</i>
    </p>
  );
}
import { Remarkable } from 'remarkable';

const md = new Remarkable();

export default function MarkdownPreview({ markdown }) {
  return (
    <div
      className="content"
      dangerouslySetInnerHTML={{ __html: md.render(markdown) }}
    />
  );
}

위의 데모는 인위적인 지연되어 로드된다. 다음에 체크박스를 선택 해제했다가 다시 선택하면 Preview 가 캐시되므로 로딩 상태가 표시되지 않는다.

트러블슈팅

lazy 컴포넌트의 상태가 예기치 않게 초기화되는 경우

다른 컴포넌트 안에 lazy 컴포넌트를 선언하지 않아야 한다:

import { lazy } from 'react';

function Editor() {
  // 🔴 Bad: 이렇게 하면 리렌더링 시 모든 상태가 초기화된다.
  const MarkdownPreview = lazy(() => import('./MarkdownPreview.js'));
  // ...
}

대신 항상 모듈의 최상위 레벨에서 선언해야 한다:

import { lazy } from 'react';

// ✅ Good: 컴포넌트 외부에 lazy 컴포넌트 선언하기
const MarkdownPreview = lazy(() => import('./MarkdownPreview.js'));

function Editor() {
  // ...
}