
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
참고
- Docs: Difference between esnext, es6, es2015 module targets
- npm > style-loader
- npm > url-loader
- 웹팩의 file-loader와 url-loader
- npm > ts-loader
- Talk to your React components with custom events
- github issue-dispatchEvent customEvent of web components not working
- webpack & ts image import
- https://github.com/pmmmwh/react-refresh-webpack-plugin/issues/176
- react-refresh-webpack-plugin