Webpack 구성 파일
Last updated
Was this helpful?
Last updated
Was this helpful?
Webpack 구성 파일을 프로젝트 루트(root) 위치에 생성하고 진입, 진출 파일 경로, 등을 설정합니다.
// Node.js 모듈 path 불러오기
const path = require('path')
// CommonJS 방식의 모듈 내보내기
module.exports = {
// 엔트리 파일 설정
entry: './src/index.js',
// 아웃풋 파일 출력 설정
output: {
// 파일 이름
filename: 'main.js',
// 경로
path: path.resolve(__dirname, './dist'),
},
// 번들링 모드 설정
mode: 'development',
}
webpack
명령을 터미널에 입력해 모듈 번들링을 수행합니다.
npx webpack
만약 명령 실행 후, 다음과 같은 오류 메시지가 출력된다면? 출력 경로 설정이 잘못된 것입니다.
[webpack-cli]
# 잘못된 구성 객체입니다.
Invalid configuration object.
# Webpack API 스키마(Schema)와 일치하지 않는 구성 객체를 사용해 초기화 되었습니다.
Webpack has been initialized using a configuration object that does not match the API schema.
# 구성.출력.경로 설정인 './dist' 공급 값은 절대 경로가 아닙니다!
- configuration.output.path: The provided value "./dist" is not an absolute path!
# 출력 디렉토리 설정은 반드시 **절대 경로**로 처리해야 합니다.
-> The output directory as **absolute path** (required).
이 문제는 출력 경로 설정을 다음과 같이 절대 값으로 설정하면 손쉽게 해결할 수 있습니다.
path: path.resolve(__dirname, './dist'),
프로젝트 구성 파일의 scripts
항목에 bundle
명령을 추가 등록한 다음 값으로 webpack
명령을 추가합니다.
"scripts": {
"bundle": "webpack"
}
그러면 터미널에서 NPM 명령 bundle
로 Webpack 모듈 번들링을 수행할 수 있습니다.
npm run bundle
.
├── config/
│ ├── webpack.dev.js # 개발용 Webpack 구성 파일
│ └── webpack.prod.js # 배포용 Webpack 구성 파일
├── src/
├── package-lock.json
└── package.json
const path = require('path')
const webpack = require('webpack')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
/* -------------------------------------------------------------------------- */
module.exports = {
// 입력 파일
entry: {
main: './src/index.js',
},
// 출력 파일
output: {
path: path.resolve(__dirname, '../dist'),
// 파일 이름 브라우저 캐싱 처리
filename: '[name].js',
},
// 감시 설정 (package.json 스크립트로 제어)
// watch: true,
// 모드 설정
mode: 'development',
// 소스맵 설정
devtool: 'inline-source-map',
// 모듈 설정
module: {
// 로더 규칙
rules: [
// 이미지 파일 로더
{
test: /\.(png|jpe?g|gif|svg|webp)$/i,
use: {
loader: 'file-loader',
options: {
name: '[name].[ext]',
},
},
},
// 스타일 파일 로더
{
test: /\.(sa|sc|c)ss$/i,
exclude: /node_modules/,
use: ['style-loader', 'css-loader', 'sass-loader'],
},
// 스크립트 파일 로더
{
test: /\.js$/i,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env', '@babel/preset-react'],
plugins: ['@babel/plugin-proposal-class-properties'],
sourceMap: true,
},
},
},
],
},
// 플러그인 설정
plugins: [
// 환경 변수 등록
new webpack.EnvironmentPlugin({
NODE_ENV: 'development',
}),
// 빌드 된 결과물 정리
new CleanWebpackPlugin(),
// 빌드 결과 HTML 문서 자동 주입
new HtmlWebpackPlugin({
// 템플릿 설정
template: './src/template/template.ejs',
// 자동 주입 해제
inject: false,
// 압축 설정
minify: true,
// 문서 메타
meta: {
'theme-color': '#4285f4',
'description': 'Webpack 러닝 가이드 실습을 진행합니다.',
},
// 사용자 정의 옵션
templateParameters: {
title: 'Webpack 러닝 가이드',
lang: 'ko-KR',
},
}),
],
}
const os = require('os')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
const CSSMQPackerPlugin = require('css-mqpacker-webpack-plugin')
const ImageMinimizerPlugin = require('image-minimizer-webpack-plugin')
// 개발 모드 구성 가져오기
const devConfig = require('./webpack.dev')
// 배포 모드 구성 설정
const prodConfig = {
entry: {
...devConfig.entry,
'polyfills': './src/polyfills/polyfills.js',
'detect.polyfills': './src/polyfills/detect.polyfills.js',
},
output: {
...devConfig.output,
filename: '[name].[contenthash].js',
publicPath: '',
},
module: {
rules: [
...devConfig.module.rules.filter((rule) => {
if (Array.isArray(rule.use)) {
return !rule.use.includes('css-loader')
}
return rule
}),
// MiniCssExtractPlugin.loader 설정 (대체)
{
test: /\.(sa|sc|c)ss$/i,
exclude: /node_modules/,
use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader'],
},
],
},
plugins: [
...devConfig.plugins,
// CSS 추출
new MiniCssExtractPlugin({
linkType: false,
filename: '[name].[contenthash].css',
chunkFilename: '[id].[contenthash].css',
}),
// PNG — 이미지 포멧 최적화
new ImageMinimizerPlugin({
minimizerOptions: {
plugins: ['pngquant'],
},
}),
// WEBP — 이미지 포멧 최적화
new ImageMinimizerPlugin({
deleteOriginalAssets: true,
filename: '[name].webp',
minimizerOptions: {
plugins: ['imagemin-webp'],
},
}),
// JPE?G — 이미지 포멧 최적화
new ImageMinimizerPlugin({
// 약 8kb 이상 파일만 최적화 적용
filter: (source) => (source.byteLength >= 8192 ? true : false),
minimizerOptions: {
plugins: [['jpegtran', { progressive: true }]],
},
}),
new ImageMinimizerPlugin({
// 약 8kb 미만 파일만 최적화 적용
filter: (source) => (source.byteLength < 8192 ? true : false),
minimizerOptions: {
plugins: [['jpegtran', { progressive: false }]],
},
}),
],
}
/* -------------------------------------------------------------------------- */
module.exports = {
entry: prodConfig.entry,
output: prodConfig.output,
mode: 'production',
devtool: 'source-map',
// 모듈 설정
module: prodConfig.module,
// 플러그인 설정
plugins: prodConfig.plugins,
// 최적화 설정
optimization: {
// 압축
minimize: true,
// 미니마이저
minimizer: [
// 플러그인 인스턴스 생성
new CssMinimizerPlugin({
// CPU 멀티 프로세서 병렬화 옵션 (기본 값: true)
// os.cpus() → CPU 코어 정보를 포함하는 배열 객체 반환
parallel: os.cpus().length - 1,
}),
// 미디어쿼리 그룹 병합
new CSSMQPackerPlugin({
sort: true,
}),
],
},
}
const os = require('os')
const path = require('path')
const webpack = require('webpack')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
const CSSMQPackerPlugin = require('css-mqpacker-webpack-plugin')
const ImageMinimizerPlugin = require('image-minimizer-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
/* -------------------------------------------------------------------------- */
// 개발 모드 감지
const isDevMode = process.env.NODE_ENV.includes('dev')
/* -------------------------------------------------------------------------- */
// 개발,배포 모든 모드에서 사용되는 Webpack 플러그인 목록(배열)
const plugins = [
// 환경 변수 등록
new webpack.EnvironmentPlugin({
// process.env.NODE_ENV가 정의되지 않은 경우, 'development' 사용
NODE_ENV: 'development',
}),
// 빌드 된 결과물 정리
new CleanWebpackPlugin(),
// 빌드 결과 HTML 문서 자동 주입
new HtmlWebpackPlugin({
// 템플릿 설정
template: './src/template/template.ejs',
// 자동 주입 해제
inject: false,
// 압축 설정
minify: true,
// 문서 타이틀
// 문서 메타
meta: {
'theme-color': '#4285f4',
'description': 'Webpack 러닝 가이드 실습을 진행합니다.',
},
// 사용자 정의 옵션
templateParameters: {
title: 'Webpack 러닝 가이드',
lang: 'ko-KR',
},
}),
]
// 개발,배포 모든 모드에서 사용되는 엔트리 파일 설정
let entry = {
main: './src/index.js',
}
// 배포 모드 설정
if (!isDevMode) {
// 배포용 엔트리 파일(들) 추가
entry = {
...entry,
'polyfills': './src/polyfills/polyfills.js',
'detect.polyfills': './src/polyfills/detect.polyfills.js',
}
// 배포용 플러그인 추가
plugins.push(
// CSS 추출
new MiniCssExtractPlugin({
linkType: false,
filename: '[name].[contenthash].css', // 'style.css' 설정도 가능
chunkFilename: '[id].[contenthash].css',
}),
// PNG — 이미지 포멧 최적화
new ImageMinimizerPlugin({
minimizerOptions: {
plugins: ['pngquant'],
},
}),
// WEBP — 이미지 포멧 최적화
new ImageMinimizerPlugin({
deleteOriginalAssets: true,
filename: '[name].webp',
minimizerOptions: {
plugins: ['imagemin-webp'],
},
}),
// JPE?G — 이미지 포멧 최적화
new ImageMinimizerPlugin({
// 약 8kb 이상 파일만 최적화 적용
filter: (source) => {
if (source.byteLength >= 8192) {
return true
}
return false
},
minimizerOptions: {
plugins: [['jpegtran', { progressive: true }]],
},
}),
new ImageMinimizerPlugin({
// 약 8kb 미만 파일만 최적화 적용
filter: (source) => {
if (source.byteLength < 8192) {
return true
}
return false
},
minimizerOptions: {
plugins: [['jpegtran', { progressive: false }]],
},
})
)
}
/* -------------------------------------------------------------------------- */
module.exports = {
// 입력 파일
entry,
// 출력 파일
output: {
path: path.resolve(__dirname, './dist'),
// 파일 이름 브라우저 캐싱 처리
filename: '[name].[contenthash].js',
// publicPath: 'dist/',
},
// 감시 설정 (package.json 스크립트로 제어)
// watch: true,
// 모드 설정
mode: isDevMode ? 'development' : 'production',
// 소스맵 설정
devtool: isDevMode ? 'inline-source-map' : 'source-map',
// 플러그인 설정
plugins,
// 모듈 설정
module: {
// 로더 규칙
rules: [
// 이미지 파일 로더
{
test: /\.(png|jpe?g|gif|svg|webp)$/i,
use: {
loader: 'file-loader',
options: {
// name: '[name].[ext]',
name: '[name].[contenthash].[ext]',
},
},
},
// 스타일 파일 로더
{
test: /\.(sa|sc|c)ss$/i,
exclude: /node_modules/,
use: [
isDevMode ? 'style-loader' : MiniCssExtractPlugin.loader,
'css-loader',
'sass-loader',
],
},
// 스크립트 파일 로더
{
test: /\.m?js$/i,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env', '@babel/preset-react'],
plugins: ['@babel/plugin-proposal-class-properties'],
sourceMap: true,
},
},
},
],
},
// 최적화 설정
optimization: {
// 압축
minimize: isDevMode ? false : true,
// 미니마이저
minimizer: [
// 플러그인 인스턴스 생성
new CssMinimizerPlugin({
// CPU 멀티 프로세서 병렬화 옵션 (기본 값: true)
// os.cpus() → CPU 코어 정보를 포함하는 배열 객체 반환
parallel: os.cpus().length - 1,
}),
// 미디어쿼리 그룹 병합
new CSSMQPackerPlugin({
sort: true,
}),
],
},
}
구성할 내용이 많아지고, 에 따라 조건 처리해야 할 경우 구성 파일은 상당히 복잡해집니다. 이런 경우 개발용, 배포용 구성 파일을 각각 만들어 효율적으로 관리할 수도 있습니다.