โ ๏ธ ์ ๋ฆฌํ ๋ด์ฉ์ ์คํ๋ ์๋ชป๋ ์ ๋ณด๊ฐ ์์ ์ ์์ต๋๋ค. ๋๊ธ๋ก ์๋ ค์ฃผ์๋ฉด ๊ฐ์ฌํ๊ฒ ์ต๋๋ค.
๋๋ถ๋ถ ์ฑ๋ค์ Webpack, Rollup, Browserify๊ฐ์ ํด์ ์ฌ์ฉํ์ฌ ์ฌ๋ฌ ํ์ผ์ ํ๋๋ก ๋ณํฉํ ๋ฒ๋ค๋ ํ์ผ์ ์น ํ์ด์ง์ ํฌํจํ์ฌ ํ ๋ฒ์ ์ ์ฒด ์ฑ์ ๋ก๋ ํ ์ ์๋ค.
๋ฒ๋ค์ด ๊ฑฐ๋ํด์ง๋ ๊ฒ์ ๋ฐฉ์งํ๊ธฐ ์ํ ์ข์ ํด๊ฒฐ๋ฐฉ๋ฒ์ ๋ฒ๋ค์ ๋๋๋ ๊ฒ์ด๋ค.
์ฝ๋ ๋ถํ ์ ๋ฐํ์์ ์ฌ๋ฌ ๋ฒ๋ค์ ๋์ ์ผ๋ก ๋ง๋ค๊ณ ๋ถ๋ฌ์ค๋ ๊ฒ์ผ๋ก Webpack, Rollup, Browserify(factor-buldle) ๊ฐ์ ๋ฒ๋ค๋ฌ๊ฐ ์ง์ํ๋ ๊ธฐ๋ฅ์ด๋ค.
์ฝ๋ ๋ถํ ์ ์ฌ๋ฌ๋ถ์ ์ฑ์ ์ง์ฐ ๋ก๋ฉํ๊ฒ ๋์์ฃผ๊ณ ์ฑ ์ฌ์ฉ์์๊ฒ ํ๊ธฐ์ ์ธ ์ฑ๋ฅ ํฅ์์ ํ๊ฒ ํฉ๋๋ค.
์ฑ์ ์ฝ๋ ๋ถํ ์ ๋์
ํ๋ ๊ฐ์ฅ ์ข์ ๋ฐฉ๋ฒ์ ๋์ 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์์๋ ๊ธฐ๋ณธ์ผ๋ก ์นํฉ์ด ๊ตฌ์ฑ๋์ด ์๋ค.
์นํฉ์ ๊ฐ์ฅ ๊ฐ๋ ฅํ ๊ธฐ๋ฅ ์ค ํ๋์ด๋ค. ์์ฒญ ์ ๋๋ ๋ณ๋ ฌ๋ก ๋ค์ํ ๋ฒ๋ค๋ก ์ฝ๋๋ฅผ ๋ถํ ํ ์ ์๋ค.
์ฝ๋๋ถํ ์ ์ผ๋ฐ์ ์ธ ์ธ ๊ฐ์ง ์ ๊ทผ ๋ฐฉ์
1. Entry Points
entry
์ค์ ์ ์ฌ์ฉํ์ฌ ์ฝ๋๋ฅผ ์๋์ผ๋ก ๋ถํ ํฉ๋๋ค.
Entry dependencies
๋๋ SplitChunksPlugin
์ ์ฌ์ฉํ์ฌ ์ค๋ณต ์ฒญํฌ๋ฅผ ์ ๊ฑฐํ๊ณ ์ฒญํฌ๋ฅผ ๋ถํ ํฉ๋๋ค.
๋ชจ๋ ๋ด์์ ์ธ๋ผ์ธ ํจ์ ํธ์ถ์ ํตํด ์ฝ๋๋ฅผ ๋ถํ ํฉ๋๋ค.
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
๋ lazy ์ปดํฌ๋ํธ๊ฐ ๋ก๋๋๊ธธ ๊ธฐ๋ค๋ฆฌ๋ ๋์ ๋ก๋ฉ ํ๋ฉด๊ณผ ๊ฐ์ ์๋น ์ปจํ
์ธ ๋ฅผ ๋ณด์ฌ์ค ์ ์๊ฒ ํด์ค๋ค. fallback
ย prop์ ์ปดํฌ๋ํธ๊ฐ ๋ก๋๋ ๋๊น์ง ๊ธฐ๋ค๋ฆฌ๋ ๋์ ๋ ๋๋งํ๋ ค๋ React ์๋ฆฌ๋จผํธ๋ฅผ ๋ฐ์๋ค์ธ๋ค.ย Suspense
ย ์ปดํฌ๋ํธ๋ lazy ์ปดํฌ๋ํธ๋ฅผ ๊ฐ์ธ๊ณ ํ๋์ย Suspense
ย ์ปดํฌ๋ํธ๋ก ์ฌ๋ฌ lazy ์ปดํฌ๋ํธ๋ฅผ ๊ฐ์ ์๋ ์์ต๋๋ค.
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);
});
}
React18์ ๋์ ๋ ์๋ก์ด ๋์ ๊ธฐ๋ฅ์ด๋ค. React์ ์ค๋จ๋๊ฑฐ๋ ์ด๋ฏธ ๋ณด์ด๋ ์ฝํ ์ธ ์ ๋ํด suspense fallback์ผ๋ก ๋์๊ฐ๋ ๊ฒ์ ๋ฐฉ์งํ ์ ์๋ค.
๋คํธ์ํฌ ์ฅ์ ๊ฐ์ ์ด์ ๋ก ๋ค๋ฅธ ๋ชจ๋์ ๋ก๋์ ์คํจํ ๊ฒฝ์ฐ ์๋ฌ๋ฅผ ๋ฐ์์ํฌ ์ ์๋ค. 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>
);
๋ฒ๋ค์ ๊ท ๋ฑํ๊ฒ ๋ถ๋ฐฐํ ์ ์๋๋ก ๋ผ์ฐํธ ์ค์ ํ๋ค. ์น ํ์ด์ง๋ฅผ ๋ถ๋ฌ์ค๋ ์๊ฐ์ ํ์ด์ง ์ ํ์ ์ด๋ ์ ๋ ๋ฐ์ํ๋ฉฐ ๋๋ถ๋ถ ํ์ด์ง๋ฅผ ํ๋ฒ์ ๋ ๋๋งํ๊ธฐ ๋๋ฌธ์ ์ฌ์ฉ์๊ฐ ํ์ด์ง๋ฅผ ๋ ๋๋งํ๋ ๋์ ๋ค๋ฅธ ์์์ ์ํธ์์ฉํ์ง ์๋๋ค.
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 ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํด์ ์ ํ๋ฆฌ์ผ์ด์
์ ๋ผ์ฐํธ ๊ธฐ๋ฐ ์ฝ๋ ๋ถํ ์ ์ค์
React.lazy
๋ ํ์ฌ default exports๋ง ์ง์ํ๋ค.
named exports๋ฅผ ์ฌ์ฉํ๊ณ ์ ํ๋ค๋ฉด default๋ก ์ด๋ฆ์ ์ฌ์ ์ํ ์ค๊ฐ ๋ชจ๋์ ์์ฑํ ์ ์๋ค. ์ด๋ ๊ฒ ํ๋ฉด tree shaking์ด ๊ณ์ ๋์ํ๊ณ ์ฌ์ฉํ์ง ์๋ ์ปดํฌ๋ํธ๋ ๊ฐ์ ธ์ค์ง ์๋๋ค.
code splitting ์ ์ฉ ์ฌ๋ก๋ฅผ ์ฐพ์๋ณด๊ณ ์ค์ ๋ก ๋ฒ๋ค ์ฌ์ด์ฆ๊ฐ ๊ฐ์ ๋๋ ๊ฒ์ lighthouse ํผํฌ๋จผ์ค ์ ์๋ก๋ ํ์ธํ ์ ์์๋ค.
๋ฌผ๋ก ์ ๊ทผ์ฑ ์ ์๋ ์ ํ๋ ์ ์์ง๋ง ๊ทธ์ ๋นํด ํผํฌ๋จผ์ค ์์น๊ฐ ์๋ฑํ ํฅ์ํ๊ธฐ ๋๋ฌธ์ code splitting์ ์ค์์ฑ์ ์๊ฒ ๋์๋ค.