Webpack์์ Vite๋ก ๊ฐ๋ฐ ํ๊ฒฝ ๋ง์ด๊ทธ๋ ์ด์ ํ๊ธฐ
- #react
- #vite
- #esbuild
- #swc
๊ธ์ ์์ํ๊ธฐ์ ์์
์๋์ ์น์ Create React App(CRA)์ react-script 4๋ฒ์ ์ ๊ธฐ๋ฐ์ผ๋ก Webpack4๋ฅผ ํตํด ๋ฒ๋ค๋ง๋ ํ๋ก์ ํธ์์ ์ด์๋๊ณ ์์ต๋๋ค.(2022๋ 7์๋ถ๋ก NextJS๋ก ๋ง์ด๊ทธ๋ ์ด์ ๋์์ต๋๋ค.) ์ ์ฌ ์ด๊ธฐ์๋ ๋ณ๋ค๋ฅธ ๋ถํธํจ ์์ด ๊ฐ๋ฐ์ ์งํํ ์ ์์์ง๋ง, ์๋น์ค ๊ท๋ชจ๊ฐ ๋น ๋ฅด๊ฒ ํ๋๋๋ฉด์ ์ฝ๋ ๋ฒ ์ด์ค๋ ํจ๊ป ์ปค์ง๊ฒ ๋์๊ณ ์ด๋ก ์ธํด ๋๋ฆฐ ๋ก์ปฌ ๊ฐ๋ฐ ํ๊ฒฝ์ ๋ํ ๋ถํธํจ์ ๋๋ผ๋ ์ฃผ๋ณ ์ด์ผ๊ธฐ๊ฐ ์กฐ๊ธ์ฉ ๋ค๋ฆฌ๊ธฐ ์์ํ์ต๋๋ค.
Webpack์ ์์ง๊น์ง ํ๋ก ํธ์๋ ์ํ๊ณ์ ์ฃผ๋ฅ ๋น๋ ๋๊ตฌ์ด๊ณ ๊ณ์ ๋ฐ์ ํ๊ณ ์์ง๋ง, CRA์ ๊ธฐ๋ณธ์ผ๋ก ์ค์ ๋์ด ์๋ ๋ฒ๋ค๋ฌ์ธ babel์ Javascript๋ฅผ ์ด์ฉํ ๋ฒ๋ค๋ฌ๋ผ๋ ํ์์ ํ๊ณ์ ์ ๋์ง ๋ชปํด ์๋๋ฉด์์ ๋ค์ณ์ง ์ ๋ฐ์ ์์ผ๋ฉฐ go๋ฅผ ์ฌ์ฉํ esbuild, rust๋ฅผ ์ฌ์ฉํ swc์ ๊ฐ์ด ๋ณ๋ ฌ ์์ ์ ๊ฐ๋ฅํ๊ฒ ํ๋ ์ธ์ด๋ฅผ ํตํด ๊ฐ์ ์ ๊พํ๋ ๋๊ตฌ๋ค์ด ์ ์ ์ฃผ๋ชฉ๋ฐ๊ณ ์๋ ์ํฉ์ ๋๋ค. ์ด์ ์ ํฌ ํ๋ก ํธ์๋ํ์ Webpack์ ๋ฉ์ธ์ผ๋ก esbuild ํน์ swc๋ฅผ ํตํด ๊ฐ์ ํ ์ง, Snowpack ํน์ Vite๋ฅผ ํตํ ๊ฐ์ ์ ๊พํ ์ง ํ์๋ฅผ ๊ฑฐ์น๊ฒ ๋์๊ณ Vite ๋์ ์ ๊ฒฐ์ ํ์ต๋๋ค. Vite๋ก ๊ฐ์ ์ ๊ฒฐ์ ํ๊ฒ๋ ์ด์ ๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
- swc์ esbuild๊ฐ ์์ง ์์ ์ ์ธ ๊ถค๋์ ์ฌ๋ผ์ค์ง ์์๋ค๋ ์ด์ผ๊ธฐ๊ฐ ์์ผ๋ฉฐ, babel ์์กด์ฑ์ ๊ฑท์ด๋ด๊ธฐ ์ด๋ ค์ด ๋ชจ๋์ด ์กด์ฌ
- Snowpack์ deprecated
- Vite๊ฐ ์ ๊ณตํ๋ ์ฌ์ ๋ฒ๋ค๋ง์ ํตํ ๋ก์ปฌ ์คํํธ ์์ ์๊ฐ์ ํ๊ธฐ์ ์ธ ๊ฐ์ ์ ๋ํ ๊ธฐ๋๊ฐ
- Vite์ ๊ฐํธํ ์ค์ ์ ํตํ ๊ฐ๋ฐ ๋ฆฌ์์ค ์ ๊ฐ
๊ฒฐ๋ก ๋ถํฐ ๋งํ์๋ฉด ์ ํฌ๋ Vite๋ฅผ ํ๋ก๋์ ํ๊ฒฝ์ด ์๋ ๋ก์ปฌ ๊ฐ๋ฐํ๊ฒฝ๊ณผ ํ ์คํธ ์๋ฒ ๋น๋์์๋ง ์ฌ์ฉํ๊ณ ์์ต๋๋ค. ๋ฐ์ ์ด์ด์ง ๊ธ์์๋ Vite์ react-script๋ฅผ ํผ์ฉํด์ ์ฌ์ฉํ๊ธฐ ์ํ ์ค์ ๋ค์ด ํฌํจ๋์ด ์์ผ๋ ์ฐธ๊ณ ํด์ฃผ์ธ์. ๊ทธ ์ด์ ๋ ๊ธ์ ์ ์ผ ํ๋จ๋ถ์ ์ ํ ๊ฒฐ๋ก ์์ ๋ง์๋๋ฆฌ๊ฒ ์ต๋๋ค.
Vite๋?
์ฐจ์ธ๋ ํ๋ก ํธ์๋ ๋น๋ ๋๊ตฌ๋ผ๊ณ ์์นญํ๋ Vite๋ ๋น ๋ฅธ ์๋ฒ ์์๊ณผ HMR์ ๋ด์ธ์ฐ๋ฉฐ ํ๋ก ํธ์๋ ์ํ๊ณ์์์ ์์ญ์ ํ์ฅํด๋๊ฐ๊ณ ์์ต๋๋ค. ์ฒ์์๋ Vue์ ์ฐฝ์์์ธ Evan You๊ฐ Vue ์ ์ฉ ๋น๋ ๋๊ตฌ๋ก ๊ฐ๋ฐํ์ง๋ง, ํ์ฌ๋ React, Svelte ๋ฑ ๋ง์ ํ๊ฒฝ์์ ์ฌ์ฉ ๊ฐ๋ฅํฉ๋๋ค.
Vite๋ ESM ๊ธฐ๋ฐ์ ๋ฒ๋ค๋ง๊ณผ, ์ฌ์ ๋ฒ๋ค๋ง์ ํตํด ๊ฐ๋ฐ์๋ค์๊ฒ ํฅ์๋ ๊ฐ๋ฐ ๊ฒฝํ์ ์ ๊ณตํฉ๋๋ค. Webpack์ ๊ฐ๋ฐ ์๋ฒ ์คํ์ ์ ์ฒด ํ์ผ์ ์ฒ์๋ถํฐ ๋ฒ๋ค๋งํ๋ ๋ฐ๋ฉด, Vite๋ ์ค์น์ ๋์์ ์์ ์ด ์ ์ผ์ด๋์ง ์๋ ์์กด์ฑ ํจํค์ง๋ค์ ESBuild๋ฅผ ์ฌ์ฉํ์ฌ ์ฌ์ ๋ฒ๋ค๋งํด๋๊ณ , ์์ ์ด ์ฆ์ ๋ด๋ถ ์์ค ์ฝ๋๋ ๋ธ๋ผ์ฐ์ ์์ esm์ ์ด์ฉํ์ฌ ๋ฒ๋ค๋ฌ ์ญํ ์ ์ํํ๊ฒํ์ฌ ์ต์ ํํฉ๋๋ค.
๋น๋ ๊ณผ์ ์์๋ ์กฐ๊ธ ๋ค๋ฅธ ๋ฐฉ์์ ์ฌ์ฉํฉ๋๋ค. ๊ฐ๋ฐ ํ๊ฒฝ์์๋ ๋น ๋ฅธ ์๋๋ฅผ ๊ฐ์ง esbuild๋ฅผ ์ฌ์ฉํ์ฌ์ง๋ง ๋น๋ ๊ณผ์ ์์๋ ์ฝ๋ ์คํ๋ฆฌํ ๊ณผ CSS ๊ด๋ จ ์ฒ๋ฆฌ๊ฐ ๋ฏธ๋นํ์ฌ ์ด๋ฌํ ์ญํ ์ ์ ์ํํด์ค ์ ์๋ rollup์ ์ฌ์ฉํ๊ณ ์์ต๋๋ค.
๊ธฐ๋ณธ ์ค์
yarn add -D vite @vitejs/plugin-react
Vite์ react ํ๊ฒฝ์์ Fast Refresh ๊ธฐ๋ฅ์ ํ์ฑํํด์ฃผ๊ธฐ ์ํด react ํ๋ฌ๊ทธ์ธ์ ์ค์นํฉ๋๋ค. ์์ ๋จ์์ ํ๋ก์ ํธ๋ผ๋ฉด ์๋ง ์ด ์ ๋ ์ค์น๋ง์ผ๋ก๋ Vite ์ค์ ํ์ผ์ ์์ฑํ ์ ์์ต๋๋ค.
// <root>/vite.config.ts
import {defineConfig} from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()]
});
ํ๋ก์ ํธ์ ๋ฃจํธํด๋์ vite.config.ts์ react ํ๋ฌ๊ทธ์ธ์ ์ฃผ์ ํด์ค ๋ชจ์ต์ ๋๋ค.
<!-- <root>/index.html -->
<html>
<head>
<link href="./public/manifest.json" rel="manifest" />
<link href="./public/favicon.ico" rel="shortcut icon" />
</head>
<body>
<!-- ... -->
<div id="root"></div>
<script type="module" src="/src/index.tsx"></script>
<!-- ... -->
</body>
</html>
// <root>/vite.config.ts
import {defineConfig} from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
publicDir: false // vite์ public ํด๋ ์ค์ ์ ๋นํ์ฑํํฉ๋๋ค.
});
๋ค์์ Vite์์ ์ฌ์ฉํ index.html ์์ฑ์ด ํ์ํฉ๋๋ค. Vite๋ ๊ธฐ๋ณธ์ ์ผ๋ก public์ด ์๋ root ํด๋๋ฅผ ๊ธฐ์ค์ผ๋ก index.html์ ๊ฐ์ ธ์ค๋ฉฐ, ESM ํ์ฉ์ ์ํด <script type=โmoduleโ></script> ํ๊ทธ๋ฅผ ํตํด์ ๋ฆฌ์กํธ ์ดํ๋ฆฌ์ผ์ด์ ์ ์ง์ ํ์ผ์ ๊ฐ์ ธ์์ผํฉ๋๋ค.
๋ํ ๊ธฐ๋ณธ public ํด๋ ์ค์ ์ react-script์ ๋ง์ฐฌ๊ฐ์ง๋ก root/public ํด๋์ ๋๋ค. ํ์ฌ public ํด๋์๋ react-script์์ ์ฌ์ฉํ๋ index.html ํ์ผ์ด ์กด์ฌํฉ๋๋ค. ์ด ํ์ผ์ด Vite์ public ํด๋์ ์กด์ฌํ๋ฉด ์๋ฌ๊ฐ ๋ฐ์ํ๊ฒ ๋๋ฏ๋ก Vite config์์ public ํด๋๋ฅผ false ์ฒ๋ฆฌํ๊ณ , public file๋ค์ ๊ฒฝ๋ก๋ ์๋๊ฒฝ๋ก๋ก ๋ณ๊ฒฝํฉ๋๋ค.
ํ๋ฌ๊ทธ์ธ ์ค์น
yarn add -D vite-plugin-env-compatible vite-plugin-checker
vite-plugin-env-compatible
-
Vite์์๋ process.env ๋์ import.meta.env ๊ฐ์ฒด๋ฅผ ํตํด ํ๊ฒฝ ๋ณ์์ ์ ๊ทผํด์ผํฉ๋๋ค.
-
import.meta.env ๋์ ๊ธฐ์กด์ฒ๋ผ process.env๋ฅผ ์ฌ์ฉํ ์ ์๊ฒ ํ๋ฌ๊ทธ์ธ ์ฌ์ฉํด์ react-script์์ ํธํ์ฑ์ ๊ทธ๋๋ก ์ ์งํ ์ ์๊ฒ ํด์ค๋๋ค.
vite-plugin-checker
-
Vite๋ ํ์ ์คํฌ๋ฆฝํธ ํ์ผ์ ๋ํ ๋ณํ ์์ ๋ง ์ํํ๊ณ ํ์ ๊ฒ์ฌ๋ ์ํํ์ง ์์ต๋๋ค. eslint ๋ํ ๋ง์ฐฌ๊ฐ์ง์ ๋๋ค.
-
๊ณต์ ๋ฌธ์์๋ tsc โnoEmit์ ํตํด build ํ๋ก์ธ์ค์์์ ๊ฒ์ฆ์ ๊ถ์ฅํ๊ณ ์์ง๋ง, ๊ฐ๋ฐ ๋จ๊ณ์์ ๋ฏธ๋ฆฌ lint์ ํ์ ๊ฒ์ฌ๋ฅผ ์ํํ๊ธฐ ์ํด ํ๋ฌ๊ทธ์ธ์ ์ค์นํฉ๋๋ค.
config ํ์ผ์ plugin์ด ์ ์ฉ๋ ๋ชจ์ต
// <root>/vite.config.ts
import {defineConfig} from 'vite';
import react from '@vitejs/plugin-react';
import checker from 'vite-plugin-checker';
import envCompatible from 'vite-plugin-env-compatible';
export default defineConfig({
publicDir: false,
plugins: [react(), checker({typescript: true, eslint: {lintCommand: 'eslint ./src --ext .ts,.tsx'}}), envCompatible({prefix: 'REACT_APP'})]
});
์ ๋ ๊ฒฝ๋ก๋ฅผ ์ฌ์ฉํ๊ณ ์๋ ๋ด๋ถ ์ฝ๋ alias ์ง์
import path from 'path';
export default defineConfig({
// ...
resolve: {
alias: {
pages: path.resolve(__dirname, 'src/pages'),
components: path.resolve(__dirname, 'src/components')
}
}
// ...
});
์๋ ๊ฒฝ๋ก๋ฅผ ํตํด ๋ชจ๋์ ๊ฐ์ ธ์ค๊ณ ์๋ค๋ฉด ์ง์ alias๋ฅผ ์ค์ ํด์ฃผ์ด์ผ ํฉ๋๋ค. Webpack๊ณผ ์ ์ฌํ ํํ๋ก ์ค์ ์ด ๊ฐ๋ฅํฉ๋๋ค. ํค ๊ฐ์๋ alias ๋ช ์นญ, ๋ฐธ๋ฅ์๋ resolve๋ ๊ฒฝ๋ก๋ฅผ ์ค์ ํด์ค๋๋ค.
Polyfill ์ค์น
yarn add -D rollup-plugin-node-polyfills @esbuild-plugins/node-globals-polyfill
์ ํฌ ํ๋ก์ ํธ๋ Webpack4๊ฐ ์ ๊ณตํ๊ณ ์๋ NodeJS ๋ด์ฅ ๋ชจ๋ ํด๋ฆฌํ์ ์์กดํ๊ณ ์๋ ์ฝ๋๋ค์ด ์กด์ฌํ์ต๋๋ค. Vite์์๋ ์ด๋ฌํ ํด๋ฆฌํ์ ์ ๊ณตํ๊ณ ์์ง ์์๊ณ , ์ง์ ์ค์ ์ด ํ์ํฉ๋๋ค.
์ด๋ฌํ ์ฝ๋๋ ๋ด๋ถ ํ๋ก์ ํธ์๋ ์กด์ฌํ ์ ์๊ณ , ์ค์น๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์๋ ์กด์ฌํ ์๋ ์์ต๋๋ค. ์ ํฌ๋ crypto๋ผ๋ NodeJS ๋ด์ฅ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ๊ณ ์์๊ณ , ์ด๋ฅผ crypto-browserify๋ฅผ ์ค์น ํ ๋์ฒดํ์์ต๋๋ค. ์ด์ธ์๋ ์ค์น๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์์ ์ฌ์ฉ๋๊ณ ์๋ค๋ฉด ๋ค๋ฅธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ก ๋์ฒดํ๊ฑฐ๋ ํด๋ฆฌํ์ ์ค์ ํด์ค์ผํฉ๋๋ค.
์ ํฌ๊ฐ ์ฌ์ฉํ๊ณ ์๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ค์์๋ stream๊ณผ buffer์ ๋ํด ์๋ฌ๊ฐ ๋ฐ์ํ๋ ๊ฒฝ์ฐ๊ฐ ์์์ผ๋ฉฐ ์ด๋ฅผ rollup๊ณผ esbuild ํด๋ฆฌํ ์ค์ ์ ํตํด ํด๊ฒฐํ์์ต๋๋ค. ์ด์ธ์ ํด๋ฆฌํ๋ค๋ ์ค์นํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋ค์ด๊ฐ ์๊ธฐ ๋๋ฌธ์ ํ์์ ๋ฐ๋ผ ์ถ๊ฐํ์ค ์ ์์ต๋๋ค.
import {NodeGlobalsPolyfillPlugin} from '@esbuild-plugins/node-globals-polyfill';
import rollupNodePolyFill from 'rollup-plugin-node-polyfills';
export default defineConfig({
// ...
build: {
rollupOptions: {
plugins: [rollupNodePolyFill()]
}
},
resolve: {
alias: {
// ...
stream: 'rollup-plugin-node-polyfills/polyfills/stream',
buffer: 'rollup-plugin-node-polyfills/polyfills/buffer-es6'
}
},
optimizeDeps: {
esbuildOptions: {
plugins: [
NodeGlobalsPolyfillPlugin({
process: true,
buffer: true
})
]
}
}
});
๊ฒฐ๋ก
๊ฐ๋จํ ์ค์
Vite๊ฐ ์์ฒด์ ์ผ๋ก esbuild์ rollup ์ค์ ์ ์ ๊ณตํด์ ์ต์ํ์ ์ค์ ์ผ๋ก ๊ตฌ๋์ด ๊ฐ๋ฅํ๋ฉฐ react-script์ ๋ฌ๋ฆฌ ์์ฒด์ ์ผ๋ก config ํ์ผ์ ์ ๊ณตํ๊ณ ์์ผ๋ฉฐ ํ์ ๋ ์ง์ํ๊ณ ์์ด์ ์์ฝ๊ฒ ์ปค์คํ ํ ์ค์ ์ ์ ์ฉํ ์ ์์ต๋๋ค.
๋ก์ปฌ ์๋ฒ ์์ ์๋ ๊ฐ์
๋ชฉํ๋ก ํ๋ ๋ก์ปฌ ๊ฐ๋ฐ ํ๊ฒฝ์์์ ํ๋ก์ ํธ ์คํ ์๊ฐ์ ์ฝ 40์ด์์ 1์ด ๋ด์ธ๋ก ํฌ๊ฒ ๊ฐ์ ๋์์ผ๋ฉฐ ์๋ ๊ฐ์ ์ ์ํฅ์ ์ค ์ฌ์ ๋ฒ๋ค๋ง๋ ํ์ผ๋ค์ node_modules/.vite/deps ํด๋๋ฅผ ๋ณด๋ฉด ํ์ธํ ์ ์์ต๋๋ค.
๋ก์ปฌ์์๋ง Vite๋ฅผ ์ฌ์ฉํ๊ฒ๋ ์ด์
- ๊ฐ๋ฐ ๋น๋์ ํ๋ก๋์ ๋น๋ ๋ฐฉ๋ฒ์ด ๋ค๋ฅธ ๋ฌธ์ ์ ์ ํด๊ฒฐํ๊ธฐ ์ํด Vite์์ ์ต์ ํ ๋๊ตฌ๋ฅผ ์ ๊ณตํ์ง๋ง, ์ค์ ๊ฐ์ ๋ฐ๋ผ ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์ฌ์ง๊ฐ ์กด์ฌํ๋ฉฐ ํ ์คํธ ๊ณผ์ ์์ ๋ฐํ์์ ๋ฌธ์ ๊ฐ ์๊ธฐ๋ ๊ฒฝ์ฐ๋ฅผ ๊ฒฝํํ์์ต๋๋ค.
- ๋น๋ ์๊ฐ๊ณผ ๋ฒ๋ค๋ง ๊ฒฐ๊ณผ์ ์ฉ๋์ ๋ํ ๊ฐ์ ์ด ์๋ฏธ์์ ์ ๋๋ก ๋ฐ๋์ง ์์ต๋๋ค.
๋ฌผ๋ก ์ค์ ์ ํตํด ๊ฐ์ ์ ์ฌ์ง๊ฐ ๋จ์์์ง๋ง ์ ํฌ ํ์ ๊ณง์ด์ด NextJS๋ก์ ๋ง์ด๊ทธ๋ ์ด์ ์ ์ค๋นํ๊ณ ์๊ธฐ ๋๋ฌธ์ ๋ ์ด์ ์๊ฐ์ ์์ ํ์๋ฅผ ๋๋ผ์ง ๋ชปํ๊ณ , ์ด๋ฏธ ๋ก์ปฌ ์๋ฒ ์์ ์๋ ๊ฐ์ ์ด๋ผ๋ ์ต์ฐ์ ๋ชฉํ๋ฅผ ๋น๊ต์ ์์ฝ๊ฒ ๋ฌ์ฑํ์๋ค๋ ์ ์์ ๋ง์กฑํ๋ฉฐ ์ฌ์ฉํ๊ณ ์์์ต๋๋ค.
๋ง๋ฌด๋ฆฌ
esbuild๋ ์์ง 0์ ๋ ๋ฒ์ ์์๋ ์ํ๊ณ๋ฅผ ๋จ๊ฒ๊ฒ ๋ฌ๊ตฌ๊ณ ์๊ณ , swc ๋ํ vercel๊ณผ์ ํ๋ ฅ์ ํตํด ํ์ธต ๋ ๋ง์ ๊ฐ๋ฐ์๋ค์๊ฒ ์ฃผ๋ชฉ์ ๋ฐ๊ณ ์์ต๋๋ค. ์๋ก์ด ๋๊ตฌ๋ค์ด ๋์ฌ์๋ก ๊ฐ๋ฐ์๋ค์ด ๊ณต๋ถํด์ผ ํ ๊ฒ๋ค์ด ๋ง์์ง๊ธฐ๋ ํ์ง๋ง ์คํ๋ ค ๊ฐ๋ฐ์๋ผ๋ ํ์ดํ์ด ๊ฐ์ง ๋งค๋ ฅ์ด ์๋๊ฐ ์๊ฐํฉ๋๋ค. ์์ผ๋ก ํ๋ก ํธ์๋ ์ํ๊ณ์ ๊ฒ์ ์ฒด์ธ์ ๊ฐ ๋ ๋น๋ ๋๊ตฌ๋ esbuild๊ฐ ๋ ์ง swc๊ฐ ๋ ์ง, ํน์ ์์ง ์ ์๋ ค์ง์ง ์์ ๋ค๋ฅธ ๋๊ตฌ์ธ์ง ์์ผ๋ก์ ๊ณ์ ๋ฐ์ ํด๋๊ฐ ๋ฒ๋ค๋ง ๋๊ตฌ๋ค์ ๊ท์ถ๊ฐ ์ฃผ๋ชฉ๋ฉ๋๋ค.
์ฐธ๊ณ ํด๋ณด๋ฉด ์ข์ ์๋ฃ
Vite: Rethinking Frontend Tooling
swc์ ์น ๊ฐ๋ฐ์ ๋ฏธ๋