로더
는 파일 단위로 처리하는 반면 플러그인
은 번들된 결과물을 처리한다
번들된 자바스크립트를 난독화 한다거나 특정 텍스트를 추출하는 용도로 사용한다.
class MyPlugin {
apply(compiler) {
compiler.hooks.done.tap("My Plugin", (stats) => {
console.log("MyPlugin: done");
});
}
}
module.exports = MyPlugin;
플러그인이 동작하면 MyPlugin: done 문자열이 출력되도록 했다
이제 해당 플러그인을 webpack.config.js에 설정해보자
const path = require("path");
const MyWebpackPlugin = require("./my-webpack-plugin");
module.exports = {
mode: "development",
entry: {
main: "./src/app.js",
},
output: {
path: path.resolve("./dist"),
filename: "[name].js",
},
module: {
rules: [
{
test: /\.css$/,
use: ["style-loader", "css-loader"],
},
{
test: /\.(png|jpg|jpeg|gif|svg)$/,
type: "asset", // 40KB(default) 미만은 inline, 이상은 resource로 대처
parser: {
dataUrlCondition: {
maxSize: 20 * 1024, // 기준을 20KB 로 변경
},
},
},
],
},
plugins: [new MyWebpackPlugin()],
};
로더를 module이라는 객체에 넣었다면, 플러그인은 plugins
라는 배열에 추가하면 된다
배열 안에 만든 플러그인 클래스의 생성자를 new 키워드
를 사용해서 생성해주면 된다
npm run build 명령어를 통해 결과를 확인해보자
npm run build
> webpack-babel-practice@1.0.0 build
> webpack
MyPlugin: done
assets by status 1.17 MiB [cached] 1 asset
asset main.js 52.8 KiB [compared for emit] (name: main)
runtime modules 2.42 KiB 8 modules
javascript modules 10.7 KiB
modules by path ./node_modules/ 8.66 KiB
modules by path ./node_modules/style-loader/dist/runtime/*.js 5.75 KiB 6 modules
modules by path ./node_modules/css-loader/dist/runtime/*.js 2.91 KiB
./node_modules/css-loader/dist/runtime/noSourceMaps.js 64 bytes [built] [code generated]
+ 2 modules
modules by path ./src/ 2.05 KiB
./src/app.js 181 bytes [built] [code generated]
./src/app.css 1.11 KiB [built] [code generated]
./node_modules/css-loader/dist/cjs.js!./src/app.css 779 bytes [built] [code generated]
asset modules 24.8 KiB (javascript) 1.17 MiB (asset)
./src/nyancat.jpeg 24.7 KiB [built] [code generated]
./src/bg.png 42 bytes (javascript) 1.17 MiB (asset) [built] [code generated]
webpack 5.73.0 compiled successfully in 143 ms
콘솔에 MyPlugin: done
이 잘 출력되는것을 확인할 수 있다
로더가 여러개의 파일에 대해서 각각 실행했다면 플러그인은 번들 파일에 대해서 한번 실행된다
그러면 어떻게 번들 결과에 접근할 수 있을까?
class MyWebpackPlugin {
apply(compiler) {
// compiler.hooks.done.tap("My Plugin", (stats) => {
// console.log("MyPlugin: done");
// });
compiler.hooks.emit.tapAsync("My Plugin", (compilation, callback) => {
const source = compilation.assets["main.js"].source();
console.log(source);
callback();
});
}
}
module.exports = MyWebpackPlugin;
compilation 객체를 이용해서 webpack이 번들링한 결과인 main.js에 접근할 수 있다
npm run build를 실행하면 콘솔에 번들 결과인 main.js의 내용이 출력되는 것을 확인할 수 있다
BannerPlugin
은 결과물에 빌드 정보나 커밋 버전같은 걸 추가할 수 있다
webpack이 기본적으로 제공하는 플러그인이다
const webpack = require('webpack');
...
plugins: [
new webpack.BannerPlugin({
banner: "이것은 배너입니다.",
}),
],
...
webpack에서 기본으로 제공하는 플러그인 이므로 webpack 모듈을 사용한다
plugins 배열에 new 키워드로 BannerPlugin 생성자를 호출하는데 생성자에 객체를 전달할 수 있다
객체에 banner
: "이것은 배너입니다."를 입력하고 build를 해보자
npm run build
명령어를 입력하여 번들링 후 번들 결과인 main.js에서 확인해보면
상단에 "이것은 배너입니다." 라는 주석이 추가
된 것을 확인할 수 있다
...
const webpack = require("webpack");
const ChildProcess = require("child_process"); // 터미널 명령어를 실행할 수 있음
...
plugins: [
new webpack.BannerPlugin({
banner: `
month: "long",
Build Date: ${new Date().toLocaleDateString("ko", {
day: "numeric",
year: "numeric",
})}
Commit Version: ${ChildProcess.execSync("git rev-parse --short HEAD")}
Author: ${ChildProcess.execSync("git config user.name")}
`,
}),
],
...
main.js 최상단에 빌드날짜, 커밋버전, 작성자 같은 정보를 추가할 수 있다
애플리케이션은 개발환경과 운영환경으로 나눠서 운영한다
같은 소스 코드를 두 환경에 배포하기 위해서는 이러한 환경 의존적인 정보를 소스가 아닌 곳에서 관리하는 것이 좋다
배포할 때마다 코드를 수정하는 것은 곤란하기 때문이다
웹팩은 이러한 환경 정보를 제공
하기 위해 DefinePlugin
을 제공한다
...
plugins: [
new webpack.DefinePlugin({}),
],
...
빈 객체를 전달해도 기본적으로 넣어주는 값이 있다.
노드 환경정보인 process.env.NODE_ENV
인데 웹팩 설정의 mode에 설정한 값이 여기에 들어간다
현재는 development
를 설정했기 때문에 애플리케이션 코드에서 process.env.NODE_ENV 변수로 접근하면 development
값을 얻을 수 있다
이 외에도 직접 환경변수를 넣을 수 있다
...
plugins: [
new webpack.DefinePlugin({
TWO: "1+1",
}),
],
...
import "./app.css";
import nyancat from "./nyancat.jpeg";
document.addEventListener("DOMContentLoaded", () => {
document.body.innerHTML = `
<img src="${nyancat}" />
`;
});
console.log(process.env.NODE_ENV);
console.log(TWO);
HtmlTemplatePlugin
은 HTML 파일을 후처리
하는데 사용한다
빌드 타임의 값을 넣거나 코드를 압축할 수 있다
먼저 패키지를 다운로드 한다
npm i -D html-webpack-plugin
이 플러그인으로 빌드하면 HTML파일로 아웃풋에 생성
될 것이다
index.html도 웹팩으로 빌드할 것이기 때문에 index.html 파일을 src/index.html로 옮긴고 기존에 빌드된 main.js파일을 로드하는 script태그를 삭제한다
...
const HtmlWebpackPlugin = require("html-webpack-plugin");
...
plugins: [
...
new HtmlWebpackPlugin({
template: "./src/index.html",
templateParameters: {
env: process.env.NODE_ENV === "development" ? "(개발용)" : "",
},
}),
...
],
template에 index.html경로를 전달해준다
빌드 후 dist폴더를 확인해보면 index.html파일이 생성되었다
index.html파일을 확인해보면 script태그가 생성되어 있는것을 확인할 수 있다
HtmlTemplatePlugin을 사용하면 빌드 과정에 HTML도 포함하기 때문에 좀 더 의존적이지 않은 코드로 HTML을 만들 수 있다
...
<title>Document<%= env %></title>
...
templateParameters
를 통해 개발환경 모드에 따라 HTML에 값을 전달해 줄 수도 있다
터미널에 NODE_ENV=development npm run build
를 입력하고 dist/index.html를 확인해보자
...
<title>Document(개발용)</title>
...
HtmlTemplatePlugin은 HTML을 압축
하고 주석을 제거
하는 기능도 있다
src/index.html에 주석을 추가하고 webpack.config.js에서 주석 제거와 빈칸 제거를 해보겠다
...
new HtmlWebpackPlugin({
template: "./src/index.html",
templateParameters: {
env: process.env.NODE_ENV === "development" ? "(개발용)" : "",
},
minify: {
collapseWhitespace: true, // 빈칸 제거
removeComments: true, // 주석 제거
},
}),
...
minify 객체에 collapseWhitespace
와 removeComments
를 true
로 설정해주고 빌드를 해보자
빌드 결과를 확인해보면 다음과 같다
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Document(개발용)</title><script defer="defer" src="main.js"></script></head><body></body></html>
주석이 제거되었고 코드가 한줄로 나열되어 있는 것을 확인할 수 있다
minify:
process.env.NODE_ENV === "production"
? {
collapseWhitespace: true, // 빈칸 제거
removeComments: true, // 주석 제거
}
: false,
위와 같이 개발환경에서는 적용하지 않고 배포환경에서만 적용하도록 할 수 있다
CleanWebpackPlugin
은 빌드 이전 결과물을 제거
하는 플러그인이다
ouput경로에 남아 있을 수 있는 과거 파일을 제거해준다
먼저 패키지를 설치해준다
npm i -D clean-webpack-plugin
...
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
...
plugins: [
...
new CleanWebpackPlugin(),
],
...
webpack.config.js를 위와 같이 설정하고 output폴더인 /dist에 임의의 파일인 foo.js를 생성하자
생성 후 빌드해보면 dist폴더에 foo.js가 삭제된 것을 확인할 수 있다
스타일시트가 점점 많아지면 하나의 자바스크립트 결과물로 만드는 것은 부담일 수 있다
번들 결과에서 스타일시트 코드만 뽑아서 별도의 CSS 파일
로 만들어 역할에 따라 파일을 분리하는 것이 좋다
브라우저에서 큰 파일 하나를 내려받는 것 보다 여러 개의 작은 파일을 동시에 다운로드 하는 것이 더 빠르다
개발 환경에서는 CSS를 하나의 모듈로 처리해도 상관없지만 프로덕션 환경에서는 분리하는 것이 효과적이다
MiniCssExtractPlugin
은 CSS를 별도 파일로 뽑아내는 플러그인
이다
먼저 패키지를 설치한다
npm i -D mini-css-extract-plugin
...
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
...
plugins: [
...
new MiniCssExtractPlugin({filename: '[name].css'}),
],
...
filename을 [name].css
로 설정하여 원본 파일명
을 가지도록 했다
MiniCssExtractPlugin은 굳이 개발환경에서는 적용할 필요는 없으므로 모드에 따라 적용
시키도록 한다
...
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
...
plugins: [
...
...(process.env.NODE_ENV === "production"
? [new MiniCssExtractPlugin({ filename: "[name].css" })]
: []),
],
...
이 플러그인은 다른 플러그인과 다르게
로더
설정도 해야한다
MiniCssExtractPlugin을 사용하려면 style-loader 대신에 자체적으로 제공하는 로더
를 사용해야 한다
...
module: {
rules: [
{
test: /\.css$/,
use: [
process.env.NODE_ENV === "production"
? MiniCssExtractPlugin.loader
: "style-loader",
"css-loader",
],
},
...
NODE_ENV를 production으로 빌드를 해보자
NODE_ENV=production npm run build
dist폴더에 main.css가 생성된 것을 확인할 수 있다
dist/index.html에 link태그로 main.css를 불러오는 코드가 추가되었다
참고: https://jeonghwan-kim.github.io/series/2019/12/10/frontend-dev-env-webpack-basic.html
https://wnsah052.tistory.com/188