๐Ÿ’ป ์ฝ”๋“œ ๋ถ„ํ• (feat. ๋ฆฌ์•กํŠธ ๊ณต์‹๋ฌธ์„œ)

waterglassesยท2022๋…„ 10์›” 5์ผ
0

TIL

๋ชฉ๋ก ๋ณด๊ธฐ
39/50
post-thumbnail

โš ๏ธ ์ •๋ฆฌํ•œ ๋‚ด์šฉ์€ ์˜คํƒ€๋‚˜ ์ž˜๋ชป๋œ ์ •๋ณด๊ฐ€ ์žˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋Œ“๊ธ€๋กœ ์•Œ๋ ค์ฃผ์‹œ๋ฉด ๊ฐ์‚ฌํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

๐Ÿ“ƒ ์˜ค๋Š˜ ๊ณต๋ถ€ํ•œ ๊ฒƒ

๋ฒˆ๋“ค๋ง

๋Œ€๋ถ€๋ถ„ ์•ฑ๋“ค์€ Webpack, Rollup, Browserify๊ฐ™์€ ํˆด์„ ์‚ฌ์šฉํ•˜์—ฌ ์—ฌ๋Ÿฌ ํŒŒ์ผ์„ ํ•˜๋‚˜๋กœ ๋ณ‘ํ•ฉํ•œ ๋ฒˆ๋“ค๋œ ํŒŒ์ผ์„ ์›น ํŽ˜์ด์ง€์— ํฌํ•จํ•˜์—ฌ ํ•œ ๋ฒˆ์— ์ „์ฒด ์•ฑ์„ ๋กœ๋“œ ํ•  ์ˆ˜ ์žˆ๋‹ค.

์ฝ”๋“œ ๋ถ„ํ• 

๋ฒˆ๋“ค์ด ๊ฑฐ๋Œ€ํ•ด์ง€๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•œ ์ข‹์€ ํ•ด๊ฒฐ๋ฐฉ๋ฒ•์€ ๋ฒˆ๋“ค์„ ๋‚˜๋ˆ„๋Š” ๊ฒƒ์ด๋‹ค.

์ฝ”๋“œ ๋ถ„ํ• ์€ ๋Ÿฐํƒ€์ž„์— ์—ฌ๋Ÿฌ ๋ฒˆ๋“ค์„ ๋™์ ์œผ๋กœ ๋งŒ๋“ค๊ณ  ๋ถˆ๋Ÿฌ์˜ค๋Š” ๊ฒƒ์œผ๋กœ Webpack, Rollup, Browserify(factor-buldle) ๊ฐ™์€ ๋ฒˆ๋“ค๋Ÿฌ๊ฐ€ ์ง€์›ํ•˜๋Š” ๊ธฐ๋Šฅ์ด๋‹ค.

์ฝ”๋“œ ๋ถ„ํ• ์€ ์—ฌ๋Ÿฌ๋ถ„์˜ ์•ฑ์„ ์ง€์—ฐ ๋กœ๋”ฉํ•˜๊ฒŒ ๋„์™€์ฃผ๊ณ  ์•ฑ ์‚ฌ์šฉ์ž์—๊ฒŒ ํš๊ธฐ์ ์ธ ์„ฑ๋Šฅ ํ–ฅ์ƒ์„ ํ•˜๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.

import()

์•ฑ์— ์ฝ”๋“œ ๋ถ„ํ• ์„ ๋„์ž…ํ•˜๋Š” ๊ฐ€์žฅ ์ข‹์€ ๋ฐฉ๋ฒ•์€ ๋™์  import() ๋ฌธ๋ฒ•์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

before

import { add } from './math';

console.log(add(16, 26));

after

import("./math").then(math => {
  console.log(math.add(16, 26));
});

์›นํŒฉ์ด import๋ฅผ ๋งŒ๋‚˜๋ฉด ์•ฑ์˜ ์ฝ”๋“œ๋ฅผ ๋ถ„ํ• ํ•œ๋‹ค. cra์™€ next.js์—์„œ๋Š” ๊ธฐ๋ณธ์œผ๋กœ ์›นํŒฉ์ด ๊ตฌ์„ฑ๋˜์–ด ์žˆ๋‹ค.

