[Vite] ์ด๊ธฐ ๋ก๋ ์๊ฐ ๊ฐ์ ์ ์ํ Vendor Chunk ์ชผ๊ฐ๊ธฐ
Intro
ํ๋ก ํธ์๋ ์์ญ์์ .jsx
, .tsx
, .vue
๋ฑ์ ํ์ฅ์๋ฅผ ๊ฐ์ง ํ์ผ์ ์ฌ์ฉํ์ง๋ง ๋ธ๋ผ์ฐ์ ๋ ์ด๋ฅผ ์ดํดํ์ง ๋ชปํ๋ค. ๋๋ฌธ์ ์ด๋ฌํ ํ์ผ๋ค์ ๋ชจ๋ .js
ํ์ผ๋ก ๋ณํํด์ผ ํ๋ค. ๋ชจ๋ ํ์ผ์ ํ๋์ .js
ํ์ผ๋ก ๋ง๋ค ์ ์์ง๋ง ์ด ๋ฐฉ์์ ์ฑ๋ฅ์ ๋ถ์ ์ ์ธ ์ํฅ์ ์ค ์ ์์ผ๋ฏ๋ก ๊ถ์ฅ๋์ง ์๋๋ค. ๋์ ์ฌ๋ฌ .js
ํ์ผ(chunks)๋ก ๋ณํํ ๋ค ํ์ํ ์๊ฐ์๋ง ๋ก๋(์ง์ฐ ๋ก๋ฉ)ํ๋๊ฒ ์ฑ๋ฅ์ ๋ ์ ๋ฆฌํ๋ค. ํนํ ์ด๊ธฐ ๋ก๋ฉ ์๊ฐ์ ์ค์ผ ๋ ์ ์ฉํ๋ค.
Vite, Webpack ๊ฐ์ ๋ฒ๋ค๋ฌ๋ ๋ณดํต index, vendor ๋ ๊ฐ์ ๋ฉ์ธ chunk๋ฅผ ์์ฑํ๋ค. ์ฐธ๊ณ ๋ก Vite 2.9 ๋ฒ์ ๋ถํด Production ์ข ์์ฑ ๋ชจ๋์ด index chunk์ ํฌํจ๋๋ค.
- index : App.tsx์ ๊ฐ์ ์ดํ๋ฆฌ์ผ์ด์ ์ ํต์ฌ ์ฝ๋
- vendor : package.json - dependencies ์น์ ์ ๋ช ์๋ ์ข ์์ฑ
Vite ๊ธฐ๋ณธ ์ค์ ์ผ๋ก ๋น๋ํด๋ณด๋ฉด ์๋์ฒ๋ผ index ์ฒญํฌ๊ฐ ์์ฑ๋๋ค. chunk๋ฅผ ๋ฐ๋ก ๋ถ๋ฆฌํ์ง ์์๊ธฐ ๋๋ฌธ์ ์ดํ๋ฆฌ์ผ์ด์ ์ ์ด๋ค ํ์ด์ง๋ฅผ ๋ฐฉ๋ฌธํ๋ 'ํฉ์ณ์ง' index ์ฒญํฌ ๋ฉ์ด๋ฆฌ๋ฅผ ๋ฐ์์ผ ํ๋ค.
# ๋น๋ ๊ฒฐ๊ณผ
dist/registerSW.js 0.13 kB
dist/manifest.webmanifest 0.15 kB
dist/index.html 1.96 kB โ gzip: 1.02 kB
dist/assets/index-df6d6f21.css 12.84 kB โ gzip: 1.03 kB
dist/assets/QueryErrorResetBoundary-30b62529.js 0.18 kB โ gzip: 0.16 kB
dist/assets/chunk-G5IOFHY7-58761137.js 1.58 kB โ gzip: 0.88 kB
dist/assets/app-68a6bb5a.js 2.56 kB โ gzip: 1.20 kB
dist/assets/index-6b503c34.js 7.12 kB โ gzip: 2.70 kB
dist/assets/index-91b8ad77.js 577.07 kB โ gzip: 157.92 kB
dist/assets/index-a08b75ea.js 787.30 kB โ gzip: 253.44 kB
๐ Rollup Plugin Visualizer ํ๋ฌ๊ทธ์ธ์ ์ฌ์ฉํ๋ฉด ๋ฒ๋ค ์ฌ์ด์ฆ์ ๊ตฌ์กฐ๋ฅผ ์๊ฐ์ ์ผ๋ก ํ์ธํ ์ ์๋ค. ๊ฐ ๋ชจ๋์ ํฌ๊ธฐ, ์์กด์ฑ, ๋ฒ๋ค์์ ์ฐจ์งํ๋ ์ฌ์ด์ฆ ๋น์จ ๋ฑ์ ํธ๋ฆฌ๋งต ํน์ ๋ผ์ธ ์ฐจํธ ํํ๋ก ์ ๊ณตํ๋ค.
๐ Vite๋ Vue.js ์ฐฝ์์ Evan You(ๅฐค้จๆบช)๊ฐ ๋ง๋ ์น ๋น๋ ๋๊ตฌ๋ก, ์๋ ๋ ๊ฐ์ง ํน์ง์ ๊ฐ๋๋ค.
- Native ES Modules ๊ธฐ๋ฐ์ ๊ฐ๋ฐ ์๋ฒ: ES Modules๋ ๋ธ๋ผ์ฐ์ ์์ ์ง์ ์ง์ํ๋ฏ๋ก ๋ฒ๋ค๋ง ๊ณผ์ ์์ด ๋ชจ๋์ ๋ก๋ํ ์ ์๋ค. ์ด๋ฅผ ํตํด ๊ฐ๋ฐ ๊ณผ์ ์์ ๋น ๋ฅธ ๋ฆฌ๋ก๋ ๊ฒฝํ์ ์ ๊ณตํ๋ค.
- ํ์ผ ํตํฉ์ esbuild, ๋ฒ๋ค๋ง์ Rollup ์ฌ์ฉ: esbuild๋ Go๋ก ์์ฑ๋ ๊ณ ์ฑ๋ฅ ๋ฒ๋ค๋ฌ/ํธ๋์คํ์ผ๋ฌ๋ค. ํ๋ก๋์ ๋น๋์์ ํธ๋ฆฌ ์์ดํน ๊ฐ์ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ Rollup์ ์ฌ์ฉํ์ฌ ํจ์จ์ ์ธ ๊ฒฐ๊ณผ๋ฌผ์ ์์ฑํ๋ค.
Vendor ์ฒญํฌ ๋ถ๋ฆฌ
Vite 2.9 ๋ฒ์ ๋ถํด ๊ธฐ๋ณธ์ ์ผ๋ก vendor ์ฒญํฌ๋ฅผ ๋ถ๋ฆฌํ์ง ์์ง๋ง splitVendorChunkPlugin
ํ๋ฌ๊ทธ์ธ์ ์ฌ์ฉํ๋ฉด ๋ฒค๋ ๋ชจ๋๋ง ๋ณ๋์ ์ฒญํฌ๋ก ๋ถ๋ฆฌํ ์ ์๋ค.
// vite.config.ts
import { splitVendorChunkPlugin } from 'vite';
export default defineConfig({
plugins: [react(), splitVendorChunkPlugin()],
});
ํ๋ฌ๊ทธ์ธ์ ์ค์ ํ๊ณ ๋น๋ํด๋ณด๋ฉด vendor-*.js
์ด๋ฆ์ ์ฒญํฌ๊ฐ ์์ฑ๋๋ค. index ์ฒญํฌ๋ ๋ฒค๋ ๋ชจ๋์ด ์ ๊ฑฐ๋ผ์ ์ฌ์ด์ฆ๊ฐ ์ค์์ง๋ง, vendor ์ฒญํฌ๋ 832 kB ์ ๋๋ก ๊ฝค ๋น๋ํด์ก๋ค.
# ๋น๋ ๊ฒฐ๊ณผ
dist/registerSW.js 0.13 kB
dist/manifest.webmanifest 0.15 kB
dist/index.html 2.04 kB โ gzip: 1.05 kB
dist/assets/index-df6d6f21.css 12.84 kB โ gzip: 1.03 kB
dist/assets/app-04f6ef89.js 2.21 kB โ gzip: 1.03 kB
dist/assets/index-8be5580d.js 6.86 kB โ gzip: 2.56 kB
dist/assets/index-995e1624.js 21.26 kB โ gzip: 6.72 kB
dist/assets/index-9413cf69.js 501.03 kB โ gzip: 135.67 kB
dist/assets/vendor-16e06f2d.js 832.65 kB โ gzip: 267.41 kB
Manual Chunks
Rollup์์ ์ ๊ณตํ๋ manualChunks ํจ์๋ฅผ ์ด์ฉํ๋ฉด vendor ์ฒญํฌ๋ฅผ ๋ ์์ ๋จ์๋ก ์ชผ๊ฐค ์ ์๋ค. ์ผ๋ฐ์ ์ผ๋ก ์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ์ข ์์ฑ์ ๊ฐ์ง๊ณ ์๊ณ , ์ด ์ข ์์ฑ๋ค์ด ์ฌ๋ฐ๋ฅธ ์์๋ก ๋ก๋๋ผ์ผ ํ๋ค. ์ฒญํฌ๋ฅผ ์๋์ผ๋ก ์์ฑํ ๋ ๊ฐ์ฅ ์ด๋ ค์ด ๋ถ๋ถ ์ค ํ๋๋ค. ์๋ฅผ๋ค์ด react-router-dom ํจํค์ง๋ @remix-run/router, react-router์ ์์กด์ ์ด๋ฏ๋ก ์ด๋ค์ ๋์ผํ ์ฒญํฌ์ ํฌํจ์์ผ์ผ ํ๋ค.
# react-router-dom ํจํค์ง์ ์์กด์ฑ ํ์ธ ๋ช
๋ น์ด
# { '@remix-run/router': '1.9.0', 'react-router': '6.16.0' }
npm show react-router-dom dependencies
# { react: '^18.2.0', 'react-dom': '^18.2.0' }
npm show react-router-dom devDependencies
๐ splitVenderChunkPlugin ํ๋ฌ๊ทธ์ธ์ ์ฌ์ฉํ ๋ manualChunks๋ ํจ์ ํํ๋ง ์ฌ์ฉํ ์ ์๋ค.
manualChunks๋ ํจ์์ ๊ฐ์ฒด ๋ ๊ฐ์ง ๋ฐฉ์์ผ๋ก ์ฌ์ฉํ ์ ์๋ค. ๊ฐ์ฒด ํํ๋ก ์ฌ์ฉํ ๋ ๊ฐ ์์ฑ์ ์ฒญํฌ๋ฅผ ๊ฐ๋ฆฌํค๊ณ , ์ฒญํฌ ์ด๋ฆ์ ์์ฑ ํค๋ก ๊ฒฐ์ ๋๋ค. ํจ์ ํํ๋ก ์ฌ์ฉํ ๊ฒฝ์ฐ, ๋ชจ๋ ID๋ฅผ ํ๋ผ๋ฏธํฐ๋ก ๋ฐ๋๋ค. ํจ์๊ฐ ๋ฌธ์์ด์ ๋ฐํํ๋ฉด, ํด๋น ID์ ๋ชจ๋๊ณผ ๋ชจ๋ ์์กด์ฑ์ด ๋ฌธ์์ด ์ด๋ฆ์ ์ฒญํฌ์ ์ถ๊ฐ๋๋ค.
// ๊ฐ์ฒด๋ก ์ฌ์ฉํ ๋
manualChunks: {
lodash: ['lodash'], // 'lodash' ๋ชจ๋์ 'lodash' ์ฒญํฌ๋ก ๊ทธ๋ฃนํ
}
// ํจ์๋ก ์ฌ์ฉํ ๋
manualChunks(id) {
// 'node_modules'๋ฅผ ํฌํจํ๋ ๋ชจ๋ ๋ชจ๋์ 'vendor' ์ฒญํฌ๋ก ๊ทธ๋ฃนํ
if (id.includes('node_modules')) return 'vendor';
}
์๋๋ react-router-dom ๋ชจ๋๊ณผ ๊ด๋ จ๋ ์์กด์ฑ์ @react-router๋ผ๋ ์ด๋ฆ์ ์ฒญํฌ๋ก ๊ทธ๋ฃนํํ ์์.
// vite.config.ts
import { splitVendorChunkPlugin } from 'vite';
export default defineConfig({
plugins: [react(), splitVendorChunkPlugin()],
build: {
rollupOptions: {
output: {
manualChunks(id: string) {
const reactRouterDeps = [
'@remix-run',
'react-router',
'react-router-dom',
];
if (reactRouterDeps.some((lib) => id.includes(lib)))
return '@react-router';
},
},
},
},
});
์ Vite ์ค์ ์ผ๋ก ๋น๋ํด๋ณด๋ฉด @react-router ์ด๋ฆ์ ์ฒญํฌ ํ์ผ์ด ์์ฑ๋๊ฑธ ํ์ธํ ์ ์๋ค. vendor ์ฒญํฌ ์ฉ๋์ @react-router ์ฒญํฌ ํฌ๊ธฐ(50 kB)๋งํผ ๊ฐ์ํ๋ค.
# ๋น๋ ๊ฒฐ๊ณผ
dist/registerSW.js 0.13 kB
dist/manifest.webmanifest 0.15 kB
dist/index.html 2.12 kB โ gzip: 1.07 kB
dist/assets/index-df6d6f21.css 12.84 kB โ gzip: 1.03 kB
dist/assets/app-c7e6b312.js 2.29 kB โ gzip: 1.06 kB
dist/assets/index-049072fc.js 6.91 kB โ gzip: 2.58 kB
dist/assets/index-bfb7ca8b.js 21.44 kB โ gzip: 6.75 kB
dist/assets/@react-router-eb1c5b4a.js 50.25 kB โ gzip: 17.34 kB
dist/assets/index-e3190f43.js 501.06 kB โ gzip: 135.69 kB
dist/assets/vendor-0663586e.js 781.40 kB โ gzip: 250.49 kB
์์กด์ฑ ๊ธฐ์ค์ผ๋ก ๋ถ๋ฆฌ โญ
๐ package.json.dependencies
๋ฅผ ๋ถ๋ฌ์ค๋ ค๋ฉด tsconfig.node.json.include
๋ฐฐ์ด์ package.json ๊ฒฝ๋ก๋ฅผ ํฌํจ์์ผ์ผ ํ๋ค. e.g. "include": ["vite.config.ts", "package.json"]
ํจํค์ง ์์กด์ฑ๊ณผ ๋ก๋ ์์๋ฅผ ์ผ์ผ์ด ํ์ ํด์ ์ฒญํฌ๋ฅผ ์๋ ์์ฑํ๋ ์์ ์ ๋ฒ๊ฑฐ๋กญ๋ค. ๋์ โ ์ดํ๋ฆฌ์ผ์ด์ ์ ๋ฉ์ธ ํ๋ ์์ํฌ(React)์ ๊ด๋ จ ํจํค์ง๋ฅผ vendor ์ฒญํฌ๋ก ๋ฌถ๊ณ , โก๋๋จธ์ง๋ package.json์ ์ข ์์ฑ์ ๊ธฐ์ค์ผ๋ก ์ฒญํฌ๊ฐ ์๋ ๋ถ๋ฆฌ๋๋๋ก ์ค์ ํ ์ ์๋ค.
// vite.config.ts
import { dependencies } from './package.json';
// dependencies: { axios: '^1.5.0', jotai: '^2.4.2', ... }
// React ๊ด๋ จ ์์กด์ฑ ํํฐ๋ง
const reactDeps = Object.keys(dependencies).filter(
(key) => key === 'react' || key.startsWith('react-'),
);
const manualChunks = {
// 'vendor' chunk์ ๋ชจ๋ React ๊ด๋ จ ์์กด์ฑ ํฌํจ
vendor: reactDeps,
// ๋๋จธ์ง ์์กด์ฑ์ ๋ํด ์ถ๊ฐ์ ์ธ chunk ์์ฑ
...Object.keys(dependencies).reduce((chunks, name) => {
// 'vendor'์ ์ด๋ฏธ ํฌํจ๋ ์์กด์ฑ์ ๊ฑด๋๋ฐ๊ธฐ
if (!reactDeps.includes(name)) chunks[name] = [name];
return chunks;
}, {}),
};
export default defineConfig({
plugins: [react()],
build: { rollupOptions: { output: { manualChunks } } },
});
{
vendor: [
'react',
'react-dom',
'react-error-boundary',
'react-hook-form',
'react-router-dom',
'react-tsparticles'
],
'@chakra-ui/react': [ '@chakra-ui/react' ],
'@fingerprintjs/fingerprintjs': [ '@fingerprintjs/fingerprintjs' ],
'@formkit/auto-animate': [ '@formkit/auto-animate' ],
'@hookform/resolvers': [ '@hookform/resolvers' ],
'@tanstack/react-query': [ '@tanstack/react-query' ],
'@vercel/analytics': [ '@vercel/analytics' ],
axios: [ 'axios' ],
'date-fns': [ 'date-fns' ],
'framer-motion': [ 'framer-motion' ],
jotai: [ 'jotai' ],
nanoid: [ 'nanoid' ],
qs: [ 'qs' ],
sass: [ 'sass' ],
yup: [ 'yup' ]
}
์ ์ค์ ์ ์ ์ฉํ๊ณ ๋น๋ํด ๋ณด๋ฉด ๊ธฐ์กด 781kB์๋ vendor ์ฒญํฌ๊ฐ ์ฝ 290kB ํฌ๊ธฐ์ ๋ฆฌ์กํธ ๊ด๋ จ ํจํค์ง๋ก ๊ทธ๋ฃนํ๋๊ณ , vendor ์ฒญํฌ์ ์๋ ์์กด์ฑ๋ค์ ๊ฐ๊ฐ ๊ฐ๋ณ ์ฒญํฌ๋ก ๋ถ๋ฆฌ๋๋ค. ์ด๋ฅผ ํตํด ์ด๊ธฐ ํ์ด์ง ๋ก๋ฉ ์๊ฐ์ ๋จ์ถํ๊ณ , ์ฌ์ฉ์๋ ํ์ํ ๋ฆฌ์์ค๋ง ๋ค์ด๋ก๋ํ ์ ์๊ฒ ๋๋ค.
# ๋น๋ ๊ฒฐ๊ณผ
dist/registerSW.js 0.13 kB
dist/manifest.webmanifest 0.15 kB
dist/index.html 2.60 kB โ gzip: 1.17 kB
dist/assets/index-df6d6f21.css 12.84 kB โ gzip: 1.03 kB
dist/assets/@fingerprintjs/fingerprintjs-4ed993c7.js 0.00 kB โ gzip: 0.02 kB
dist/assets/@vercel/analytics-4ed993c7.js 0.00 kB โ gzip: 0.02 kB
dist/assets/sass-4ed993c7.js 0.00 kB โ gzip: 0.02 kB
dist/assets/nanoid-20264c15.js 0.44 kB โ gzip: 0.31 kB
dist/assets/@hookform/resolvers-6186e247.js 0.76 kB โ gzip: 0.43 kB
dist/assets/app-ee4ec089.js 1.25 kB โ gzip: 0.59 kB
dist/assets/jotai-5371d6c6.js 5.52 kB โ gzip: 2.42 kB
dist/assets/@formkit/auto-animate-99b7ba86.js 5.77 kB โ gzip: 2.45 kB
dist/assets/index-10a9dd48.js 7.10 kB โ gzip: 2.68 kB
dist/assets/date-fns-1253a22e.js 26.79 kB โ gzip: 7.52 kB
dist/assets/axios-599d2e10.js 29.03 kB โ gzip: 11.62 kB
dist/assets/qs-c2440112.js 31.85 kB โ gzip: 10.57 kB
dist/assets/yup-4151e337.js 35.43 kB โ gzip: 11.58 kB
dist/assets/@tanstack/react-query-512219a8.js 39.93 kB โ gzip: 11.01 kB
dist/assets/index-653ca8f3.js 56.92 kB โ gzip: 18.33 kB
dist/assets/framer-motion-5b367961.js 101.02 kB โ gzip: 34.13 kB
dist/assets/vendor-ead95c8e.js 290.76 kB โ gzip: 92.84 kB
dist/assets/@chakra-ui/react-03b491e2.js 300.44 kB โ gzip: 99.47 kB
dist/assets/index-7c561f25.js 422.93 kB โ gzip: 109.15 kB
๋ ํผ๋ฐ์ค
- Splitting vendor chunk with Vite and loading them async
- Vite code splitting that just works | Sambit Sahoo
- Chunking-strategy | Vite
๊ธ ์์ ์ฌํญ์ ๋ ธ์ ํ์ด์ง์ ๊ฐ์ฅ ๋น ๋ฅด๊ฒ ๋ฐ์๋ฉ๋๋ค. ๋งํฌ๋ฅผ ์ฐธ๊ณ ํด ์ฃผ์ธ์
'๐ช Programming' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[JS] split() ๋ฉ์๋์์ ๋น ๋ฌธ์์ด์ด ์๊ธฐ๋ ์๋ฆฌ (0) | 2024.05.24 |
---|---|
[React] Proxy๋ฅผ ํ์ฉํ Custom Lazy Import (0) | 2024.05.24 |
[Markdown] GitHub ๋งํฌ๋ค์ด ์์ฑ ๊ฟํ ๋ชจ์ (0) | 2024.05.23 |
[JS] ์๋ฐ์คํฌ๋ฆฝํธ ES2023 ๋ถ๋ณ์ฑ ๋ฐฐ์ด ๋ฉ์๋ ํบ์๋ณด๊ธฐ (0) | 2024.05.23 |
[JS] ์์ด ์ถ์ฝ์ด ๊ด๋ จ ์ ํธ๋ฆฌํฐ ํจ์ ๋ชจ์ (0) | 2024.05.22 |
๋๊ธ
์ด ๊ธ ๊ณต์ ํ๊ธฐ
-
๊ตฌ๋
ํ๊ธฐ
๊ตฌ๋ ํ๊ธฐ
-
์นด์นด์คํก
์นด์นด์คํก
-
๋ผ์ธ
๋ผ์ธ
-
ํธ์ํฐ
ํธ์ํฐ
-
Facebook
Facebook
-
์นด์นด์ค์คํ ๋ฆฌ
์นด์นด์ค์คํ ๋ฆฌ
-
๋ฐด๋
๋ฐด๋
-
๋ค์ด๋ฒ ๋ธ๋ก๊ทธ
๋ค์ด๋ฒ ๋ธ๋ก๊ทธ
-
Pocket
Pocket
-
Evernote
Evernote
๋ค๋ฅธ ๊ธ
-
[JS] split() ๋ฉ์๋์์ ๋น ๋ฌธ์์ด์ด ์๊ธฐ๋ ์๋ฆฌ
[JS] split() ๋ฉ์๋์์ ๋น ๋ฌธ์์ด์ด ์๊ธฐ๋ ์๋ฆฌ
2024.05.24 -
[React] Proxy๋ฅผ ํ์ฉํ Custom Lazy Import
[React] Proxy๋ฅผ ํ์ฉํ Custom Lazy Import
2024.05.24 -
[Markdown] GitHub ๋งํฌ๋ค์ด ์์ฑ ๊ฟํ ๋ชจ์
[Markdown] GitHub ๋งํฌ๋ค์ด ์์ฑ ๊ฟํ ๋ชจ์
2024.05.23 -
[JS] ์๋ฐ์คํฌ๋ฆฝํธ ES2023 ๋ถ๋ณ์ฑ ๋ฐฐ์ด ๋ฉ์๋ ํบ์๋ณด๊ธฐ
[JS] ์๋ฐ์คํฌ๋ฆฝํธ ES2023 ๋ถ๋ณ์ฑ ๋ฐฐ์ด ๋ฉ์๋ ํบ์๋ณด๊ธฐ
2024.05.23