[React.js] Webpack Setting


1. ํด๋” ๊ตฌ์กฐ


//React18
โ”œโ”€ dist
โ”œโ”€ package-lock.json
โ”œโ”€ package.json
โ”œโ”€ public
โ”‚  โ””โ”€ index.html
โ”œโ”€ src
โ”‚  โ”œโ”€ App.js
โ”‚  โ”œโ”€ index.js
โ”‚  โ””โ”€ index.css
โ””โ”€ webpack.config.js

CRA ๋ช…๋ น์–ด๋กœ ์ƒ์„ฑํ•œ ๊ฒƒ ์ฒ˜๋Ÿผ ์œ„์™€ ๊ฐ™์ด ๊ตฌ์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค.ย  ๊ฐ ํด๋”๋ณ„ ์“ฐ์ž„์ƒˆ๋Š” ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  • public: HTML ํ…œํ”Œ๋ฆฟ๊ณผ ์ด๋ฏธ์ง€ ํŒŒ์ผ ๋“ฑ ์ ˆ๋Œ€๊ฒฝ๋กœ๋กœ ์“ฐ์ด๋Š” ํŒŒ์ผ์ด ์œ„์น˜ํ•˜๋Š” ์žฅ์†Œ
  • src: ๊ธฐ๋ณธ์ ์œผ๋กœ ์ž‘์„ฑํ•˜๋Š” ์†Œ์Šค์ฝ”๋“œ๊ฐ€ ์œ„์น˜ํ•˜๋Š” ์žฅ์†Œ
  • dist: ์›นํŒฉ์ด ๋ฒˆ๋“ค๋ง๋œ ๊ฒฐ๊ณผ๋ฅผ ์ €์žฅํ•˜๋Š” ์žฅ์†Œ

์ด๋ ‡๊ฒŒ ์ƒ์„ฑํ•œ ๊ตฌ์กฐ๋Œ€๋กœ npm ํŒจํ‚ค์ง€ ๋งค๋‹ˆ์ €๋ฅผ ํ†ตํ•ด ํ•„์š”ํ•œ ํŒจํ‚ค์ง€๋“ค์„ ์„ค์น˜ํ•˜๊ณ ย ์›นํŒฉ ์„ค์ •์„ ์ง„ํ–‰ํ•˜๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ๋จผ์ € ๋ชจ๋“ˆ ๋ฒˆ๋“ค๋Ÿฌ๋‚˜ ์›นํŒฉ์— ๋Œ€ํ•œ ์ง€์‹์ด ์—†๋Š” ๋ถ„๋“ค์€ ์•„๋ž˜ ํฌ์ŠคํŒ…์„ ๋จผ์ € ์ฐธ๊ณ ํ•ด์ฃผ์„ธ์š”.

JS ๋ชจ๋“ˆ ๋ฒˆ๋“ค๋Ÿฌ์™€ ์›นํŒฉ(Webpack) ์š”์ฆ˜ ํšŒ์‚ฌ์—์„œ๋Š” Vue๋ฅผ ์‚ฌ์šฉํ•ด ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฒˆ 1์ฐจ ํ”„๋กœ์ ํŠธ๊ฐ€ ๊ณง ๋๋‚˜๊ฐ€๋Š”๋ฐ์š”. 2์ฐจ๋„ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ Vue๋ฅผ ์‚ฌ์šฉํ•ด ๊ฐœ๋ฐœํ•œ๋‹ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค. ์ €๋Š” ๋ฆฌ์•กํŠธ๊ฐ€ ์ข‹์€๋ฐ ๋ง์ด์ฃ ? ์•„๋‹ˆ๋ฉด juni-official.tistory.com

2. ํŒจํ‚ค์ง€ ์ƒ์„ฑ


npm init

ํ„ฐ๋ฏธ๋„์„ ํ†ตํ•ด npm ๋ช…๋ น์–ด๋กœ ํŒจํ‚ค์ง€๋ฅผ ์ƒ์„ฑํ•ด์ค๋‹ˆ๋‹ค. ์ž‘์„ฑ์ž ๋ฐ ํ”„๋กœ์ ํŠธ ์ด๋ฆ„์€ ๊ฐœ๋ณ„ ์„ค์ •ํ•ด์ค๋‹ˆ๋‹ค.

3. ํŒจํ‚ค์ง€ ์„ค์น˜


๋ฆฌ์•กํŠธ

yarn add react react-dom

์›นํŒฉ