์›นํŒฉ์˜ Code Splitting

์›นํŒฉ์˜ ๊ฐ€์žฅ ๊ฐ•๋ ฅํ•œ ๊ธฐ๋Šฅ ์ค‘ ํ•˜๋‚˜์ด๋‹ค. ์š”์ฒญ ์‹œ ๋˜๋Š” ๋ณ‘๋ ฌ๋กœ ๋‹ค์–‘ํ•œ ๋ฒˆ๋“ค๋กœ ์ฝ”๋“œ๋ฅผ ๋ถ„ํ• ํ•  ์ˆ˜ ์žˆ๋‹ค.

์ฝ”๋“œ๋ถ„ํ• ์˜ ์ผ๋ฐ˜์ ์ธ ์„ธ ๊ฐ€์ง€ ์ ‘๊ทผ ๋ฐฉ์‹

1. Entry Points

entry ์„ค์ •์„ ์‚ฌ์šฉํ•˜์—ฌ ์ฝ”๋“œ๋ฅผ ์ˆ˜๋™์œผ๋กœ ๋ถ„ํ• ํ•ฉ๋‹ˆ๋‹ค.

  1. Prevent Duplication

Entry dependencies ๋˜๋Š” SplitChunksPlugin์„ ์‚ฌ์šฉํ•˜์—ฌ ์ค‘๋ณต ์ฒญํฌ๋ฅผ ์ œ๊ฑฐํ•˜๊ณ  ์ฒญํฌ๋ฅผ ๋ถ„ํ• ํ•ฉ๋‹ˆ๋‹ค.

  1. Dynamic Imports

๋ชจ๋“ˆ ๋‚ด์—์„œ ์ธ๋ผ์ธ ํ•จ์ˆ˜ ํ˜ธ์ถœ์„ ํ†ตํ•ด ์ฝ”๋“œ๋ฅผ ๋ถ„ํ• ํ•ฉ๋‹ˆ๋‹ค.

React.lazy

React.lazy ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋™์  Import๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ Œ๋”๋ง ํ•  ์ˆ˜ ์žˆ๋‹ค.

before

import OtherComponent from './OtherComponent';

after

const OtherComponent = React.lazy(() => import('./OtherComponent'));
// React ์ปดํฌ๋„ŒํŠธ๋ฅผ default export๋กœ ๊ฐ€์ง„ ๋ชจ๋“ˆ ๊ฐ์ฒด๊ฐ€ ์ดํ–‰๋˜๋Š” Promise๋ฅผ ๋ฐ˜ํ™˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

MyComponent๊ฐ€ ๋ Œ๋”๋ง ๋  ๋•Œ OtherComponent๋ฅผ ํฌํ•จํ•œ ๋ฒˆ๋“ค์„ ์ž๋™์œผ๋กœ ๋ถˆ๋Ÿฌ์˜จ๋‹ค.

lazy ์ปดํฌ๋„ŒํŠธ๋Š” Suspense ์ปดํฌ๋„ŒํŠธ ํ•˜์œ„์—์„œ ๋ Œ๋”๋ง๋˜์–ด์•ผ ํ•œ๋‹ค.

import React, { Suspense } from 'react';

const OtherComponent = React.lazy(() => import('./OtherComponent'));

function MyComponent() {
  return (
    <div>
      <Suspense fallback={<Spinner />}>
        <OtherComponent />
      </Suspense>
    </div>
  );
}

Suspense

Suspense ๋Š” lazy ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋กœ๋“œ๋˜๊ธธ ๊ธฐ๋‹ค๋ฆฌ๋Š” ๋™์•ˆ ๋กœ๋”ฉ ํ™”๋ฉด๊ณผ ๊ฐ™์€ ์˜ˆ๋น„ ์ปจํ…์ธ ๋ฅผ ๋ณด์—ฌ์ค„ ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค€๋‹ค. fallback ย prop์€ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋กœ๋“œ๋  ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ฆฌ๋Š” ๋™์•ˆ ๋ Œ๋”๋งํ•˜๋ ค๋Š” React ์—˜๋ฆฌ๋จผํŠธ๋ฅผ ๋ฐ›์•„๋“ค์ธ๋‹ค.ย Suspenseย ์ปดํฌ๋„ŒํŠธ๋Š” lazy ์ปดํฌ๋„ŒํŠธ๋ฅผ ๊ฐ์‹ธ๊ณ  ํ•˜๋‚˜์˜ย Suspenseย ์ปดํฌ๋„ŒํŠธ๋กœ ์—ฌ๋Ÿฌ lazy ์ปดํฌ๋„ŒํŠธ๋ฅผ ๊ฐ์Œ€ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

