Webpack 훑어보기 - 적용

Lee Sang Hyuk·2023년 4월 21일
0

Bundling

목록 보기
2/2
post-thumbnail

시작하기 전

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 코드

// 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를 통해 분석하고 줄일 수 있는 방법을 찾고 고쳐보는 시간을 가져야 하겠다.

profile
개발자가 될 수 있을까?

0개의 댓글