Webpack에 대해 간단하게 이해를 하고 본격적으로 실습해보면서 체감을 느껴보고 싶었다. 간단하게 Vanilla JS로 Todo List 웹 페이지를 만든 후, Webpack을 적용하려고 한다. 복잡도가 낮은 웹 애플리케이션이라 큰 체감을 못 느낄 수 있지만 수치를 통해서 변화가 있는지 확인하고 싶다.
/dist
: webpack에 대한 결과물/src
: 웹 자원들(HTML, CSS, Images, app.js가 main)package.json
: 패키지 설치 목록webpack.config.js
: Webpack 속성 관리 파일// createTodoItem.js
// Todo 아이템 생성 역할
import anime from "animejs/lib/anime.es.js";
const createTodoItem = (value) => {
let isDescriptDisabled = true;
const container = document.createElement("li");
container.style.transform = "translateY(30px)";
container.style.opacity = 0;
const itemBox = document.createElement("div");
const description = document.createElement("input");
description.setAttribute("type", "text");
description.value = value;
description.disabled = isDescriptDisabled;
const checkBox = document.createElement("input");
checkBox.setAttribute("type", "checkbox");
checkBox.addEventListener("change", (e) => {
description.style.textDecorationLine = e.target.checked
? "line-through"
: "none";
});
const buttonBox = document.createElement("div");
const editButton = document.createElement("button");
editButton.className = "edit__button__default";
editButton.addEventListener("click", () => {
isDescriptDisabled = !isDescriptDisabled;
description.disabled = isDescriptDisabled;
editButton.className = isDescriptDisabled
? "edit__button__default"
: "edit__button__cancel";
});
const deleteButton = document.createElement("button");
deleteButton.className = "delete__button";
deleteButton.addEventListener("click", () => {
anime({
targets: container,
translateX: -50,
opacity: 0,
duration: 500,
});
setTimeout(() => {
container.remove();
}, 200);
});
buttonBox.appendChild(editButton);
buttonBox.appendChild(deleteButton);
itemBox.appendChild(checkBox);
itemBox.appendChild(description);
itemBox.appendChild(buttonBox);
container.appendChild(itemBox);
return container;
};
export default createTodoItem;
// app.js
// 필수 DOM 생성 및 Event 등록
import anime from "animejs/lib/anime.es.js";
const createTodoItem = (value) => {
let isDescriptDisabled = true;
const container = document.createElement("li");
container.style.transform = "translateY(30px)";
container.style.opacity = 0;
const itemBox = document.createElement("div");
const description = document.createElement("input");
description.setAttribute("type", "text");
description.value = value;
description.disabled = isDescriptDisabled;
const checkBox = document.createElement("input");
checkBox.setAttribute("type", "checkbox");
checkBox.addEventListener("change", (e) => {
description.style.textDecorationLine = e.target.checked
? "line-through"
: "none";
});
const buttonBox = document.createElement("div");
const editButton = document.createElement("button");
editButton.className = "edit__button__default";
editButton.addEventListener("click", () => {
isDescriptDisabled = !isDescriptDisabled;
description.disabled = isDescriptDisabled;
editButton.className = isDescriptDisabled
? "edit__button__default"
: "edit__button__cancel";
});
const deleteButton = document.createElement("button");
deleteButton.className = "delete__button";
deleteButton.addEventListener("click", () => {
anime({
targets: container,
translateX: -50,
opacity: 0,
duration: 500,
});
setTimeout(() => {
container.remove();
}, 200);
});
buttonBox.appendChild(editButton);
buttonBox.appendChild(deleteButton);
itemBox.appendChild(checkBox);
itemBox.appendChild(description);
itemBox.appendChild(buttonBox);
container.appendChild(itemBox);
return container;
};
export default createTodoItem;
// webpack.config.js
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const BundleAnalyzerPlugin =
require("webpack-bundle-analyzer").BundleAnalyzerPlugin;
module.exports = {
mode: "none",
entry: "./src/app.js",
output: {
filename: "bundle.js",
path: path.resolve(__dirname, "./dist"),
},
module: {
rules: [{ test: /\.css$/, use: ["style-loader", "css-loader"] }],
},
devServer: {
static: {
directory: path.join(__dirname, "./dist"),
},
compress: true,
hot: true,
port: 9000,
open: true,
},
plugins: [
new HtmlWebpackPlugin({
template: "./src/index.html",
}),
new BundleAnalyzerPlugin({
analyzerMode: "static",
reportFilename: "docs/size_dev.html",
defaultSizes: "parsed",
openAnalyzer: true,
generateStatsFile: true,
statsFilename: "docs/stats_dev.json",
}),
],
};
stat
: Minification 등의 변환이 일어나기 이전의 input 파일 사이즈parsed
: 변환 이후 output된 파일 사이즈gzip
: gzip으로 압축 이후 사이즈기존 57.99KB 사이즈를 26.75KB 사이즈로 압축되어 네트워크로부터 파일을 받아와 출력되는 걸 확인했다. gzip으로 사용하면 더 줄일 수 있다는데 사용자의 PC 혹은 디바이스의 성능이 좋지 못할 경우 파일을 압축 해제하는데 시간이 더 걸릴 수 있다고 한다.
다음에는 webpack 적용된 프로젝트에서 bundle-analyzer를 통해 분석하고 줄일 수 있는 방법을 찾고 고쳐보는 시간을 가져야 하겠다.