Avoiding fallbacks

import React, { Suspense } from 'react';
import Tabs from './Tabs';
import Glimmer from './Glimmer';

const Comments = React.lazy(() => import('./Comments'));
const Photos = React.lazy(() => import('./Photos'));

function MyComponent() {
  const [tab, setTab] = React.useState('photos');
  
  function handleTabSelect(tab) {
    setTab(tab);
  };

  return (
    <div>
      <Tabs onTabSelect={handleTabSelect} />
      <Suspense fallback={<Glimmer />}>
        {tab === 'photos' ? <Photos /> : <Comments />}
      </Suspense>
    </div>
  );
}

์œ„์˜ ์˜ˆ์‹œ์˜ ๊ฒฝ์šฐ ํƒญ์ด 'photos'๋กœ ๋ณ€๊ฒฝ 'comments'๋ณ€๊ฒฝ ๋  ๋•Œ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ค€๋น„๋˜์ง€ ์•Š์•˜๋‹ค๋ฉด ์‚ฌ์šฉ์ž๋Š” ํฌ๋ฏธํ•˜๊ฒŒ ๋ณด์ธ๋‹ค. ํ•˜์ง€๋งŒ ํฌ๋ฏธํ•˜๊ฒŒ ๋ณด์ด๋Š” ๊ฒƒ๋ณด๋‹ค๋Š” ์ด์ „ UI๋ฅผ ๋ณด์—ฌ์ฃผ๋Š” ๊ฒƒ์ด ๋‚ซ๋‹ค.

๋”ฐ๋ผ์„œ ์•„๋ž˜์ฒ˜๋Ÿผ startTransition API๋ฅผ ์‚ฌ์šฉํ•ด ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ค€๋น„๊ฐ€ ๋˜๋ฉด switchํ•˜๋ผ๊ณ  ์•Œ๋ฆฌ๋Š” ์ฝ”๋“œ๋ฅผ ๋„ฃ์–ด์ฃผ๋ฉด ๋œ๋‹ค.

function handleTabSelect(tab) {
  startTransition(() => {
    setTab(tab);
  });
}

React.startTransition

React18์— ๋„์ž…๋œ ์ƒˆ๋กœ์šด ๋™์‹œ ๊ธฐ๋Šฅ์ด๋‹ค. React์— ์ค‘๋‹จ๋˜๊ฑฐ๋‚˜ ์ด๋ฏธ ๋ณด์ด๋Š” ์ฝ˜ํ…์ธ ์— ๋Œ€ํ•ด suspense fallback์œผ๋กœ ๋Œ์•„๊ฐ€๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค.

Error boundaries

๋„คํŠธ์›Œํฌ ์žฅ์•  ๊ฐ™์€ ์ด์œ ๋กœ ๋‹ค๋ฅธ ๋ชจ๋“ˆ์„ ๋กœ๋“œ์— ์‹คํŒจํ•  ๊ฒฝ์šฐ ์—๋Ÿฌ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋‹ค. Error Boundary๋ฅผ ๋งŒ๋“ค๊ณ  lazy ์ปดํฌ๋„ŒํŠธ๋ฅผ ๊ฐ์‹ธ๋ฉด ๋„คํŠธ์›Œํฌ ์žฅ์• ๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ ์—๋Ÿฌ๋ฅผ ํ‘œ์‹œํ•  ์ˆ˜ ์žˆ๋‹ค.

import React, { Suspense } from 'react';
import MyErrorBoundary from './MyErrorBoundary';

