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
// webpack.config.js
const path = require('path');
module.exports = {
// 선택한 모드를 통해 webpack이 알맞은 내장 최적화를 사용
mode: "production", // "production" | "development" | "none"
// ./src 를 기본으로 함
// 애플리케이션이 여기에서 실행되며 webpack이 번들링을 시작
entry: "./app/entry", // string | object | array
// webpack이 결과를 내보내는 방법과 관련된 옵션
output: {
// 모든 출력 파일의 대상 디렉터리는 반드시 절대 경로 여야함 (Node.js의 path 모듈을 사용)
path:path.resolve(__dirname, "dist"), // string (기본값)
filename: "[name].js", // string (기본값)
}
},
// 모듈 관련 설정
module: {
// 모듈에 대한 규칙 (로더 설정, 파서 옵션 등)
rules: [
{
// 각각의 정규식 또는 문자열을 허용하는 일치 조건
// test 및 include 동작은 동일하며 둘 다 일치해야함
// exclude는 일치하지 않아야함 (test 및 include 보다 우선함)
test: /\.js$/,
loader: "babel-loader",
include: [
path.resolve(__dirname, "app")
],
exclude: /node_modules/
},
{
test: /\.(png|jpg|gif|svg)$/,
loader: 'file-loader',
options: {
name: '[name].[ext]?[hash]'
}
}
},
// 파일의 연관관계를 해석할 때의 해석 방식을 지정
// 모듈 요청 해석 옵션
resolve: {
// 모듈을 해석할 때 검색할 디렉터리
modules: ["node_modules",path.resolve(__dirname, "app")],
// 사용자가 import할 때 확장자를 생략 할 수 있도록 한다.
extensions: [".js", ".json", ".jsx", ".css"],
// 모듈 이름 별칭 목록
// 현재 컨텍스트 기준으로 별칭을 import
alias: {
"@": path.resolve(__dirname, "src/")
},
},
// 브라우저 devtools에 대한 메타 정보를 추가하여 디버깅 향상
// 빌드 속도는 느리나 가장 상세한 소스맵.
devtool: "source-map", // enum
// devServer 설정
devServer: {
port: 9000,
},
plugins: [
// ...
],
}
1. entry
webpack이 번들 빌드를 시작하는 곳이다.
1) entry 유형
- SPA(Single Page Application): 엔트리 포인트가 1개이다.
- MPA(Multi Pate Application): 다중 엔터리 포인트를 갖고 있다.
1
2
3
4
5
6
7
8
9
// 다중 엔트리 포인트
module.exports = {
//...
entry: {
home: './home.js',
about: './about.js',
contact: './contact.js',
},
};
2) entry 파일
entry 속성에 지정된 파일에는 애플리케이션의 전반적인 구조와 내용이 담겨야 한다.
웹 팩이 해당 파일을 가지고 애플리케이션에서 사용되는 모듈의 연관 관계를 이해하고 분석하기에 애플리케이션을 동작시킬 수 있는 내용이 담겨야 한다.
1
2
3
4
5
6
7
8
9
10
// 예시) vue의 main.js
import { createApp } from "vue";
import App from "./App.vue";
import router from "./router";
import store from "./store";
const app = createApp(App);
app.use(store).use(router);
app.mount("#app");
2. output
output속성에는 웹팩으로 번들링 한 결과물을 저장하는 파일 경로를 의미한다.
1) Path
모든 출력 파일의 대상 디렉터리는 반드시 절대 경로여야 한다. (Node.js의 path 모듈을 사용)
2) 파일 이름 옵션
- 단일 엔트리 포인트의 경우, 정적인 이름으로 설정할 수 있다.
- 그러나 둘 이상의 엔트리 포인트 , 코드 뿐할 또는 다양한 플러그인을 통해 여러 번들을 생성할 때 다음과 같은 파일 이름 옵션을 사용하여 각 번들에 고유한 이름을 부여해야 한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
module.exports = {
output: {
// 엔트리의 이름을 사용
filename: '[name].bundle.js'
// 내부 청크 id를 사용
filename: '[id].bundle.js'
// 여러 옵션을 조합해서 사용
filename: '[name].[hash].bundle.js'
// 생성된 콘텐츠에서 생성된 해시를 사용
filename: '[chunkhash].bundle.js'
}
};
3. module
프로젝트 내에서 다른 유형의 모듈을 처리하는 방법을 결정한다.
1
2
3
4
5
6
// webpack.config.js
module.exports = {
module: {
rules: []
}
}
1) rules
모듈이 생성될 때 요청과 일치하는 Rule의 배열이다. 이러한 규칙은 모듈 생성 방법을 수정할 수 있다.
로더를 모듈에 적용시키거나 파서를 수정할 수 있다.
- test: loader를 적용할 모듈 (정규식 사용)
- use: 해당 모듈에 적용되는 Loader의 배열
use: ["style-loader"]
는use:[{loader: "style-loader"}]
와 같다.- 여러 로더를 전달하여 연결할 수 있으며, 맨 오른쪽에서 부터 순차적으로 로더가 적용된다.
- ex)
use: ["style-loader", "css-loader", "scss-loader"]
- exclude: 조건과 일치하는 모든 모듈을 제외한다.
- include: 조건과 일치하는 모든 모듈을 포함한다.
2) loader가 필요한 이유
만약 loader없이, 다른 유형의 모듈 파일을 엔트리 파일에 import하려고 하면 다음과 같은 에러가 발생한다.
1
2
3
ERROR in ./base.css 1:2
Module parse failed: Unexpected token (1:2)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
- 이는 base.css를 loader없이 index.js(엔트리 파일)에 import하려고 해서 발생한 오류이다.
- 해당 파일 타입을 다루기 위해 loader를 사용하라는 의미이다.
4. plugin
다양한 방법으로 webpack 빌드 프로세스를 사용자 정의하는 데 사용된다.
1
2
3
4
// webpack.config.js
module.exports = {
plugins: []
}
- 웹팩의 기본적인 동작에 추가적인 기능을 제공하는 속성이다.
- 플러그인은 해당 결과물의 형태를 바꾸는 역할을 한다.
- 플러그인의 배열에는 생성자 함수로 생성된 객체 인스턴스만 추가될 수 있다.
1) 플러그인 예시
MiniCssExtractPlugin을 사용한다면 다음과 같이 코드를 작성할 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// webpack.config.js
var path = require("path");
var MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
mode: "none",
entry: "./index.js",
output: {
filename: "bundle.js",
path: path.resolve(__dirname, "dist"),
},
module: {
rules: [
{
test: /\.css$/,
use: [{ loader: MiniCssExtractPlugin.loader }, "css-loader"],
},
],
},
// 결과물에 대한 정보를 바꾼다.
plugins: [new MiniCssExtractPlugin()],
};
해당 플러그인의 사용 전 후는 다음과 같다.
사용 전
사용 후
이렇게 dist 폴더의 구조가 변경된다. 해당 플러그인은 CSS파일을 필요로하는 JS파일만 CSS파일을 생성한다. 그 결과, bundle.js에는 css가 포함되어 있지 않다.
해당 플러그인을 사용하면 html에 직접 스타일 시트를 추가해야 한다. 이전에는 bundle.js에 css가 포함되어 있어서 style-loader를 사용하면 추가할 필요가 없었다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>CSS & Libraries Code Splitting</title>
<!-- style-loader를 사용하지 않고 직접 추가한다. -->
<link rel="stylesheet" href="./dist/main.css" />
</head>
<body>
<header>
<h3>CSS Code Splitting</h3>
</header>
<div>
<p>This text should be colored with blue after injecting CSS bundle</p>
</div>
<!-- 웹팩의 빌드 결과물을 로딩하는 스크립트 -->
<script src="./dist/bundle.js"></script>
</body>
</html>
5. devServer
개발을 할 때, 수정한 코드를 확인하려면 매번 빌드를 해야 한다. 이런 불편함을 없애기 위해 webpack의 dev 서버를 사용할 수 있다.
- webpack dev 서버는 빌드를 매번 안 해도 코드의 변경 사항을 확인 할 수 있다.
npm run dev
명령어는 dist 폴더를 생성하지 않는다.- 즉, 메모리에서 빌드 결과물을 그냥 올려놓는다.
- 메모리 상으로만 빌드 결과물 올려놓아 실시간 확인이 가능하다.
1
2
# 필요한 라이브러리 설치
$ npm i webpack webpack-cli webpack-dev-server html-webpack-plugin -D
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// package.json
{
"name": "devserver",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
// dev 서버를 여는 명령어 추가
"dev": "webpack-dev-server",
"build": "webpack"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"html-webpack-plugin": "^3.2.0",
"webpack": "^4.42.0",
"webpack-cli": "^3.3.11",
"webpack-dev-server": "^3.10.3"
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// webpack.config.js
const path = require('path');
module.exports = {
mode: 'developement',
entry: './index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
},
devServer: {
port: 9000,
},
};
6. merge
merge를 활용하면 Webpack 설정 파일을 나눠서 관리할 수 있다.
1) 폴더 생성
1
2
# Webpack 개발, 서버, 빌드, 유틸리티 구성 파일을 webpack 디렉토리 안에 생성한다.
$ code webpack/config.{dev,server,build,utils}.js
2) 각 파일 설정
1
2
3
4
5
// webpack/config.utils.js
const path = require('path');
const getAbsPath = (dirOrFile) => path.resolve(process.cwd(), dirOrFile);
exports.getAbsPath = getAbsPath;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 개발 구성
// webpack/config.dev.js
const { getAbsPath } = require('./utils');
const devConfig = {
target: 'web',
mode: 'development',
devtool: 'eval-source-map',
entry: {
main: getAbsPath('src/index.js'),
},
output: {
path: getAbsPath('public'),
filename: 'js/main.js',
}
};
module.exports = devConfig;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 서버 구성
// webpack/config.server.js
const { merge } = require('webpack-merge');
const devConfig = require('./dev');
const serverConfig = {
devServer: {
static: ['public'],
client: {
overlay: true,
},
compress: true,
host: 'localhost',
port: 3000,
open: false,
},
};
module.exports = merge(devConfig, serverConfig);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 빌드 구성
// webpack/config.build.js
const { merge } = require('webpack-merge');
const { getAbsPath } = require('./utils');
const devConfig = require('./dev');
const buildConfig = {
mode: 'production',
devtool: false,
output: {
path: getAbsPath('public'),
filename: 'js/bundle.min.js',
},
};
module.exports = merge(devConfig, buildConfig);
1
2
3
4
5
6
7
8
9
10
// package.json
{
"scripts": {
"start": "npm run dev -- --open",
"bundle": "webpack --config webpack/config.dev.js",
"dev": "webpack serve --config webpack/config.server.js",
"build": "webpack build --config webpack/config.build.js"
},
}