yarn add webpack webpack-cli webpack-dev-server

์›นํŒฉ๊ณผ ํ”Œ๋Ÿฌ๊ทธ์ธ์€ ๊ฐœ๋ฐœํ™˜๊ฒฝ์—์„œ๋งŒ ํ•„์š”ํ•˜๋ฏ€๋กœ ์„ค์น˜ ์‹œ โ€˜-Dโ€™ ๋˜๋Š” โ€˜โ€“save-devโ€™ ์˜ต์…˜์„ ์ถ”๊ฐ€ํ•ด์ค๋‹ˆ๋‹ค.

์›นํŒฉ ํ”Œ๋Ÿฌ๊ทธ์ธ

yarn add babel-loader html-webpack-plugin clean-webpack-plugin css-loader style-loader cross-env dotenv dotenv-webpack
  • babel-loader: ์›นํŒฉ์—์„œ ๋ฐ”๋ฒจ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์ฒ˜๋ฆฌ
  • CleanWebpackPlugin: ์ด์ „์— ๋ฒˆ๋“ค๋œ ํŒŒ์ผ ์ž๋™ ์‚ญ์ œ
  • HtmlWebpackPlugin: HTML ํ…œํ”Œ๋ฆฟ ์„ค์ •
  • css-loader/style-loader: CSS ๊ด€๋ จ ํŒŒ์ผ ์ฒ˜๋ฆฌ

๋ฐ”๋ฒจ

yarn add @babel/core @babel/preset-env @babel/preset-react

ํŠธ๋žœ์ŠคํŒŒ์ผ๋Ÿฌ(Transpiler)์™€ ๋ฐ”๋ฒจ(Babel) ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋ฅผ ๊ณต๋ถ€ํ•˜๋‹ค ๋ณด๋ฉด ์‰ฝ๊ฒŒ ๋“ค์„ ์ˆ˜ ์žˆ๋Š” ํด๋ฆฌํ•„๊ณผ ๋ฐ”๋ฒจ์— ๋Œ€ํ•ด์„œ ๋‹ค์‹œ ๊ณต๋ถ€ํ•ด๋ณด๋ฉฐ ๊ธฐ๋กํ•ด๋ณธ๋‹ค. ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋Š” ํ˜„์žฌ๊นŒ์ง€ ๊ณ„์† ์—…๋ฐ์ดํŠธ๋˜๊ณ  ์žˆ๋Š” ์–ธ์–ด์ด๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๋ธŒ๋ผ์šฐ์ €๋งˆ๋‹ค ์ž juni-official.tistory.com

3. ์›นํŒฉ ์„ค์ •


const path = require("path");
const webpack = require("webpack");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const ESLintPlugin = require("eslint-webpack-plugin"); // eslint ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ
const ReactRefreshWebpackPlugin = require("@pmmmwh/react-refresh-webpack-plugin");

// const isDevelopment = process.env.NODE_ENV !== "production";
// const envPath = `./.env.${isDevelopment ? "development" : "production"}`;

// dotenv.config({
//   path: envPath,
// });

const config = {
  name: "React18-webpack-babel-setting", // ์„ค์ • ์ด๋ฆ„
  mode: "development", // production, development // ์„ค์ • ๋ชจ๋“œ
  devtool: "eval",
  entry: {
    app: "./src/index.js",
  },
  resolve: {
    extensions: [".js", ".jsx"],
  },
  module: {
    rules: [
      {
        // ๋ฆฌ์•กํŠธ ๋ฐ”๋ฒจ ์„ค์ •
        test: /\.js/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader",
          options: {
            presets: ["@babel/preset-env", "@babel/preset-react"],
          },
        },
      },
      {
        test: /\.css$/i,
        use: ["style-loader", "css-loader"],
      },
    ],
  },
  plugins: [
    new CleanWebpackPlugin(),
    new HtmlWebpackPlugin({
      template: "./public/index.html", // ํ…œํ”Œ๋ฆฟ ์„ค์ •
      minify: true, // ์••์ถ• ์„ค์ •
    }),
    new webpack.ProvidePlugin({
      // ๋ฆฌ์•กํŠธ ์ž๋™ ๋กœ๋“œ
      React: "react",
    }),
    new ESLintPlugin(),
    new ReactRefreshWebpackPlugin(),
  ],
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "app.js",
    publicPath: "/",
  },
  devServer: {
    // ๊ฐœ๋ฐœ ์„œ๋ฒ„ ์„ค์ •
    port: 3000, // ํฌํŠธ ์„ค์ •
    hot: true, // ํ•ซ ๋ชจ๋“ˆ ๊ต์ฒด(HMR) ํ™œ์„ฑํ™”
    compress: true, // ์••์ถ• ์œ ๋ฌด
    open: true, // ๊ธฐ๋ณธ ๋ธŒ๋ผ์šฐ์ €์—์„œ ์‹คํ–‰
    historyApiFallback: true, // connect-history-api-fallback error ๋ฐฉ์ง€
  },
};

