lazy๋Š” ๋กœ๋”ฉ ์ค‘์ธ ์ปดํฌ๋„ŒํŠธ ์ฝ”๋“œ๊ฐ€ ์ฒ˜์Œ์œผ๋กœ ๋ Œ๋”๋ง ๋  ๋•Œ๊นŒ์ง€ ์—ฐ๊ธฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

const SomeComponent = lazy(load)

๋ ˆํผ๋Ÿฐ์Šค

lazy(load)

lazy๋ฅผ ์ด์šฉํ•˜์—ฌ ๋กœ๋”ฉํ•˜๋Š” React ์ปดํฌ๋„ŒํŠธ๋ฅผ ์„ ์–ธํ•˜๋ ค๋ฉด ์ปดํฌ๋„ŒํŠธ ์™ธ๋ถ€์—์„œ lazy๋ฅผ ํ˜ธ์ถœํ•˜์„ธ์š”.

import { lazy } from 'react';

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

์•„๋ž˜์—์„œ ๋” ๋งŽ์€ ์˜ˆ์‹œ๋ฅผ ํ™•์ธํ•˜์„ธ์š”.

๋งค๊ฐœ๋ณ€์ˆ˜

  • load: Promise ๋˜๋Š” ๋˜ ๋‹ค๋ฅธ thenable (then ๋ฉ”์„œ๋“œ๊ฐ€ ์žˆ๋Š” Promise ์œ ์‚ฌ ๊ฐ์ฒด)์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค. React๋Š” ๋ฐ˜ํ™˜๋œ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ฒ˜์Œ ๋ Œ๋”๋งํ•˜๋ ค๊ณ  ํ•  ๋•Œ๊นŒ์ง€ load๋ฅผ ํ˜ธ์ถœํ•˜์ง€ ์•Š์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. React๋Š” ๋จผ์ € load๋ฅผ ์‹คํ–‰ํ•œ ํ›„ load๊ฐ€ ์ดํ–‰๋  ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ ธ๋‹ค๊ฐ€ ์ดํ–‰๋œ ๊ฐ’์˜ .default๋ฅผ React ์ปดํฌ๋„ŒํŠธ๋กœ ๋ Œ๋”๋งํ•ฉ๋‹ˆ๋‹ค. ๋ฐ˜ํ™˜๋œ Promise์™€ Promise์˜ ์ดํ–‰๋œ ๊ฐ’์ด ๋ชจ๋‘ ์บ์‹œ ๋˜๋ฏ€๋กœ React๋Š” load๋ฅผ ๋‘ ๋ฒˆ ์ด์ƒ ํ˜ธ์ถœํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. Promise๊ฐ€ ๊ฑฐ๋ถ€ํ•˜๋ฉด React๋Š” ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด Error Boundary๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด Error Boundary์— ๋Œ€ํ•œ ๊ฑฐ๋ถ€ ์‚ฌ์œ ๋ฅผ throw ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๋ฐ˜ํ™˜๊ฐ’

lazy๋Š” ํŠธ๋ฆฌ์— ๋ Œ๋”๋งํ•  ์ˆ˜ ์žˆ๋Š” React ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ์ปดํฌ๋„ŒํŠธ์˜ ์ฝ”๋“œ๊ฐ€ ์—ฌ์ „ํžˆ ๋กœ๋“œ๋˜๋Š” ๋™์•ˆ ๋ Œ๋”๋ง์„ ์‹œ๋„ํ•˜๋ฉด ์ผ์‹œ ์ค‘์ง€๋ฉ๋‹ˆ๋‹ค. ๋กœ๋”ฉ ์ค‘์— loading indicator๋ฅผ ํ‘œ์‹œํ•˜๋ ค๋ฉด <Suspense>๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.


load ํ•จ์ˆ˜

๋งค๊ฐœ๋ณ€์ˆ˜

load๋Š” ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์ˆ˜์‹ ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๋ฐ˜ํ™˜๊ฐ’

Promise ๋˜๋Š” ๋‹ค๋ฅธ thenable (then ๋ฉ”์„œ๋“œ๊ฐ€ ์žˆ๋Š” Promise ์œ ์‚ฌ ๊ฐ์ฒด)์„ ๋ฐ˜ํ™˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ฒฐ๊ตญ .default ํ”„๋กœํผํ‹ฐ๊ฐ€ ํ•จ์ˆ˜, memo ๋˜๋Š” forwardRef ์ปดํฌ๋„ŒํŠธ์™€ ๊ฐ™์€ ์œ ํšจํ•œ React ์ปดํฌ๋„ŒํŠธ ์œ ํ˜•์ธ ๊ฐ์ฒด๋กœ ํ•ด์„๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.


์‚ฌ์šฉ๋ฒ•

Suspense์™€ Lazy-loading ์ปดํฌ๋„ŒํŠธ

์ผ๋ฐ˜์ ์œผ๋กœ ์ •์  import ์„ ์–ธ์œผ๋กœ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.

import MarkdownPreview from './MarkdownPreview.js';

