1/**
2 * Webpack ์ค์ ํ์ผ
3 * https://webpack.js.org/configuration/
4 */
5
6const path = require('path');
7const HtmlWebpackPlugin = require('html-webpack-plugin');
8const MiniCssExtractPlugin = require('mini-css-extract-plugin');
9
10module.exports = (env, argv) => {
11 const isProduction = argv.mode === 'production';
12
13 return {
14 // ์ง์
์
15 entry: {
16 main: './src/index.js',
17 // ๋ค์ค ์ง์
์ ์์
18 // vendor: './src/vendor.js',
19 },
20
21 // ์ถ๋ ฅ ์ค์
22 output: {
23 path: path.resolve(__dirname, 'dist'),
24 filename: isProduction ? '[name].[contenthash].js' : '[name].js',
25 chunkFilename: isProduction ? '[name].[contenthash].chunk.js' : '[name].chunk.js',
26 clean: true, // ๋น๋ ์ dist ํด๋ ์ ๋ฆฌ
27 publicPath: '/',
28 },
29
30 // ๊ฐ๋ฐ ์๋ฒ ์ค์
31 devServer: {
32 static: {
33 directory: path.join(__dirname, 'public'),
34 },
35 port: 3000,
36 open: true,
37 hot: true, // HMR ํ์ฑํ
38 historyApiFallback: true, // SPA ๋ผ์ฐํ
์ง์
39 compress: true,
40 // ํ๋ก์ ์ค์
41 // proxy: {
42 // '/api': {
43 // target: 'http://localhost:8080',
44 // changeOrigin: true,
45 // }
46 // }
47 },
48
49 // ๋ชจ๋ ๋ก๋ ์ค์
50 module: {
51 rules: [
52 // JavaScript/JSX ์ฒ๋ฆฌ
53 {
54 test: /\.js$/,
55 exclude: /node_modules/,
56 use: {
57 loader: 'babel-loader',
58 options: {
59 presets: ['@babel/preset-env'],
60 cacheDirectory: true,
61 }
62 }
63 },
64
65 // CSS ์ฒ๋ฆฌ
66 {
67 test: /\.css$/,
68 use: [
69 isProduction ? MiniCssExtractPlugin.loader : 'style-loader',
70 {
71 loader: 'css-loader',
72 options: {
73 sourceMap: !isProduction,
74 }
75 }
76 ]
77 },
78
79 // ์ด๋ฏธ์ง ์ฒ๋ฆฌ (asset modules)
80 {
81 test: /\.(png|jpe?g|gif|svg|webp)$/i,
82 type: 'asset',
83 parser: {
84 dataUrlCondition: {
85 maxSize: 8 * 1024, // 8KB ์ดํ๋ ์ธ๋ผ์ธ
86 }
87 },
88 generator: {
89 filename: 'images/[name].[hash:8][ext]'
90 }
91 },
92
93 // ํฐํธ ์ฒ๋ฆฌ
94 {
95 test: /\.(woff|woff2|eot|ttf|otf)$/i,
96 type: 'asset/resource',
97 generator: {
98 filename: 'fonts/[name].[hash:8][ext]'
99 }
100 }
101 ]
102 },
103
104 // ํ๋ฌ๊ทธ์ธ
105 plugins: [
106 // HTML ํ
ํ๋ฆฟ ์ฒ๋ฆฌ
107 new HtmlWebpackPlugin({
108 template: './src/index.html',
109 filename: 'index.html',
110 inject: 'body',
111 minify: isProduction ? {
112 removeComments: true,
113 collapseWhitespace: true,
114 removeAttributeQuotes: true,
115 } : false,
116 }),
117
118 // CSS ์ถ์ถ (ํ๋ก๋์
)
119 ...(isProduction ? [
120 new MiniCssExtractPlugin({
121 filename: 'css/[name].[contenthash].css',
122 chunkFilename: 'css/[name].[contenthash].chunk.css',
123 })
124 ] : []),
125 ],
126
127 // ๊ฒฝ๋ก ๋ณ์นญ
128 resolve: {
129 extensions: ['.js', '.json'],
130 alias: {
131 '@': path.resolve(__dirname, 'src'),
132 '@components': path.resolve(__dirname, 'src/components'),
133 '@utils': path.resolve(__dirname, 'src/utils'),
134 '@styles': path.resolve(__dirname, 'src/styles'),
135 }
136 },
137
138 // ์ต์ ํ
139 optimization: {
140 // ์ฝ๋ ๋ถํ
141 splitChunks: {
142 chunks: 'all',
143 cacheGroups: {
144 // ๋ฒค๋ ๋ฒ๋ค ๋ถ๋ฆฌ
145 vendors: {
146 test: /[\\/]node_modules[\\/]/,
147 name: 'vendors',
148 priority: -10,
149 },
150 // ๊ณตํต ๋ชจ๋ ๋ถ๋ฆฌ
151 common: {
152 minChunks: 2,
153 priority: -20,
154 reuseExistingChunk: true,
155 }
156 }
157 },
158 // ๋ฐํ์ ์ฒญํฌ ๋ถ๋ฆฌ
159 runtimeChunk: 'single',
160 },
161
162 // ์์ค๋งต
163 devtool: isProduction ? 'source-map' : 'eval-source-map',
164
165 // ์ฑ๋ฅ ํํธ
166 performance: {
167 hints: isProduction ? 'warning' : false,
168 maxEntrypointSize: 250000, // 250KB
169 maxAssetSize: 250000,
170 },
171
172 // ์บ์ ์ค์
173 cache: {
174 type: 'filesystem',
175 buildDependencies: {
176 config: [__filename],
177 }
178 },
179
180 // ํต๊ณ ์ถ๋ ฅ ์ค์
181 stats: {
182 colors: true,
183 modules: false,
184 children: false,
185 }
186 };
187};