const OtherComponent = React.lazy(() => import('./OtherComponent'));
const AnotherComponent = React.lazy(() => import('./AnotherComponent'));

const MyComponent = () => (
  <div>
    <MyErrorBoundary>
      <Suspense fallback={<div>Loading...</div>}>
        <section>
          <OtherComponent />
          <AnotherComponent />
        </section>
      </Suspense>
    </MyErrorBoundary>
  </div>
);

Route-based code splitting

๋ฒˆ๋“ค์„ ๊ท ๋“ฑํ•˜๊ฒŒ ๋ถ„๋ฐฐํ•  ์ˆ˜ ์žˆ๋„๋ก ๋ผ์šฐํŠธ ์„ค์ •ํ•œ๋‹ค. ์›น ํŽ˜์ด์ง€๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์‹œ๊ฐ„์€ ํŽ˜์ด์ง€ ์ „ํ™˜์— ์–ด๋Š ์ •๋„ ๋ฐœ์ƒํ•˜๋ฉฐ ๋Œ€๋ถ€๋ถ„ ํŽ˜์ด์ง€๋ฅผ ํ•œ๋ฒˆ์— ๋ Œ๋”๋งํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์‚ฌ์šฉ์ž๊ฐ€ ํŽ˜์ด์ง€๋ฅผ ๋ Œ๋”๋งํ•˜๋Š” ๋™์•ˆ ๋‹ค๋ฅธ ์š”์†Œ์™€ ์ƒํ˜ธ์ž‘์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค.

import React, { Suspense, lazy } from 'react';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';

const Home = lazy(() => import('./routes/Home'));
const About = lazy(() => import('./routes/About'));

const App = () => (
  <Router>
    <Suspense fallback={<div>Loading...</div>}>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
      </Routes>
    </Suspense>
  </Router>
);

React.lazy๋ฅผย React Router ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ๋ผ์šฐํŠธ ๊ธฐ๋ฐ˜ ์ฝ”๋“œ ๋ถ„ํ• ์„ ์„ค์ •

Named Exports

React.lazy ๋Š” ํ˜„์žฌ default exports๋งŒ ์ง€์›ํ•œ๋‹ค.
named exports๋ฅผ ์‚ฌ์šฉํ•˜๊ณ ์ž ํ•œ๋‹ค๋ฉด default๋กœ ์ด๋ฆ„์„ ์žฌ์ •์˜ํ•œ ์ค‘๊ฐ„ ๋ชจ๋“ˆ์„ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด tree shaking์ด ๊ณ„์† ๋™์ž‘ํ•˜๊ณ  ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ์ปดํฌ๋„ŒํŠธ๋Š” ๊ฐ€์ ธ์˜ค์ง€ ์•Š๋Š”๋‹ค.

๐Ÿ”ฅ ๋Š๋‚€์ 

code splitting ์ ์šฉ ์‚ฌ๋ก€๋ฅผ ์ฐพ์•„๋ณด๊ณ  ์‹ค์ œ๋กœ ๋ฒˆ๋“ค ์‚ฌ์ด์ฆˆ๊ฐ€ ๊ฐœ์„ ๋˜๋Š” ๊ฒƒ์„ lighthouse ํผํฌ๋จผ์Šค ์ ์ˆ˜๋กœ๋„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค.
๋ฌผ๋ก  ์ ‘๊ทผ์„ฑ ์ ์ˆ˜๋Š” ์ €ํ•˜๋  ์ˆ˜ ์žˆ์ง€๋งŒ ๊ทธ์— ๋น„ํ•ด ํผํฌ๋จผ์Šค ์ˆ˜์น˜๊ฐ€ ์›”๋“ฑํžˆ ํ–ฅ์ƒํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— code splitting์˜ ์ค‘์š”์„ฑ์„ ์•Œ๊ฒŒ ๋˜์—ˆ๋‹ค.

Refer

profile
๋งค ์ˆœ๊ฐ„ ์„ฑ์žฅํ•˜๋Š” ๊ฐœ๋ฐœ์ž๊ฐ€ ๋˜๋ ค๊ณ  ๋…ธ๋ ฅํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

0๊ฐœ์˜ ๋Œ“๊ธ€