Home Webpack과 React를 사용하여 Custome Element 만들기[2 - 웹팩(플러그인)]
Post
Cancel

Webpack과 React를 사용하여 Custome Element 만들기[2 - 웹팩(플러그인)]

Preview Image

Webpack

1. 사용한 플러그인(plugins)

플러그인은 번들된 파일을 처리하기 위해 사용하는 것이다. 각 플러그인의 개념은 간단하게 적고 넘어가겠다.

1.1 CleanWebpackPlugin

이 플러그인은 출력 Path(output)으로 정해놓은 것의 모든파일을 지워주고 리빌드 완료 후 사용되지않는 이미지 파일등의 webpack assets을 모두 지워준다.

1.2 HtmlWebpackPlugin

html파일을 후처리하는데 사용된다. 이 플러그인으로 빌드 시 html파일로 아웃풋에 생성이 된다.

1
2
3
{
  plugins: [new HtmlWebpackPlugin({ template: './src/index.html' }) ],
}

1.3 ReactRefreshWebpackPlugin

새로고침에도 리액트 컴포넌트가 hot-reload 가 되기 위해 사용한 plugin이다.

나는 설정 cross-env 를 이용하여 스크립트 작동 시 isDevelopment 변수가 true가 될 때 해당 플러그인을 plugins라는 배열에 push해주는 방식을 택했다.

아래 코드는 위에 언급된(1.1~1.3)모든 플러그인들이 적용되는 방식을 보여주는 예제이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const ReactRefreshWebpackPlugin = require("@pmmmwh/react-refresh-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const isDevelopment = process.env.SERVE;

const plugins = [new CleanWebpackPlugin()];

if (isDevelopment) {
  // react -element를 별도로 만드는 것이므로 개발 모드에서만 htmlplugin, refresh plugin 사용
  plugins.push(
    new ReactRefreshWebpackPlugin(),
    new HtmlWebpackPlugin({ template: "./src/index.html" })
  );
}

module.exports = {
  plugins,
};

1.4 TsconfigPathsPlugin

ts에서 간단한 경로 설정을 위해 사용하는 플러그인이다. 아래코드는 tsconfig.json을 참조하는 코드이다.

1
2
3
4
5
6
7
8
9
10
11
12
const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');
const tsConfigPath = path.resolve(__dirname, './tsconfig.json');

  resolve: {
    extensions: ['.ts', '.tsx', '.js', '.jsx'],
    plugins: [
      //ts를 위한 추가 플러그인 설정
      new TsconfigPathsPlugin({
        configFile: tsConfigPath,
      }),
    ],
  },

tsconfig.json은 1편에서 본것과 똑같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
  "compilerOptions": {
    "outDir": "./dist/",
    "sourceMap": true,
    "noImplicitAny": true,
    "module": "commonjs",
    "target": "es6",
    "jsx": "react",
    "esModuleInterop": true,
    "baseUrl": "./src"
  },
  "exclude": ["node_modules", "**/*,spce.ts"],
  "include": ["./src", "index.d.ts"]
}

2.전체 세팅(로더+플러그인)

아래는 지금까지 참고한 블로그 글들과 유튜브 동영상등을 통해 필요한것을 추려 사용한 전체세팅이다 추후에 부족한부분을 계속 채워넣으며 포스팅을 업그레이드 해야겠다..

