[React] ๐ŸงคCRA ์—†์ด Webpack์œผ๋กœ React ํ™˜๊ฒฝ ๊ตฌ์„ฑํ•˜๊ธฐ

TATAยท2023๋…„ 3์›” 21์ผ
0

React

๋ชฉ๋ก ๋ณด๊ธฐ
18/32

โ–ท ๋ฆฌ์•กํŠธ์—์„œ์˜ Webpack

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
// ์œ„ ํŒŒ์ผ๋“ค์„ ์ œ์™ธํ•œ ๋‚˜๋จธ์ง€ ํŒŒ์ผ์€ ์ „๋ถ€ ์‚ญ์ œํ•˜๊ณ  ์‹œ์ž‘ํ•˜์˜€๋‹ค.


๐Ÿงค start!

// 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

๐Ÿงค webpack.config.js

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/,
      },
    ],
  },
}

๐Ÿงค CSS๋ฅผ ๋ฒˆ๋“ค์— ํฌํ•จํ•˜๊ธฐ

// ์„ค์น˜ ํ›„ ์‚ฌ์šฉ
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',
  }),
],

๐Ÿงค HTML์„ ๋ฒˆ๋“ค์— ํฌํ•จํ•˜๊ธฐ

// ์„ค์น˜ ํ›„ ์‚ฌ์šฉ
npm i -D html-webpack-plugin

-------
const HtmlWebpackPlugin = require('html-webpack-plugin');

plugins: [
  new HtmlWebpackPlugin({
    template: './public/index.html',
  }),
],

๐Ÿงค webpack-dev-server

// ์„ค์น˜ ํ›„ ์‚ฌ์šฉ
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",
 },

๐Ÿงค webpack-bundle-analyzer

๋ฒˆ๋“ค ํŒŒ์ผ์˜ ํฌ๊ธฐ ๋ฐ ์ข…์†์„ฑ์„ ์‹œ๊ฐ์ ์œผ๋กœ ๋ถ„์„ํ•  ์ˆ˜ ์žˆ๋Š” ๋„๊ตฌ์ด๋‹ค.
ํฌ๊ธฐ, ์ƒ‰์ƒ ๋“ฑ์œผ๋กœ ์‹œ๊ฐํ™”๋œ ๊ทธ๋ž˜ํ”„๋ฅผ ํ†ตํ•ด ๋ฒˆ๋“ค ํŒŒ์ผ์˜ ๊ตฌ์„ฑ์„ ์‰ฝ๊ฒŒ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋‹ค.

// ์„ค์น˜ ํ›„ ์‚ฌ์šฉ
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", // ์ถ”๊ฐ€
 },

๐Ÿงค eslint

์ฝ”๋“œ์˜ ๋ฌธ์ œ๊ฐ€ ์—†๋Š”์ง€ ๊ฒ€์‚ฌํ•ด์ค€๋‹ค.

// ์„ค์น˜ ํ›„ ์‚ฌ์šฉ
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,
  },
};

๐Ÿงค prettier

์ฝ”๋“œ์˜ ํ˜•์‹์„ ํ†ต์ผ์‹œ์ผœ์ค€๋‹ค.

// ์„ค์น˜ ํ›„ ์‚ฌ์šฉ
npm i -D prettier

-------
/* .prettierrc.js */
module.exports = {
  singleQuote: true,
  jsxSingleQuote: true,
};

๐Ÿงค ์ตœ์ข… ์ฝ”๋“œ - webpack.config.js

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๋ฅผ ์ž˜ ๋น„๊ตํ•œ ๊ธ€

profile
๐Ÿพ

0๊ฐœ์˜ ๋Œ“๊ธ€