ํ•ด๋‹น ์ปดํฌ๋„ŒํŠธ ์ฝ”๋“œ๊ฐ€ ์ฒ˜์Œ ๋ Œ๋”๋ง ๋  ๋•Œ๊นŒ์ง€ ๋กœ๋“œํ•˜๋Š” ๊ฒƒ์„ ์—ฐ๊ธฐํ•˜๋ ค๋ฉด import๋ฅผ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋Œ€์ฒดํ•ฉ๋‹ˆ๋‹ค.

import { lazy } from 'react';

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

์œ„์˜ ์ฝ”๋“œ๋Š” ๋™์  import()์— ์˜์กดํ•˜๋ฏ€๋กœ ๋ฒˆ๋“ค๋Ÿฌ ๋˜๋Š” ํ”„๋ ˆ์ž„์›Œํฌ์˜ ์ง€์›์ด ํ•„์š”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ํŒจํ„ด์„ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ์ž„ํฌํŠธํ•˜๋ ค๋Š” lazy ์ปดํฌ๋„ŒํŠธ๊ฐ€ default ๋‚ด๋ณด๋‚ด๊ธฐ๋กœ ๋‚ด๋ณด๋‚ด์ ธ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์ด์ œ ์š”์ฒญ์— ๋”ฐ๋ผ ์ปดํฌ๋„ŒํŠธ์˜ ์ฝ”๋“œ๊ฐ€ ๋กœ๋“œ๋˜๋ฏ€๋กœ ๋กœ๋“œํ•˜๋Š” ๋™์•ˆ ํ‘œ์‹œํ•  ํ•ญ๋ชฉ๋„ ์ง€์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. lazy ์ปดํฌ๋„ŒํŠธ ๋˜๋Š” ํ•ด๋‹น ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ ์ค‘ ํ•˜๋‚˜๋ฅผ <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>
      )}
    </>
  );
}

// ๋กœ๋”ฉ ์ƒํƒœ๋ฅผ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด, ํ…Œ์ŠคํŠธ๋ฅผ ์œ„ํ•œ ์ง€์—ฐ๊ฐ’์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
function delayForDemo(promise) {
  return new Promise(resolve => {
    setTimeout(resolve, 2000);
  }).then(() => promise);
}

์ด ๋ฐ๋ชจ๋Š” ์ธ์œ„์ ์ธ ์ง€์—ฐ์œผ๋กœ ๋กœ๋“œ๋ฉ๋‹ˆ๋‹ค. ๋‹ค์Œ์— ์ฒดํฌ๋ฐ•์Šค๋ฅผ ์„ ํƒ ํ•ด์ œํ•˜๊ณ  ๋‹ค์‹œ ์„ ํƒํ•˜๋ฉด Preview๊ฐ€ ์บ์‹œ ๋˜์–ด ๋กœ๋”ฉ ์ƒํƒœ๊ฐ€ ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋กœ๋”ฉ ์ƒํƒœ๋ฅผ ๋‹ค์‹œ ๋ณด๋ ค๋ฉด ์ƒŒ๋“œ๋ฐ•์Šค์—์„œ โ€œResetโ€์„ ํด๋ฆญํ•˜์„ธ์š”.

Suspense๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋กœ๋”ฉ ์ƒํƒœ๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ์ž์„ธํžˆ ์•Œ์•„๋ณด์„ธ์š”.


๋ฌธ์ œ ํ•ด๊ฒฐ

lazy ์ปดํฌ๋„ŒํŠธ์˜ ์ƒํƒœ๊ฐ€ ์˜๋„์น˜ ์•Š๊ฒŒ ์žฌ์„ค์ •๋ฉ๋‹ˆ๋‹ค.

lazy ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋‹ค๋ฅธ ์ปดํฌ๋„ŒํŠธ ๋‚ด๋ถ€์—์„œ ์„ ์–ธํ•˜์ง€ ๋งˆ์„ธ์š”.

import { lazy } from 'react';

function Editor() {
// ๐Ÿ”ด ์ž˜๋ชป๋œ ๋ฐฉ๋ฒ•: ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋‹ค์‹œ ๋ Œ๋”๋งํ•  ๋•Œ ๋ชจ๋“  ์ƒํƒœ๊ฐ€ ์žฌ์„ค์ •๋ฉ๋‹ˆ๋‹ค.
const MarkdownPreview = lazy(() => import('./MarkdownPreview.js'));
// ...
}

๋Œ€์‹  ํ•ญ์ƒ ๋ชจ๋“ˆ์˜ ์ตœ์ƒ์œ„ ์ˆ˜์ค€์—์„œ ์„ ์–ธํ•˜์„ธ์š”.

import { lazy } from 'react';

// โœ… ์˜ฌ๋ฐ”๋ฅธ ๋ฐฉ๋ฒ•: lazy ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ปดํฌ๋„ŒํŠธ ์™ธ๋ถ€์— ์„ ์–ธํ•ฉ๋‹ˆ๋‹ค.
const MarkdownPreview = lazy(() => import('./MarkdownPreview.js'));

function Editor() {
// ...
}