lazy

김동현·2026년 3월 17일

lazy

소개

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

const SomeComponent = lazy(load)

쉽게 말해서, 컴포넌트가 실제로 화면에 필요할 때까지 해당 컴포넌트의 코드를 불러오지 않고 기다리게 하는 거예요. 이렇게 하면 앱의 초기 로딩 속도를 빠르게 할 수 있죠!


목차


레퍼런스

lazy(load)

지연 로딩되는 React 컴포넌트를 선언하려면 컴포넌트 바깥에서 lazy를 호출하세요:

// 예시
import { lazy } from 'react';

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

아래에서 더 많은 예시를 확인해보세요.

매개변수

  • load: Promise 또는 다른 thenable(.then 메서드를 가진 Promise와 비슷한 객체)을 반환하는 함수예요. React는 반환된 컴포넌트를 처음 렌더링하려고 시도할 때까지 load를 호출하지 않아요. React가 load를 처음 호출한 후에는 resolve될 때까지 기다렸다가, resolve된 값의 .default를 React 컴포넌트로 렌더링해요. 반환된 Promise와 Promise의 resolve된 값 모두 캐시되기 때문에 React는 load를 두 번 이상 호출하지 않아요. Promise가 reject되면, React는 가장 가까운 Error Boundary가 처리할 수 있도록 reject 이유를 throw해요.

반환값

lazy는 트리에서 렌더링할 수 있는 React 컴포넌트를 반환해요. lazy 컴포넌트의 코드가 아직 로딩 중일 때 렌더링을 시도하면 suspend(일시 중단)돼요. 로딩하는 동안 로딩 인디케이터를 표시하려면 <Suspense>를 사용하세요.


load 함수

매개변수

load는 어떤 매개변수도 받지 않아요.

반환값

Promise 또는 다른 thenable(.then 메서드를 가진 Promise와 비슷한 객체)을 반환해야 해요. 최종적으로 .default 프로퍼티가 유효한 React 컴포넌트 타입인 객체로 resolve되어야 해요. 함수, memo, 또는 forwardRef 컴포넌트 같은 것들이 될 수 있어요.


사용법

Suspense와 함께 컴포넌트 지연 로딩하기

보통은 정적 import 선언으로 컴포넌트를 import해요:

import MarkdownPreview from './MarkdownPreview.js';

이 컴포넌트의 코드를 처음 렌더링될 때까지 로딩을 지연시키려면, 이 import를 다음과 같이 바꾸세요:

import { lazy } from 'react';

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

이 코드는 동적 import()에 의존하는데, 번들러나 프레임워크의 지원이 필요할 수 있어요. 이 패턴을 사용하려면 import하는 lazy 컴포넌트가 default export로 내보내져 있어야 해요.

이제 컴포넌트의 코드가 필요할 때 로드되니까, 로딩하는 동안 무엇을 표시할지도 지정해야 해요. lazy 컴포넌트나 그 부모 중 하나를 <Suspense> 경계로 감싸면 돼요:

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

이 예시에서 MarkdownPreview의 코드는 렌더링하려고 시도할 때까지 로드되지 않아요. MarkdownPreview가 아직 로드되지 않았으면, 그 자리에 Loading이 표시될 거예요. 체크박스를 클릭해보세요:

// src/App.js
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>
      )}
    </>
  );
}

// Add a fixed delay so you can see the loading state
function delayForDemo(promise) {
  return new Promise(resolve => {
    setTimeout(resolve, 2000);
  }).then(() => promise);
}
// src/Loading.js
export default function Loading() {
  return <p><i>Loading...</i></p>;
}
// src/MarkdownPreview.js
import { Remarkable } from 'remarkable';

const md = new Remarkable();

export default function MarkdownPreview({ markdown }) {
  return (
    <div
      className="content"
      dangerouslySetInnerHTML={{__html: md.render(markdown)}}
    />
  );
}
// package.json
{
  "dependencies": {
    "immer": "1.7.3",
    "react": "latest",
    "react-dom": "latest",
    "react-scripts": "latest",
    "remarkable": "2.0.1"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test --env=jsdom",
    "eject": "react-scripts eject"
  }
}
label {
  display: block;
}

input, textarea {
  margin-bottom: 10px;
}

body {
  min-height: 200px;
}

이 데모는 인위적인 지연이 적용되어 있어요. 다음번에 체크박스를 해제했다가 다시 체크하면 Preview가 캐시되어 있기 때문에 로딩 상태가 나타나지 않을 거예요. 로딩 상태를 다시 보려면 샌드박스에서 "Reset"을 클릭하세요.

Suspense로 로딩 상태를 관리하는 방법에 대해 더 알아보세요.


문제 해결

lazy 컴포넌트의 state가 예상치 못하게 리셋돼요

lazy 컴포넌트를 다른 컴포넌트 안에서 선언하지 마세요:

// 예시
import { lazy } from 'react';

function Editor() {
  // 🔴 나쁜 예: 리렌더링할 때마다 모든 state가 리셋돼요
  const MarkdownPreview = lazy(() => import('./MarkdownPreview.js'));
  // ...
}

이렇게 하면 Editor 컴포넌트가 리렌더링될 때마다 새로운 MarkdownPreview 컴포넌트가 생성되기 때문에, 해당 컴포넌트의 state가 매번 리셋되는 문제가 발생해요.

대신, 항상 모듈의 최상위 레벨에서 선언하세요:

// 예시
import { lazy } from 'react';

// ✅ 좋은 예: lazy 컴포넌트를 컴포넌트 바깥에서 선언해요
const MarkdownPreview = lazy(() => import('./MarkdownPreview.js'));

function Editor() {
  // ...
}

이렇게 모듈 최상위에서 선언하면 MarkdownPreview는 한 번만 생성되고, 리렌더링이 발생해도 동일한 컴포넌트를 재사용하게 돼요. 그래서 state도 유지되고요!


사이트맵

모든 문서 페이지 개요

profile
프론트에_가까운_풀스택_개발자

0개의 댓글