module.exports = config;

๋ณ€๊ฒฝ ํžˆ์Šคํ† ๋ฆฌ

+์ถ”๊ฐ€)

..
plugins: [
    ..
    new webpack.ProvidePlugin({
        "React": "react",
    })
    ..
]
..

์ปดํฌ๋„ŒํŠธ๋งˆ๋‹ค import React from โ€˜reactโ€™ ์„ ์–ธ์„ ๊ผญ ํ•ด์ค˜์•ผ ํ•˜๋Š”๋ฐ์š”. ProviderPlugin์„ ์‚ฌ์šฉํ•˜๋ฉดย ๋ฆฌ์•กํŠธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ํ•„์š”ํ•œ ๊ณณ์—๋Š” ์›นํŒฉ์ด ์•Œ์•„์„œ ๋„ฃ์–ด์ค๋‹ˆ๋‹ค. ๋ฌผ๋ก  CRA๋ฅผ ํ†ตํ•ด ํ”„๋กœ์ ํŠธ๋ฅผ ๋งŒ๋“ค์–ด๋„ ๋™์ผํ•˜์ง€๋งŒ์š” :)

moment ๊ฐ™์ด ์ž์ฃผ ์‚ฌ์šฉํ•˜๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์žˆ์„ ๊ฒฝ์šฐ ์ถ”๊ฐ€ํ•ด ๋†“์œผ๋ฉด ์ƒ๋‹จ์— ์„ ์–ธ ์—†์ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด ํŽธ๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

devServer HMR & ํ•ซ ๋ฆฌ๋กœ๋”ฉ


yarn add @pmmmwh/react-refresh-webpack-plugin

๊ธฐ์กด ๋ฆฌ๋กœ๋”ฉ์€ ๋ณ€๊ฒฝ์‚ฌํ•ญ์ด ๋ฐœ์ƒํ•˜๋ฉด ์ „์ฒด๊ฐ€ ์ƒˆ๋กœ๊ณ ์นจ ๋˜๋Š”๋ฐ, ๋ณ€๊ฒฝ๋œ ๋ถ€๋ถ„๋งŒ ๋น ๋ฅด๊ฒŒ ๋ฐ”๊ฟ”์ฃผ๋Š”ย react-refresh-webpack-plugin์„ ์ ์šฉํ•ฉ๋‹ˆ๋‹ค. ํ•ด๋‹น ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์„ค์น˜ํ•˜๊ณ  ํ”Œ๋Ÿฌ๊ทธ์ธ์— ์ถ”๊ฐ€ํ•ด์ค๋‹ˆ๋‹ค.

const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin')

..
    plugins: [
    	...
        new webpack.HotModuleReplacementPlugin(),
        new ReactRefreshWebpackPlugin()
        ...
    ],
    devServer: {// ๊ฐœ๋ฐœ ์„œ๋ฒ„ ์„ค์ •port: 3000,// ํฌํŠธ ์„ค์ •hot: true,// ํ•ซ ๋ชจ๋“ˆ ๊ต์ฒด(HMR) ํ™œ์„ฑํ™”compress: true,// ์••์ถ• ์œ ๋ฌดopen: true,// ๊ธฐ๋ณธ ๋ธŒ๋ผ์šฐ์ €์—์„œ ์‹คํ–‰historyApiFallback: true,// connect-history-api-fallback error ๋ฐฉ์ง€
    }

..

devServer์™€ ๊ด€๋ จ๋œ ์„ค์ •์ด ์—„์ฒญ ๋งŽ์•„ ์„ธ๋ถ€ ์„ค์ • ๋‚ด์šฉ์€ ์•„๋ž˜์—์„œ ์ฐธ๊ณ ํ•ด์ฃผ์„ธ์š”.

