create-react-app์ ๊ฑฐ๋ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ชฉ๋ก์ ์ค์ด๊ณ ์
์ง์ ์นํฉ์ ์ค์นํ์ฌ ํ๋์ฉ ๋ฆฌ์กํธ์ ๊ทธ์ ํ์ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ค์ ํ ์ ์๋ค.
โ๏ธ๋ฆฌ์กํธ ๊ฐ๋ฐ์ ๊ผญ ํ์ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ
โ react, react-dom
โ babel
โ css-loader
โ๏ธ๋ฆฌ์กํธ ๊ฐ๋ฐ์ ๋์์ด ๋๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ
โ react-hot-loader
โ eslint
โ prettier
// ๊ธฐ์กด์ ๋ง๋ค์ด๋๋ Reactํด๋์์
src/components
src/public/index.html
src/styleCss
src/App.js
src/index.css
src/index.js
.gitignore
// ์ ํ์ผ๋ค์ ์ ์ธํ ๋๋จธ์ง ํ์ผ์ ์ ๋ถ ์ญ์ ํ๊ณ ์์ํ์๋ค.
// package.json ์์ฑ, "script"์ "build: webpack" ์ถ๊ฐํ๊ธฐ
npm init -y
// ์นํฉ ์ค์นํ๊ธฐ
npm i -D webpack webpack-cli
// ๋ฆฌ์กํธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ค์น
npm i react react-dom
// ๋ฐ๋ฒจ ์ค์น
npm i -D babel-loader @babel/core @babel/preset-env @babel/preset-react
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'docs'),
filename: 'app.bundle.js',
clean: true,
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
use: {
loader: 'babel-loader',
options: {
presets: [
['@babel/preset-env'],
['@babel/preset-react', { runtime: 'automatic' }],
],
},
},
exclude: /node_modules/,
},
],
},
}
// ์ค์น ํ ์ฌ์ฉ
npm i -D css-minimizer-webpack-plugin
npm i -D mini-css-extract-plugin
npm i -D css-loader
-------
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
module: {
rules: [
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, 'css-loader'],
exclude: /node_modules/,
},
],
optimization: {
minimizer: [
new CssMinimizerPlugin(), // css๋ฅผ ์ค์ฌ์ฃผ๋ ํ๋ฌ๊ทธ์ธ
],
},
plugins: [
new MiniCssExtractPlugin({
filename: 'style.css',
}),
],
// ์ค์น ํ ์ฌ์ฉ
npm i -D html-webpack-plugin
-------
const HtmlWebpackPlugin = require('html-webpack-plugin');
plugins: [
new HtmlWebpackPlugin({
template: './public/index.html',
}),
],
// ์ค์น ํ ์ฌ์ฉ
npm i -D webpack-dev-server
-------
/* webpack.config.js */
devServer: {
static: "./docs",
port: 3001,
},
-------
/* package.json */
"scripts": {
"build": "webpack",
"dev": "webpack serve --open --mode=development", // ์ถ๊ฐ
"start": "react-scripts start",
},
๋ฒ๋ค ํ์ผ์ ํฌ๊ธฐ ๋ฐ ์ข
์์ฑ์ ์๊ฐ์ ์ผ๋ก ๋ถ์ํ ์ ์๋ ๋๊ตฌ์ด๋ค.
ํฌ๊ธฐ, ์์ ๋ฑ์ผ๋ก ์๊ฐํ๋ ๊ทธ๋ํ๋ฅผ ํตํด ๋ฒ๋ค ํ์ผ์ ๊ตฌ์ฑ์ ์ฝ๊ฒ ์ดํดํ ์ ์๋ค.
// ์ค์น ํ ์ฌ์ฉ
npm i -D webpack-bundle-analyzer
-------
/* webpack.config.js */
const BundleAnalyzerPlugin = require("webpack-bundle-analyzer").BundleAnalyzerPlugin;
plugins: [
// ์ด ํ๋ฌ๊ทธ์ธ์ ๋น๋ํ ์ ์ด๋ ๊ฒ ๋ฐ๋ก ์ค์ ํด์ค ์๋ ์๋ค.
new BundleAnalyzerPlugin({
//๋ถ์ ๋ชจ๋๋ ๋ญ๋ก ํ ๊ฑด์ง
analyzerMode: "static",
//๋น๋ํ๊ณ ๋์ ๋ฐ๋ก ์ด ๊ฑด์ง
openAnalyzer: false,
//ํ์ผ ๋ง๋ค ๊ฑด์ง
generateStatsFile: true,
//ํ์ผ ์ด๋ฆ ๋ญ๋ก ํ ๊ฑด์ง
statsFilename: "bundle-report.json",
}),
],
/* package.json */
"scripts": {
"analyze": "webpack-bundle-analyzer ./docs/bundle-report.json --default-sizes gzip", // ์ถ๊ฐ
},
์ฝ๋์ ๋ฌธ์ ๊ฐ ์๋์ง ๊ฒ์ฌํด์ค๋ค.
// ์ค์น ํ ์ฌ์ฉ
npm i -D eslint eslint-plugin-react @babel/eslint-parser
-------
/* package.json */
"lint": "eslink ./src", // script์ ์ถ๊ฐ
์น ์ ๊ทผ์ฑ์ ๋ํด์ ์ง์ผ์ผ ํ๋
๋ถ๋ถ์ ์๋ ค์ฃผ๋ eslint rule์ ์ถ๊ฐ ์ค์นํด ์ค๋ค.
// ์ค์น ํ ์ฌ์ฉ
npm i -D eslint-plugin-jsx-a11y
-------
/* .eslintrc.js */
module.exports = {
parser: "@babel/eslint-parser",
env: {
browser: true,
commonjs: true,
es6: true,
node: true,
},
extends: [
"eslint:recommended",
"plugin:react/recommended",
"plugin:jsx-a11y/recommended",
],
parserOptions: {
ecmaFeatures: {
jsx: true,
},
ecmaVersion: 2018,
sourceType: "module",
},
settings: {
react: {
version: "18.2.0",
},
},
plugins: ["react"],
rules: {
"react/react-in-jsx-scope": 0,
"react/jsx-uses-react": 0,
"react/prop-types": 0,
},
};
์ฝ๋์ ํ์์ ํต์ผ์์ผ์ค๋ค.
// ์ค์น ํ ์ฌ์ฉ
npm i -D prettier
-------
/* .prettierrc.js */
module.exports = {
singleQuote: true,
jsxSingleQuote: true,
};
const path = require("path");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const BundleAnalyzerPlugin = require("webpack-bundle-analyzer").BundleAnalyzerPlugin;
module.exports = {
entry: "./src/index.js",
output: {
path: path.resolve(__dirname, "docs"),
filename: "app.bundle.js",
clean: true,
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
use: {
loader: "babel-loader",
options: {
presets: [["@babel/preset-env"], ["@babel/preset-react", { runtime: "automatic" }]],
},
},
exclude: /node_modules/,
},
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, "css-loader"],
exclude: /node_modules/,
},
],
},
optimization: {
minimizer: [
new CssMinimizerPlugin(), // css๋ฅผ ์ค์ฌ์ฃผ๋ ํ๋ฌ๊ทธ์ธ
],
},
plugins: [
new MiniCssExtractPlugin({
filename: "style.css",
}),
new HtmlWebpackPlugin({
template: "./public/index.html",
}),
new BundleAnalyzerPlugin({
analyzerMode: "static",
openAnalyzer: false,
generateStatsFile: true,
statsFilename: "bundle-report.json",
}),
],
devServer: {
static: "./docs",
port: 3001,
},
};
๐งถWebpack ์ฌ์ฉ๋ฒ ๋ณด๋ฌ๊ฐ๊ธฐ
๐ Webpack ๊ณต์๋ฌธ์
๐ CRA์ next.js, CRA์ Vite๋ฅผ ์ ๋น๊ตํ ๊ธ