별도의 주석으로 설명을 달아놓았다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
const path = require("path");
// const MiniCssExtractPlugin = require('mini-css-extract-plugin'); -> css를 별도로 추출할 예정이 없으므로 임의 삭제
const ReactRefreshWebpackPlugin = require("@pmmmwh/react-refresh-webpack-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const TsconfigPathsPlugin = require("tsconfig-paths-webpack-plugin");
const tsConfigPath = path.resolve(__dirname, "./tsconfig.json");
const ReactRefreshTypeScript = require("react-refresh-typescript");
const plugins = [new CleanWebpackPlugin()];
const isDevelopment = process.env.SERVE;
let mode = "development";
let target = "web";

if (process.env.NODE_ENV === "production") {
  mode = "production";
  target = "browserslist";
}

if (isDevelopment) {
  // react -element를 별도로 만드는 것이므로 개발 모드에서만 htmlplugin, refresh plugin 사용
  plugins.push(
    new ReactRefreshWebpackPlugin(),
    new HtmlWebpackPlugin({ template: "./src/index.html" })
  );
}

module.exports = {
  mode: mode, // 웹팩에서의 default모드는 해당 설정과 같이 해주지 않으면 default로 production이다.
  entry: "./src/index.tsx",
  output: {
    path: path.resolve(__dirname, "dist"), // 깔끔한 웹팩 플러그인(clean-webpack-plugin)을 위해서는 output이  필요하다
    filename: "test.js",
    assetModuleFilename: "images/[hash][ext][query]",
    // 모든 이미지들을 dist/images 하위 폴더로 넣기 위함  + 이미지가 변경될 시 브라우저 캐싱으로 인해 못알아차리는 경우가 있으므로 hash를 넣어주자
  },

  module: {
    rules: [
      {
        test: /\.(s[ac]|c)ss$/i,
        use: [
          //웹팩 로더는 한 파일에 대해 여러가지가 실행되는데 배열의 뒤에서부터 앞으로 작동한다.
          "style-loader", // js로 생성된 css를 styles 노드로 생성
          "css-loader", // css 를 js로
          // 'postcss-loader' //css파일 따로 extract하지 않을 것이므로 제외,
          "sass-loader", // sass -> css로 컴파일
        ],
      },
      {
        test: /\.(png|jpe?g|gif|svg)$/i,
        type: "asset", //웹팩 5부터 url-loader, file-loader를 대신할 수 있게 되며 asset으로 설정 시 자동으로 base64로 컴파일(인라인번들 base 64, <= 8kb)하든지 images폴더로 넣어주든지 하게 해준다
        /**
         * 큰 사이즈의 파일을 인라이닝 하고 싶으면 limit을 정해줄 수 도 있다 하단 parser 참고
         */
        parser: {
          dataUrlCondition: {
            maxSize: 30 * 1024,
          },
        },
      },
      {
        test: /\.[jt]sx?$/,
        exclude: /node_modules/,
        use: [
          {
            loader: "ts-loader",
            options: {
              getCustomTransformers: () => ({
                before: [isDevelopment && ReactRefreshTypeScript()].filter(
                  Boolean
                ),
              }),
              transpileOnly: isDevelopment,
            },
          },
        ],
      },
      // 모든 '.js' 출력 파일은 'source-map-loader'에서 다시 처리한 소스 맵에서 존재.
      //dev setting일 시 변경되는 사항을 map을 통해서 빠르게 찾게 되는 것 + 이전 빌드 결과 존재시 더 빠른 build 가능
      {
        test: [/\.js?$/, /\.ts?$/, /\.jsx?$/, /\.tsx?$/],
        enforce: "pre",
        exclude: /node_modules/,
        use: ["source-map-loader"],
      },
      {
        test: /\.jsx?$/,
        exclude: /node_modules/,
        use: {
          //추가 설정 없으면 babelrc.를 참조하게 되어있다
          //여기서는 개발모드일때와 빌드모드일때를 구분하기 위해 로직이 필요(react-refresh/babel)하므로 bable.config.js를 사용한다
          loader: "babel-loader",
          options: {
            cacheDirectory: true, //바벨이 recompile하는데 쓰이는 큰용량의 소스를 줄여줌
            presets: [
              "@babel/preset-env",
              // in files only using JSX, -> TSX는 해당이 안되는것.. 같음
              ["@babel/preset-react", { runtime: "automatic" }], // Runtime automatic with React 17+ allows not importing React
            ],
            //하단 플러그인은 react-refresh-typescript에서 변경되는 ts를 바벨로더(js를 변환하는 용도이므로)가 못읽는 현상이 발생하기 때문
            plugins: [isDevelopment && "react-refresh/babel"],
          },
        },
      },
    ],
  },
  plugins: plugins,
  target: target,
  // Webpack의 출력물에서 디버깅을 하기위해 소스 맵 사용
  devtool: "source-map",
  resolve: {
    extensions: [".ts", ".tsx", ".js", ".jsx"],
    plugins: [
      //ts를 위한 추가 플러그인 설정
      new TsconfigPathsPlugin({
        configFile: tsConfigPath,
      }),
    ],
  },

  //dev server 세팅
  devServer: {
    static: "./dist", //웹팩5부터 static으로 명칭 변경
    port: 3000,
    hot: true,
  },
};

webpack.config.js

참고

This post is licensed under CC BY 4.0 by the author.

Webpack과 React를 사용하여 Custome Element 만들기[1 - 웹팩(로더)]

Webpack과 React를 사용하여 Custome Element 만들기[3 - 커스텀엘리먼트 렌더]