์๋ฐ์คํฌ๋ฆฝํธ ์ฝ๋ ์คํ๋ฆฌํ ์ข ํฉ ๊ฐ์ด๋๋ก ๋ ๋น ๋ฅธ ์น ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ง๋ค์ด ๋ณด์ธ์. ์ต์ ํ๋ ์์ํฌ๋ฅผ ์ํ ๋์ ๋ก๋ฉ, ๊ฒฝ๋ก ๊ธฐ๋ฐ ์คํ๋ฆฌํ , ์ฑ๋ฅ ์ต์ ํ ๊ธฐ๋ฒ์ ๋ฐฐ์๋ณด์ธ์.
์๋ฐ์คํฌ๋ฆฝํธ ์ฝ๋ ์คํ๋ฆฌํ : ๋์ ๋ก๋ฉ๊ณผ ์ฑ๋ฅ ์ต์ ํ ์ฌ์ธต ๋ถ์
ํ๋์ ๋์งํธ ํ๊ฒฝ์์ ์น ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ํ ์ฌ์ฉ์์ ์ฒซ์ธ์์ ์ข ์ข ๋จ ํ๋์ ์งํ, ๋ฐ๋ก ์๋์ ์ํด ๊ฒฐ์ ๋ฉ๋๋ค. ๋๋ฆฌ๊ณ ๊ตผ๋ฌ ์น์ฌ์ดํธ๋ ์ฌ์ฉ์ ๋ถ๋ง, ๋์ ์ดํ๋ฅ , ๊ทธ๋ฆฌ๊ณ ๋น์ฆ๋์ค ๋ชฉํ์ ์ง์ ์ ์ธ ๋ถ์ ์ ์ํฅ์ ์ด๋ํ ์ ์์ต๋๋ค. ๋๋ฆฐ ์น ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ฐ์ฅ ํฐ ์์ธ ์ค ํ๋๋ ๋ชจ๋๋ฆฌ์(monolithic) ์๋ฐ์คํฌ๋ฆฝํธ ๋ฒ๋ค์ ๋๋ค. ์ด๋ ์ฌ์ดํธ ์ ์ฒด์ ๋ชจ๋ ์ฝ๋๋ฅผ ํฌํจํ๋ ํ๋์ ๊ฑฐ๋ํ ํ์ผ๋ก, ์ฌ์ฉ์๊ฐ ํ์ด์ง์ ์ํธ์์ฉํ๊ธฐ ์ ์ ๋ค์ด๋ก๋, ํ์ฑ, ์คํ๋์ด์ผ ํฉ๋๋ค.
๋ฐ๋ก ์ด ์ง์ ์์ ์๋ฐ์คํฌ๋ฆฝํธ ์ฝ๋ ์คํ๋ฆฌํ ์ด ํ์ํฉ๋๋ค. ์ด๋ ๋จ์ํ ๊ธฐ์ ์ด ์๋๋ผ, ์ฐ๋ฆฌ๊ฐ ์น ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ตฌ์ถํ๊ณ ์ ๊ณตํ๋ ๋ฐฉ์์ ๋ํ ๊ทผ๋ณธ์ ์ธ ์ํคํ ์ฒ ๋ณํ์ ๋๋ค. ํฐ ๋ฒ๋ค์ ์๊ณ ํ์์ ๋ฐ๋ผ ๋ก๋๋๋ ์ฒญํฌ(chunk)๋ก ๋ถํ ํจ์ผ๋ก์จ, ์ด๊ธฐ ๋ก๋ ์๊ฐ์ ๊ทน์ ์ผ๋ก ๊ฐ์ ํ๊ณ ํจ์ฌ ๋ ๋ถ๋๋ฌ์ด ์ฌ์ฉ์ ๊ฒฝํ์ ๋ง๋ค ์ ์์ต๋๋ค. ์ด ๊ฐ์ด๋์์๋ ์ฝ๋ ์คํ๋ฆฌํ ์ ์ธ๊ณ๋ฅผ ์ฌ์ธต์ ์ผ๋ก ํ๊ตฌํ๋ฉฐ ํต์ฌ ๊ฐ๋ , ์ค์ฉ์ ์ธ ์ ๋ต, ๊ทธ๋ฆฌ๊ณ ์ฑ๋ฅ์ ๋ฏธ์น๋ ์ง๋ํ ์ํฅ์ ์ดํด๋ณด๊ฒ ์ต๋๋ค.
์ฝ๋ ์คํ๋ฆฌํ ์ด๋ ๋ฌด์์ด๋ฉฐ, ์ ์ค์ํ ๊น์?
ํต์ฌ์ ์ผ๋ก ์ฝ๋ ์คํ๋ฆฌํ ์ ์ ํ๋ฆฌ์ผ์ด์ ์ ์๋ฐ์คํฌ๋ฆฝํธ ์ฝ๋๋ฅผ ์ฌ๋ฌ ๊ฐ์ ์์ ํ์ผ, ์ฆ "์ฒญํฌ"๋ก ๋๋์ด ๋์ ์ผ๋ก ๋๋ ๋ณ๋ ฌ๋ก ๋ก๋ํ ์ ์๊ฒ ํ๋ ๊ดํ์ ๋๋ค. ์ฌ์ฉ์๊ฐ ํํ์ด์ง์ ์ฒ์ ๋ฐฉ๋ฌธํ์ ๋ 2MB์ ์๋ฐ์คํฌ๋ฆฝํธ ํ์ผ์ ๋ณด๋ด๋ ๋์ , ํด๋น ํ์ด์ง๋ฅผ ๋ ๋๋งํ๋ ๋ฐ ํ์ํ ํ์์ ์ธ 200KB๋ง ๋ณด๋ผ ์ ์์ต๋๋ค. ์ฌ์ฉ์ ํ๋กํ ํ์ด์ง, ๊ด๋ฆฌ์ ๋์๋ณด๋ ๋๋ ๋ณต์กํ ๋ฐ์ดํฐ ์๊ฐํ ๋๊ตฌ์ ๊ฐ์ ๊ธฐ๋ฅ์ ๋ํ ๋๋จธ์ง ์ฝ๋๋ ์ฌ์ฉ์๊ฐ ์ค์ ๋ก ํด๋น ๊ธฐ๋ฅ์ผ๋ก ์ด๋ํ๊ฑฐ๋ ์ํธ์์ฉํ ๋๋ง ๊ฐ์ ธ์ต๋๋ค.
๋ ์คํ ๋์์ ์ฃผ๋ฌธํ๋ ๊ฒ์ ์๊ฐํด๋ณด์ธ์. ๋ชจ๋๋ฆฌ์ ๋ฒ๋ค์ ์ํ๋ ์์น ์๋ ์ ์ฒด ์ฝ์ค ๋ฉ๋ด๋ฅผ ํ ๋ฒ์ ์ ๊ณต๋ฐ๋ ๊ฒ๊ณผ ๊ฐ์ต๋๋ค. ์ฝ๋ ์คํ๋ฆฌํ ์ ๋จํ ์๋ฆฌ(ร la carte) ๊ฒฝํ์ ๋๋ค. ์ฆ, ํ์ํ ๊ฒ์, ํ์ํ ๋ฐ๋ก ๊ทธ ์๊ฐ์ ์ ํํ ์ ๊ณต๋ฐ๋ ๊ฒ์ ๋๋ค.
๋ชจ๋๋ฆฌ์ ๋ฒ๋ค์ ๋ฌธ์ ์
ํด๊ฒฐ์ฑ ์ ์์ ํ ์ดํดํ๊ธฐ ์ํด์๋ ๋จผ์ ๋ฌธ์ ๋ฅผ ์ดํดํด์ผ ํฉ๋๋ค. ๋จ์ผ์ ํฐ ๋ฒ๋ค์ ์ฌ๋ฌ ๊ฐ์ง ๋ฐฉ์์ผ๋ก ์ฑ๋ฅ์ ๋ถ์ ์ ์ธ ์ํฅ์ ๋ฏธ์นฉ๋๋ค:
- ๋คํธ์ํฌ ์ง์ฐ ์๊ฐ ์ฆ๊ฐ: ํ์ผ์ด ํด์๋ก ๋ค์ด๋ก๋ํ๋ ๋ฐ ์๊ฐ์ด ๋ ์ค๋ ๊ฑธ๋ฆฌ๋ฉฐ, ํนํ ์ ์ธ๊ณ ๋ง์ ์ง์ญ์์ ํํ ๋๋ฆฐ ๋ชจ๋ฐ์ผ ๋คํธ์ํฌ์์๋ ๋์ฑ ๊ทธ๋ ์ต๋๋ค. ์ด ์ด๊ธฐ ๋๊ธฐ ์๊ฐ์ ์ข ์ข ์ฒซ ๋ฒ์งธ ๋ณ๋ชฉ ํ์์ด ๋ฉ๋๋ค.
- ๊ธด ํ์ฑ ๋ฐ ์ปดํ์ผ ์๊ฐ: ๋ค์ด๋ก๋๋ ํ, ๋ธ๋ผ์ฐ์ ์ ์๋ฐ์คํฌ๋ฆฝํธ ์์ง์ ์ ์ฒด ์ฝ๋๋ฒ ์ด์ค๋ฅผ ํ์ฑํ๊ณ ์ปดํ์ผํด์ผ ํฉ๋๋ค. ์ด๋ CPU๋ฅผ ๋ง์ด ์ฌ์ฉํ๋ ์์ ์ผ๋ก ๋ฉ์ธ ์ค๋ ๋๋ฅผ ์ฐจ๋จํ์ฌ ์ฌ์ฉ์ ์ธํฐํ์ด์ค๊ฐ ๋ฉ์ถ๊ณ ์๋ตํ์ง ์๊ฒ ๋ง๋ญ๋๋ค.
- ๋ ๋๋ง ์ฐจ๋จ: ๋ฉ์ธ ์ค๋ ๋๊ฐ ์๋ฐ์คํฌ๋ฆฝํธ๋ก ๋ฐ์ ๋์์๋ ํ์ด์ง ๋ ๋๋ง์ด๋ ์ฌ์ฉ์ ์ ๋ ฅ์ ์๋ตํ๋ ๊ฒ๊ณผ ๊ฐ์ ๋ค๋ฅธ ์ค์ํ ์์ ์ ์ํํ ์ ์์ต๋๋ค. ์ด๋ ์ง์ ์ ์ผ๋ก ์ํธ์์ฉ๊น์ง์ ์๊ฐ(TTI)์ ์ ํ์ํต๋๋ค.
- ๋ฆฌ์์ค ๋ญ๋น: ๋ชจ๋๋ฆฌ์ ๋ฒ๋ค์ ์๋ ์ฝ๋์ ์๋น ๋ถ๋ถ์ ์ผ๋ฐ์ ์ธ ์ฌ์ฉ์ ์ธ์ ๋์ ์ ํ ์ฌ์ฉ๋์ง ์์ ์ ์์ต๋๋ค. ์ด๋ ์ฌ์ฉ์๊ฐ ์๋ฌด๋ฐ ๊ฐ์น๋ ์ ๊ณตํ์ง ์๋ ์ฝ๋๋ฅผ ๋ค์ด๋ก๋ํ๊ณ ์ค๋นํ๊ธฐ ์ํด ๋ฐ์ดํฐ, ๋ฐฐํฐ๋ฆฌ, ์ฒ๋ฆฌ ๋ฅ๋ ฅ์ ๋ญ๋นํ๋ค๋ ๊ฒ์ ์๋ฏธํฉ๋๋ค.
- ์ฝ์ด ์น ๋ฐ์ดํ ์ ํ: ์ด๋ฌํ ์ฑ๋ฅ ๋ฌธ์ ๋ ์ฝ์ด ์น ๋ฐ์ดํ ์ ์์ ์ง์ ์ ์ธ ํด๋ฅผ ๋ผ์ณ ๊ฒ์ ์์ง ์์์ ์ํฅ์ ์ค ์ ์์ต๋๋ค. ์ฐจ๋จ๋ ๋ฉ์ธ ์ค๋ ๋๋ ์ต์ด ์ ๋ ฅ ์ง์ฐ(FID) ๋ฐ ๋ค์ ํ์ธํธ์ ๋ํ ์ํธ์์ฉ(INP)์ ์ ํ์ํค๊ณ , ์ง์ฐ๋ ๋ ๋๋ง์ ์ต๋ ์ฝํ ์ธ ํ ํ์ธํธ(LCP)์ ์ํฅ์ ๋ฏธ์นฉ๋๋ค.
ํ๋ ์ฝ๋ ์คํ๋ฆฌํ
์ ํต์ฌ: ๋์ import()
๋๋ถ๋ถ์ ํ๋ ์ฝ๋ ์คํ๋ฆฌํ
์ ๋ต์ ํต์ฌ์๋ ํ์ค ์๋ฐ์คํฌ๋ฆฝํธ ๊ธฐ๋ฅ์ธ ๋์ import()
ํํ์์ด ์์ต๋๋ค. ๋น๋ ์ ์ฒ๋ฆฌ๋์ด ๋ชจ๋์ ํจ๊ป ๋ฌถ๋ ์ ์ import
๋ฌธ๊ณผ ๋ฌ๋ฆฌ, ๋์ import()
๋ ํ์์ ๋ฐ๋ผ ๋ชจ๋์ ๋ก๋ํ๋ ํจ์์ ๊ฐ์ ํํ์์
๋๋ค.
์๋ ๋ฐฉ์์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
import('/path/to/module.js')
Webpack, Vite ๋๋ Rollup๊ณผ ๊ฐ์ ๋ฒ๋ค๋ฌ๊ฐ ์ด ๊ตฌ๋ฌธ์ ๋ณด๋ฉด, './path/to/module.js'
์ ๊ทธ ์์กด์ฑ๋ค์ด ๋ณ๋์ ์ฒญํฌ์ ๋ฐฐ์น๋์ด์ผ ํ๋ค๋ ๊ฒ์ ์ดํดํฉ๋๋ค. import()
ํธ์ถ ์์ฒด๋ Promise๋ฅผ ๋ฐํํ๋ฉฐ, ๋คํธ์ํฌ๋ฅผ ํตํด ๋ชจ๋์ด ์ฑ๊ณต์ ์ผ๋ก ๋ก๋๋๋ฉด ๋ชจ๋์ ์ฝํ
์ธ ๋ก ํด์(resolve)๋ฉ๋๋ค.
์ผ๋ฐ์ ์ธ ๊ตฌํ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
// id="load-feature"๋ฅผ ๊ฐ์ง ๋ฒํผ์ด ์๋ค๊ณ ๊ฐ์
const featureButton = document.getElementById('load-feature');
featureButton.addEventListener('click', () => {
import('./heavy-feature.js')
.then(module => {
// ๋ชจ๋์ด ์ฑ๊ณต์ ์ผ๋ก ๋ก๋๋จ
const feature = module.default;
feature.initialize(); // ๋ก๋๋ ๋ชจ๋์ ํจ์ ์คํ
})
.catch(err => {
// ๋ก๋ฉ ์ค ์๋ฌ ์ฒ๋ฆฌ
console.error('๊ธฐ๋ฅ์ ๋ก๋ํ๋ ๋ฐ ์คํจํ์ต๋๋ค:', err);
});
});
์ด ์์์ `heavy-feature.js`๋ ์ด๊ธฐ ํ์ด์ง ๋ก๋์ ํฌํจ๋์ง ์์ต๋๋ค. ์ฌ์ฉ์๊ฐ ๋ฒํผ์ ํด๋ฆญํ ๋๋ง ์๋ฒ์์ ์์ฒญ๋ฉ๋๋ค. ์ด๊ฒ์ด ๋์ ๋ก๋ฉ์ ๊ธฐ๋ณธ ์๋ฆฌ์ ๋๋ค.
์ค์ฉ์ ์ธ ์ฝ๋ ์คํ๋ฆฌํ ์ ๋ต
"์ด๋ป๊ฒ"๋ฅผ ์๋ ๊ฒ๋ ์ค์ํ์ง๋ง, "์ด๋์"์ "์ธ์ "๋ฅผ ์๋ ๊ฒ์ด ์ฝ๋ ์คํ๋ฆฌํ ์ ์ง์ ์ผ๋ก ํจ๊ณผ์ ์ผ๋ก ๋ง๋ญ๋๋ค. ๋ค์์ ํ๋ ์น ๊ฐ๋ฐ์์ ์ฌ์ฉ๋๋ ๊ฐ์ฅ ์ผ๋ฐ์ ์ด๊ณ ๊ฐ๋ ฅํ ์ ๋ต๋ค์ ๋๋ค.
1. ๊ฒฝ๋ก ๊ธฐ๋ฐ ์คํ๋ฆฌํ
์ด๊ฒ์ ์๋ง๋ ๊ฐ์ฅ ์ํฅ๋ ฅ ์๊ณ ๋๋ฆฌ ์ฌ์ฉ๋๋ ์ ๋ต์ผ ๊ฒ์ ๋๋ค. ์์ด๋์ด๋ ๊ฐ๋จํฉ๋๋ค: ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ฐ ํ์ด์ง๋ ๊ฒฝ๋ก๊ฐ ์์ฒด ์๋ฐ์คํฌ๋ฆฝํธ ์ฒญํฌ๋ฅผ ๊ฐ๊ฒ ๋ฉ๋๋ค. ์ฌ์ฉ์๊ฐ `/home`์ ๋ฐฉ๋ฌธํ๋ฉด ํ ํ์ด์ง์ ๋ํ ์ฝ๋๋ง ๋ก๋ํฉ๋๋ค. ๋ง์ฝ ๊ทธ๋ค์ด `/dashboard`๋ก ์ด๋ํ๋ฉด, ๋์๋ณด๋์ฉ ์๋ฐ์คํฌ๋ฆฝํธ๊ฐ ๋์ ์ผ๋ก ๊ฐ์ ธ์์ง๋๋ค.
์ด ์ ๊ทผ ๋ฐฉ์์ ์ฌ์ฉ์ ํ๋๊ณผ ์๋ฒฝํ๊ฒ ์ผ์นํ๋ฉฐ ๋ค์ค ํ์ด์ง ์ ํ๋ฆฌ์ผ์ด์ (๋จ์ผ ํ์ด์ง ์ ํ๋ฆฌ์ผ์ด์ , ์ฆ SPA ํฌํจ)์ ๋งค์ฐ ํจ๊ณผ์ ์ ๋๋ค. ๋๋ถ๋ถ์ ํ๋ ํ๋ ์์ํฌ๋ ์ด๋ฅผ ๊ธฐ๋ณธ์ ์ผ๋ก ์ง์ํฉ๋๋ค.
React ์์ (React.lazy
์ Suspense
)
React๋ ์ปดํฌ๋ํธ๋ฅผ ๋์ ์ผ๋ก ๊ฐ์ ธ์ค๋ React.lazy
์ ์ปดํฌ๋ํธ ์ฝ๋๊ฐ ๋ก๋๋๋ ๋์ ๋์ฒด UI(๋ก๋ฉ ์คํผ๋ ๋ฑ)๋ฅผ ๋ณด์ฌ์ฃผ๋ Suspense
๋ฅผ ํตํด ๊ฒฝ๋ก ๊ธฐ๋ฐ ์คํ๋ฆฌํ
์ ์ํํ๊ฒ ๋ง๋ญ๋๋ค.
import React, { Suspense, lazy } from 'react';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
// ๊ณตํต/์ด๊ธฐ ๊ฒฝ๋ก๋ฅผ ์ํ ์ปดํฌ๋ํธ๋ ์ ์ ์ผ๋ก ๊ฐ์ ธ์ค๊ธฐ
import HomePage from './pages/HomePage';
// ๋ ์ฌ์ฉ๋๊ฑฐ๋ ๋ฌด๊ฑฐ์ด ๊ฒฝ๋ก๋ฅผ ์ํ ์ปดํฌ๋ํธ๋ ๋์ ์ผ๋ก ๊ฐ์ ธ์ค๊ธฐ
const DashboardPage = lazy(() => import('./pages/DashboardPage'));
const AdminPanel = lazy(() => import('./pages/AdminPanel'));
function App() {
return (
ํ์ด์ง ๋ก๋ฉ ์ค...
Vue ์์ (๋น๋๊ธฐ ์ปดํฌ๋ํธ)
Vue ๋ผ์ฐํฐ๋ ๊ฒฝ๋ก ์ ์์์ ์ง์ ๋์ import()
๊ตฌ๋ฌธ์ ์ฌ์ฉํ์ฌ ์ปดํฌ๋ํธ๋ฅผ ์ง์ฐ ๋ก๋ฉํ๋ ๊ฒ์ ์ต์ฐ์ ์ผ๋ก ์ง์ํฉ๋๋ค.
import { createRouter, createWebHistory } from 'vue-router';
import Home from '../views/Home.vue';
const routes = [
{
path: '/',
name: 'Home',
component: Home // ์ด๊ธฐ์ ๋ก๋๋จ
},
{
path: '/about',
name: 'About',
// ๊ฒฝ๋ก ์์ค ์ฝ๋ ์คํ๋ฆฌํ
// ์ด ๊ฒฝ๋ก๋ ๋ณ๋์ ์ฒญํฌ๋ก ์์ฑ๋จ
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
}
];
const router = createRouter({
history: createWebHistory(),
routes
});
export default router;
2. ์ปดํฌ๋ํธ ๊ธฐ๋ฐ ์คํ๋ฆฌํ
๋๋ก๋ ๋จ์ผ ํ์ด์ง ๋ด์์๋ ์ฆ์ ํ์ํ์ง ์์ ํฐ ์ปดํฌ๋ํธ๊ฐ ์์ต๋๋ค. ์ด๊ฒ๋ค์ ์ปดํฌ๋ํธ ๊ธฐ๋ฐ ์คํ๋ฆฌํ ์ ์๋ฒฝํ ํ๋ณด์ ๋๋ค. ์์๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
- ์ฌ์ฉ์๊ฐ ๋ฒํผ์ ํด๋ฆญํ ํ ๋ํ๋๋ ๋ชจ๋ฌ์ด๋ ๋ํ์์.
- ์คํฌ๋กค์ ๋ด๋ ค์ผ ๋ณด์ด๋ ๋ณต์กํ ์ฐจํธ๋ ๋ฐ์ดํฐ ์๊ฐํ.
- ์ฌ์ฉ์๊ฐ "ํธ์ง"์ ํด๋ฆญํ ๋๋ง ๋ํ๋๋ ๋ฆฌ์น ํ ์คํธ ํธ์ง๊ธฐ.
- ์ฌ์ฉ์๊ฐ ์ฌ์ ์์ด์ฝ์ ํด๋ฆญํ๊ธฐ ์ ๊น์ง ๋ก๋ํ ํ์๊ฐ ์๋ ๋น๋์ค ํ๋ ์ด์ด ๋ผ์ด๋ธ๋ฌ๋ฆฌ.
๊ตฌํ์ ๊ฒฝ๋ก ๊ธฐ๋ฐ ์คํ๋ฆฌํ ๊ณผ ์ ์ฌํ์ง๋ง, ๊ฒฝ๋ก ๋ณ๊ฒฝ ๋์ ์ฌ์ฉ์ ์ํธ์์ฉ์ ์ํด ํธ๋ฆฌ๊ฑฐ๋ฉ๋๋ค.
์์ : ํด๋ฆญ ์ ๋ชจ๋ฌ ๋ก๋ฉํ๊ธฐ
import React, { useState, Suspense, lazy } from 'react';
// ๋ชจ๋ฌ ์ปดํฌ๋ํธ๋ ์์ฒด ํ์ผ์ ์ ์๋์ด ๋ณ๋์ ์ฒญํฌ๊ฐ ๋ฉ๋๋ค
const HeavyModal = lazy(() => import('./components/HeavyModal'));
function MyPage() {
const [isModalOpen, setIsModalOpen] = useState(false);
const openModal = () => {
setIsModalOpen(true);
};
return (
ํ์ด์ง์ ์ค์ ๊ฒ์ ํ์ํฉ๋๋ค
{isModalOpen && (
๋ชจ๋ฌ ๋ก๋ฉ ์ค... }>
setIsModalOpen(false)} />
)}