DevServer | ์›นํŒฉ ์›นํŒฉ์€ ๋ชจ๋“ˆ ๋ฒˆ๋“ค๋Ÿฌ์ž…๋‹ˆ๋‹ค. ์ฃผ์š” ๋ชฉ์ ์€ ๋ธŒ๋ผ์šฐ์ €์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก JavaScript ํŒŒ์ผ์„ ๋ฒˆ๋“ค๋กœ ๋ฌถ๋Š” ๊ฒƒ์ด์ง€๋งŒ, ๋ฆฌ์†Œ์Šค๋‚˜ ์• ์…‹์„ ๋ณ€ํ™˜ํ•˜๊ณ  ๋ฒˆ๋“ค๋ง ๋˜๋Š” ํŒจํ‚ค์ง•ํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. webpack.kr

4. package.json


ํ•„์š”ํ•œ ํŒจํ‚ค์ง€ ๋ฐ ์›นํŒฉ ์„ค์ •์ด ๋๋‚ฌ์Šต๋‹ˆ๋‹ค. package.json ํŒŒ์ผ์— ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์ž‘์„ฑํ•ด์ฃผ๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

// package.json"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev": "cross-env webpack serve --env development",
    "build": "cross-env NODE_ENV=production webpack"
  }
  • npm run dev: ์›นํŒฉ ๋ฐ๋ธŒ ์„œ๋ฒ„ ์‹คํ–‰ ๋ช…๋ น์–ด
  • npm run build: ์›นํŒฉ ๋ฒˆ๋“ค๋ง ๋ช…๋ น์–ด

์ด์ œ ๋ณธ๊ฒฉ์ ์œผ๋กœ ๋ฆฌ์•กํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋Š” ํ™˜๊ฒฝ์„ ๋งŒ๋“ค์–ด๋†จ์Šต๋‹ˆ๋‹ค. index.html -> index.js -> app.js ์ˆœ์œผ๋กœ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

5. ์ฝ”๋“œ์ž‘์„ฑ


public/index.html

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Hello, React18!</title>
</head>
<body>
    <div id="root"></div>
</body>
</html>

๋น„์ฃผ์–ผ ์ŠคํŠœ๋””์˜ค์—์„œ ! -> tab ์—๋ฐ‹ ๊ธฐ๋Šฅ์œผ๋กœ ๋งŒ๋“ค์–ด์ค๋‹ˆ๋‹ค. <body /> ํƒœ๊ทธ์—๋Š” <div /> ํƒœ๊ทธ ํ•˜๋‚˜๋ฅผ ๋งŒ๋“ค์–ด ์ค๋‹ˆ๋‹ค.

src/index.js

import ReactDom from "react-dom/client";
import App from "./App";
import "./index.css";

const root = document.getElementById("macjjuni");

ReactDom.createRoot(root).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

// ๋ฆฌ์•กํŠธ 18 ์ด์ „ ๋ฒ„์ „// ReactDom.render(//    <App/>,//    document.getElementById("root")// )

๋ฆฌ์•กํŠธ 18 ์ด์ „ ๋ฒ„์ „์—์„œ๋Š” ReactDom.render ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ–ˆ์œผ๋‚˜ 18 ์ดํ›„ ReactDom.createRoot().render ํ•จ์ˆ˜๋กœ ๋ณ€๊ฒฝ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

์—ฌ๊ธฐ์„œ ๋ฆฌ์•กํŠธ ์ž์ฒด๋ฅผ ์„ ์–ธํ•˜์ง€ ์•Š์•˜๋Š”๋ฐ, ๊ทธ ์ด์œ ๋Š” ์›นํŒฉ์—์„œ ์„ค์ •ํ•ด์คฌ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

src/app.js

export default function App() {
  return (
    <>
      <div className="wrap">
        <h1 className="title">Hello, React18!</h1>
      </div>
    </>
  );
}

src/index.css

@charset "utf8";

* {
    box-sizing: border-box;
    margin: 0 auto;
}

.wrap {
    display: flex;
    flex-direction: column;
    justify-content: space-evenly;
    align-items: center;
    width: 100vw;
    height: 100vh;
}

.title {
    font-size: 50px;
    font-weight: bold;
    color: skyblue;
}

์ด์ œ ์›นํŒฉ ๊ฐœ๋ฐœ ์„œ๋ฒ„๋ฅผ ์‹คํ–‰ํ•ด์„œ ๊ฒฐ๊ณผ๋ฅผ ํ™•์ธํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

๊ฐœ๋ฐœ์„œ๋ฒ„ ์‹คํ–‰


yarn dev