Webpack
Webpack์ ๋ชจ๋ ์๋ฐ์คํฌ๋ฆฝํธ ์ดํ๋ฆฌ์ผ์ด์ ์ ์ํ ๋ชจ๋ ๋ฒ๋ค๋ฌ์ด๋ค.
Webpack์ ํ๋ก ํธ์๋ ๊ฐ๋ฐ ํ๊ฒฝ์์ ์ฃผ๋ก ์ฌ์ฉํ๊ณ ์๋ ๋ชจ๋ ๋ฒ๋ค๋ฌ์ด๋ค. HTML, CSS, Javascript, ์ด๋ฏธ์ง ๋ฑ์ ๋ชจ๋ ํ๋์ ํ์ผ๋ก ๋ฒ๋ค๋งํ๋ค. ์์กด ๋ชจ๋์ด ํ๋์ ํ์ผ๋ก ๋ฒ๋ค๋ง๋๋ค.
React์ Webpack
ํ๋ก์ ํธ๋ฅผ ์์ํ๋ฉด์ Webpack ๋ฒ๋ค๋ฌ๋ฅผ ์ฌ์ฉํด์ ๋ฆฌ์กํธ ๊ฐ๋ฐ ๋น๋ ํ๊ฒฝ์ ์ง์ ๊ตฌ์ฑํ๊ธฐ๋ก ํ์๋ค.
๊ทธ๋ฅ CRA๋ฅผ ์ฐ๋ฉด ์๋๋?
์นํฉ ์ค์ ์ ์ฌ์ค ๊ท์ฐฎ๊ณ boilerplate๋ผ๊ณ ์์ฑํด์ฃผ์ด์ผํ๋ ๋ถ๋ถ์ด ๋ง์์ ์ด๋ฅผ ๊ฐ๋จํ๊ฒ ์ค์ ํ๊ณ ์ถ์ ๊ฒฝ์ฐ์๋ Create React App์ธ CRA๋ฅผ ์ฌ์ฉํ ์ ์๋ค.
๋ฆฌ์กํธ ๊ณต์๋ฌธ์์๋ ์ต์ํ์ ํด์ฒด์ธ์ผ๋ก ์์ํ๋ ๋ฐฉ๋ฒ์ผ๋ก CRA๋ฅผ ์๊ฐํ๊ณ ์๋ค..ใ
ํ์ง๋ง ์ง์ ํด์ฒด์ธ์ ๊ตฌ์ฑํด๋ณด๋ฉด์ ์๋ ์๋ฆฌ์ ๋ํด ํ์ตํ๋ ๊ฒ์ด ๋ ์๋ฏธ ์๋ค๊ณ ์๊ฐํ๋ค. ๊ทธ๋์์ ๋น ๋ฅด๊ฒ ํ๋ก์ ํธ๋ฅผ ๋๋ด๋ ๊ฒ์ ๋น์ค์ด ์์๋ค๋ฉด ์ด๋ฒ ํ๋ก์ ํธ๋ฅผ ํ๋ฉด์๋ ํ์ต์ ์ด์ ์ ๋ง์ถ๊ธฐ๋ก ํ๋ค.
ํ๋ก์ ํธ์์ ์ฌ์ฉํ ๊ธฐ์ ์คํ
์ด ๊ธ์์๋ ์๋ ๊ธฐ์ ๋ค์ Webpack๊ณผ ํจ๊ป ์ค์นํ๊ณ ์ค์ ํ์ผ์ ๊ตฌ์ฑํ๋ค.
- | - | ๋ฒ์ |
ํจํค์ง ๋งค๋์ | NPM | 8.11.0 |
UI ๋ผ์ด๋ธ๋ฌ๋ฆฌ | React | 18.2.0 |
์ธ์ด | Typescript | 4.9.4 |
๋ฒ๋ค๋ฌ | Webpack | 5.75.0 |
ํธ๋์คํ์ผ๋ฌ | Babel | - |
์คํ์ผ๋ง | TailwindCSS | |
๋ผ์ฐํ | React Router | |
์ํ ๊ด๋ฆฌ | React Query | |
ํ ์คํธ | Jest, React Test Library | 29.3.1 / - |
๋ฆฐํธ / ํฌ๋งคํฐ | ESLint, Prettier | 8.30.0 / 2.8.1 |
package.json ๊ตฌ์ฑํ๊ธฐ
npm init -y
yes ์ต์ ์ ์ฌ์ฉํด์ package.json ํ์ผ์ ์์ฑํ๋ค.
webpack ์ค์น
npm i -D webpack webpack-cli
webpack ๊ด๋ จ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ค์นํ๋ค.
package.json ๋ช ๋ น์ด
// package.json
"scripts": {
"config": "webpack --target=browserslist --entry=./src/main.tsx --output-path=public",
"dev": "npm run config -- --mode=development"
},
CLI๋ฅผ ์ด์ฉํด์ webpack ๊ด๋ จ ์ต์ ์ ์ค์ ํด์ ๋น๋ ๋ฐ ์คํ ํด๋ณผ ์ ์๋ค.
npm run dev
์คํ์
webpack ๊ฐ๋ฐ, ๋น๋ ๊ตฌ์ฑ ํ์ผ
์์ฒ๋ผ CLI๋ก ์ง์ ๋ช ๋ น์ด์ ์ต์ ์ ์์ฑํด์ค ์ ์์ง๋ง ์ต์ ์ด ๋ง์์ง ์๋ก ๋ช ๋ น์ด๊ฐ ๊ธธ์ด์ง๊ณ ๊ด๋ฆฌํ๊ธฐ๊ฐ ์ด๋ ค์์ง๋ค.
๊ฐ๋ฐ, ๋น๋ ๋ ์ต์ ์ ์ค์ ํด์ฃผ๋ ํ์ผ์ ๋ถ๋ฆฌํด์ ์์ฑํด์ค๋ค.
webpack ๊ธฐ๋ณธ ์ค์
// webpack/webpck.common.js : webpack ๊ธฐ๋ณธ ์ค์ ํ์ผ
import { resolve } from 'node:path';
import paths from "./paths.js";
const commonConfig = {
target: ["web", "browserslist"],
entry: {
app: paths.src,
},
output: {
path: paths.build,
filename: "[name].bundle.js",
assetModuleFilename: 'assets/[name].[contenthash][ext][query]',
clean: true,
},
resolve: {
modules: ["node_modules"],
extensions: [".js", ".ts", ".tsx"],
},
};
export default commonConfig;
- entry
: ์ดํ๋ฆฌ์ผ์ด์
์ ์ต์ด ์ง์
์
- output
: ์นํฉ์ ์คํํ๊ณ ๋ ๊ฒฐ๊ณผ๋ฌผ์ ๊ฒฝ๋ก. ์ด๋ป๊ฒ / ์ด๋์ output์ ์ค์ ํ ์ง์ ๋ํด ์์ฑํด์ค ์ ์๋ค.
- path
: output์ ๊ฒฝ๋ก
- filename
: ๋ฒ๋ค๋ง์ด ์๋ฃ๋(์นํฉ์ด ์คํ๋๊ณ ๋) ํ์ผ ์ด๋ฆ
- assetModuleFilename
:
- clean
: ๋ด๋ณด๋ด๊ธฐ ์ ์ output ๋๋ ํ ๋ฆฌ๋ฅผ ์ ๋ฆฌํ ์ง ๋ง์ง
- resolve
: ๋ชจ๋์ด ํด์๋๋ ๋ฐฉ์์ ๋ณ๊ฒฝํ๋ค.
- extensions
: ํ์ฅ์๋ฅผ ์์๋๋ก ํด์ํ๋ค. ํ์ผ ์ด๋ฆ์ด ๋์ผํ์ง๋ง ํ์ฅ์๊ฐ ๋ค๋ฅธ ๊ฒฝ์ฐ, ์์์ ๋ถํฐ ํด์ํ๋ค. ์ด๋ฅผ ์ฌ์ฉํ๋ฉด importํ ๋, ํ์ฅ์๋ฅผ ์๋ตํ ์ ์๋ค.
webpack ๊ฐ๋ฐ ์ค์
// webpack/webpack.dev.js
import { merge } from "webpack-merge";
import commonConfig from "./webpack.common.js";
import devServer from "./server.js";
const devConfig = merge(commonConfig, {
mode: "development",
devtool: "eval-cheap-source-map",
devServer,
});
export default devConfig;
- mode
: ๊ฐ๋ฐ ํ๊ฒฝ์ธ์ง, ๋ฐฐํฌ ํ๊ฒฝ์ธ์ง์ ๋ํด ์์ฑํ๋ค.
- devtool
: ๋ฒ๋ค๋ง ๋ ํ์ผ๊ณผ ์๋ณธ ํ์ผ์ ์ฐ๊ฒฐํด์ฃผ๋ ์์ค๋งต ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ค. ๋ฒ๋ค๋ง๋ ํ์ผ์ ์ฝ๊ธฐ ์ด๋ ต๊ฒ ๋๋
ํ๊ฐ ๋๊ธฐ ๋๋ฌธ์ ์ด๋ค ๋ถ๋ถ์์ ์๋ฌ๊ฐ ๋ฐ์ํ๋์ง ๋๋ฒ๊น
์ ํ๊ธฐ ์ํด์ ์ด์ฉํ๋ค.
Webpack devtool ๋ฌธ์์ sourcemap์ ๋ํ ์ฌ๋ฌ๊ฐ์ง ์ต์ ์ด ์๊ณ ์ฑ๋ฅ ๋ฑ์ ๋ํ ์ค๋ช ์ด ์๋ค.
๊ฐ๋ฐ ์์๋ eval
, eval-source-map
, eval-cheap-source-map
, eval-cheap-module-source-map
๋ค๊ฐ์ง๋ฅผ ์ถ์ฒํ๊ณ ์๋๋ฐ ๊ทธ ์ค
// webpack/server.js : webpack devServer ์ค์
const devServer = {
host: "localhost",
port: 8000,
hot: true,
open: false,
compress: true,
liveReload: true,
static: ["public"],
historyApiFallback: true,
client: {
logging: "info",
overlay: true,
reconnect: 3,
},
watchFiles: {
paths: ["src/**/*.*", "public/**/*.*"],
},
};
export default devServer;
๊ฐ๋ฐ ํ๊ฒฝ์ ๋ํ ์ค์ ์ ๊ด๋ฆฌ๋ฅผ ๋ ์ฝ๊ฒ ํ๊ธฐ ์ํด server.js๋ก ๋ถ๋ฆฌํ๋ค.
- devServer
- compress
: ์์ถ ํ์ฑํ
- historyApiFallback
-> ์ ๋ฆฌ ํ์ https://webpack.kr/configuration/dev-server/#devserverhistoryapifallback
- hot
: ๋ธ๋ผ์ฐ์ ๋ฅผ ์๋ก๊ณ ์นจํ์ง ์์๋ ์ค์๊ฐ์ผ๋ก ๋ฐ์ํด์ฃผ๋ HMR
๊ธฐ๋ฅ์ ์ฌ์ฉํ ์ ์๋ค.
webpack ๋ฐฐํฌ ์ค์
// webpack/webpack.prod.js : webpack ๋ฐฐํฌ ํ๊ฒฝ ์ค์
import { merge } from "webpack-merge";
import commonConfig from "./webpack.common.js";
const prodConfig = merge(commonConfig, {
mode: "production",
devtool: false,
output: {
...commonConfig.output,
filename: "[name].min.js",
},
});
export default prodConfig;
๊ฐ๋ฐ ํ๊ฒฝ๊ณผ ๋น์ทํ ๋ฐฉ์์ผ๋ก ๋ฐฐํฌ ์ค์ ํ์ผ๋ ๊ตฌ์ฑํ๋ค.
Webpack ๊ฐ๋ฐ ์๋ฒ ๊ตฌ์ฑ
npm i -D webpack-dev-server
์นํฉ ๊ฐ๋ฐ ์๋ฒ๋ฅผ ์ฌ์ฉํ๋ฉด ์นํฉ ๋ช ๋ น์ด๋ฅผ ๋งค๋ฒ ์คํํ์ง ์๊ณ ์ฝ๋ ์ ์ฅ๋ง ํ๋ฉด ๋ธ๋ผ์ฐ์ ๋ฅผ ์๋ก ๊ณ ์นจํด์ค๋ค.
package.json ๋ช ๋ น์ด ๊ตฌ์ฑ
// package.json
"scripts": {
"server": "webpack server -c webpack/webpack.dev.js",
"bundle": "webpack bundle -c webpack/webpack.dev.js",
"build": "webpack build -c webpack/webpack.prod.js",
},
webpack ์ค์ ํ์ผ์ ์ด์ฉํด์ pacakge.json ๋ช ๋ น์ด๋ฅผ ์์ฑํ ์ ์๋ค.
- server
: ๊ฐ๋ฐ ์ค์ ์ผ๋ก ์๋ฒ๋ฅผ ์คํํ๋ค.
- bundle
: ๊ฐ๋ฐ ์ค์ ์ผ๋ก ๋ฒ๋ค๋งํ๋ค.
- build
: ๋ฐฐํฌ ์ค์ ์ผ๋ก ๋น๋ํ๋ค.
babel loader ์ค์น
npm i -D babel-loader @babel/core @babel/preset-env @babel/preset-react @babel/preset-typescript
๋ชจ๋ ๋ธ๋ผ์ฐ์ ๊ฐ ์ต์ ์คํ์ ECMAScript๋ฅผ ์ง์ํ์ง ๋ชปํ๊ธฐ ๋๋ฌธ์ ํธ๋์คํ์ผ ์์ ์ ํตํด ํ์ ๋ธ๋ผ์ฐ์ ์์๋ ์คํํ ์ ์๋๋กํด์ผํ๋ค. ์นํฉ์์ Babel-Loader ์ ์ฒ๋ฆฌ๊ธฐ๋ฅผ ์ฌ์ฉํ๋ฉด ๋ค์ํ ๋ธ๋ผ์ฐ์ ์์๋ ์ฝ๋๊ฐ ์ํํ ์๋ํ๋ค.
webpack ๊ธฐ๋ณธ ์ค์
์นํฉ ์คํ ์ babel-loader๊ฐ ์๋ํ๊ธฐ ์ํด webpack ๊ธฐ๋ณธ ์ค์ ํ์ผ์ ์ถ๊ฐํด์ค๋ค.
// webpack/webpack.common.js
import { resolve } from 'node:path';
import paths from "./paths.js";
const commonConfig = {
...
module: {
rules: [
{
test: /\.(js|ts)$/,
use: {
loader: "babel-loader",
options: {
presets: [
[
"@babel/preset-env",
{
corejs: { version: 3, proposals: true },
useBuiltIns: "usage",
shippedProposals: true,
},
],
"@babel/preset-typescript",
"@babel/preset-react",
],
},
},
exclude: /(node_modules)/,
},
],
},
...
};
export default commonConfig;