[Webpack] Plugins

2021. 6. 12. 10:11Frontend

 

 

위 포스팅은 이전 포스트의 내용을 이어서 진행합니다.

[Webpack] Config file & Asset Modules
[Webpack] Introduction & Setup
[Webpack] Loaders

 

 

Plugins

webpack 공식 문서에는 플러그인에 대해 다음과 같이 정의하고 있습니다.

로더는 특정 유형의 모듈을 변환하는 데 사용되지만, 플러그인을 활용하여 번들을 최적화하거나, 애셋을 관리하고, 또 환경 변수 주입등과 같은 광범위한 작업을 수행 할 수 있습니다.

 

플러그인은 webpack의 핵심 요소이며, webpack의 컴파일 프로세스를 활용할 수 있는 방법을 제공해 준다는 점에서 커스텀 영역을 넓혀줍니다. 플러그인은 webpack 컴파일(번들링)과정에서 발생하는 주요 이벤트에 후킹하여 여러가지 기능들을 추가로 제공할 수 있도록 도와줍니다. 플러그인을 사용하면 모듈이 제공하지 못하는 기능들을 제공하므로써 효율적으로 번들 관리를 할 수 있습니다. 이를테면, Uglify bundle(코드 난독화), Minify CSS(css to js파일을 별도의 번들로 분리) 등의 기능을 플러그인을 사용해서 제공할 수 있습니다.

 

 

이번 포스팅에서는 webpack에서 자주 사용되는 여러 플러그인들을 직접 설치하고 결과물들을 확인해보도록 하겠습니다.

 

TerserPlugin

Javascript번들 사이즈를 줄이는 것은 웹 애플리케이션의 성능을 높이는 좋은 방법 중 하나입니다. 동일한 코드이지만, 변수명을 줄이고 공백을 제거하는 등의 uglify를 수행하면 코드의 양이 줄어들어 네트워크 대역폭을 아낄 수 있게 되고, 이에 따라 조금 더 빠른 애플리케이션 성능을 얻을 수 있습니다. webpack 공식문서에서 추천하는 uglify 플러그인은 terser 플러그인입니다. 

 

Uglify 수행 전 번들의 사이즈는 대략 18kb정도입니다.

 

 

yarn add -D terser-webpack-plugin

 

webpack.config.js

const path = require("path");
const TerserPlugin = require("terser-webpack-plugin");

module.exports = {
  entry: "./src/index.js",
  output: {
    filename: "bundle.js",
    path: path.resolve(__dirname, "./dist"),
  },
  mode: "none",
  module: {
    rules: [
      {
        test: /\.(png|jpg)$/,
        type: "asset/resource",
      },
      {
        test: /\.(css)$/,
        use: ["style-loader", "css-loader"],
      },
      {
        test: /\.(scss)$/,
        use: ["style-loader", "css-loader", "sass-loader"],
      },
      {
        test: /\.(js$)/,
        use: {
          loader: "babel-loader",
          options: {
            presets: ["@babel/env"],
          },
        },
      },
    ],
  },
  plugins: [new TerserPlugin()],
};

 

빌드한 결과물은 다음과 같습니다. 아래와 같이 코드의 변수명이 변경되고 공백이 제거되었으며 용량도 18kb에서 6kb로 1/3로 감소한 것을 확인할 수 있습니다.

(()=>{"use strict";var e=[,(e,t,n)=>{n.r(t),n.d(t,{default:()=>c});var r=n(2),o=n.n(r),i=n(3),a={insert.....

 

MiniCssExtractPlugin

이전 포스팅에서 살펴본 예시에서는 style-loader, css-loader, sass-loader 를 사용해서 css파일을 js파일로 번들링하여 bundle.js에 합치는 것을 살펴보았습니다. 간단하게 정리하면 sass-loader는 scss, sass문법을 css로 변환하는데 사용되고, css-loader는 css파일들을 읽는 데 사용되며, style-loader는 읽은 css파일들을 <style> 태그로 만들어서 head tag안에 넣을 수 있도록 js코드를 만들어줍니다. (js파일이 실행되면서 html head에 동적으로 넣어줌)

 

이러한 방식은 간편하다는 장점이 있지만, 한편으로는 스타일 태그가 많아질수록 해당 스타일 태그를 처리하는 자바스크립트 파일의 크기가 늘어가게 되므로 메인 번들 사이즈가 커진다는 단점이 있습니다. 큰 사이즈의 애플리케이션의 경우 style을 처리하는 css파일을 따로 분리하여 번들링하는 것이 좋습니다. 이러한 기능을 지원해주는 플러그인이 바로 MiniCssExtractPlugin입니다.

 

이 플러그인을 사용하면 style태그를 읽어서 (sass-loader, css-loader 처리 이후) js코드로 만드는 것이 아닌, 별도의 css파일을 추출해 줍니다. 

 

yarn add -D mini-css-extract-plugin

 

webpack.config.js

const path = require("path");
const TerserPlugin = require("terser-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

module.exports = {
  entry: "./src/index.js",
  output: {
    filename: "bundle.js",
    path: path.resolve(__dirname, "./dist"),
  },
  mode: "none",
  module: {
    rules: [
      {
        test: /\.(png|jpg)$/,
        type: "asset/resource",
      },
      {
        test: /\.(css)$/,
        use: [MiniCssExtractPlugin.loader, "css-loader"],
      },
      {
        test: /\.(scss)$/,
        use: [MiniCssExtractPlugin.loader, "css-loader", "sass-loader"],
      },
      {
        test: /\.(js$)/,
        use: {
          loader: "babel-loader",
          options: {
            presets: ["@babel/env"],
          },
        },
      },
    ],
  },
  plugins: [new TerserPlugin(), new MiniCssExtractPlugin()],
};

 

다음과 같이 bundle.js 아래에 css만 따로 묶여서 번들링 된 것을 확인할 수 있습니다. 

 

 

Browser Caching

로컬에서 간단하게 테스트를 할 때는 별 문제가 없지만 실제로 프로덕션에 정적 웹사이트 파일을 배포하고, 여러 지역에 좋은 퀄리티의 서비스를 제공하기 위해서는 CDN, AWS CloudFront등의 엣지 네트워크에 해당 파일(여기서는 js, css, html 번들등을 의미)들을 올려두고 빠르게 리소스에 접근할 수 있도록 하는 것이 일반적입니다. 

 

이때 중요한 것이 Cache Invalidation입니다. 즉 '캐시 무효화' 라는 것인데, 만약 웹 서버에서 새로운 번들을 생성해서 배포하면 엣지 네트워크에 있는 캐싱 파일을 무효화 해서 새롭게 생성된 번들로 교체해 주어야 한다는 것입니다. 그래야 엣지 네트워크에서도 항상 새로운 버전의 번들 파일을 제공할 수 있게 됩니다. 

 

물론 최근 클라우드 컴퓨팅 서비스를 제공하는 GCP, AWS등에서는 CloudFront등의 엣지네트워크에서 별도의 설정 없이도 캐시 무효화 기능을 제공해주기도 하지만, webpack에서는 이를 위해 bundle name에 해싱을 적용하는 기법을 제공합니다. 즉 번들 파일을 생성할때마다 unique한 이름의 파일을 생성함으로써 파일 이름의 비교만으로 캐시 무효화 전략을 수행하도록 도와주는 것입니다.

 

설정 자체는 간단합니다.

 

webpack.config.js

... config
 output: {
    filename: "bundle[contenthash].js",
    path: path.resolve(__dirname, "./dist"),
  },
... config

filename에  [contenthash]를 넣어줄 경우 bundle5755a90a43f2dfe2836c 와 같은 이름의 파일을 생성해 줍니다. 중요한 것은 내부 코드가 수정되지 않은 빌드에 대해서는 해시 아이디를 바꾸지 않는 다는 것입니다. 따라서 내부 코드를 수정하지 않은 채로 다시 빌드를 하게 되면 filename이 변경되지 않지만, 내부 로직을 변경할 경우에는 filename이 변경하는 것을 확인할 수 있습니다.

 

 

Extra Plugins

위의 여러 플러그인들을 통해 플러그인이 무엇이고, 어떻게 적용하는지에 대해 알아보았습니다. 위에 소개한 플러그인들 이외에 번들링에 자주 소개하는 플러그인들을 간단하게 소개하도록 하겠습니다.

 

CleanWebpackPlugin

 

새로운 번들을 빌드할때 기존에 빌드했던 내용을 제거하고 새롭게 빌드하는 것이 깔끔한 리소스 관리 차원과 용량 관리 차원에서 좋을 때가 있습니다. 해당 플러그인을 사용하면 위 기능을 제공할 수 있습니다.

 

 

HTMLWebpackPlugin

 

위 플러그인을 사용하면 webpack에서 생성된 번들을 자동으로 import하는 HTML파일을 생성해줍니다. 매 빌드마다 bundle의 이름을 바꾸게 된다면 그때마다 해당 번들을 생성하는 HTML파일도 변경해주어야 하므로, 위 플러그인을 통해 html파일도 같이 생성해주는 편이 권장됩니다. 

 

yarn add -D clean-webpack-plugin html-webpack-plugin

 

webpack.config.js

const path = require("path");
const TerserPlugin = require("terser-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const HtmlWepbackPlugin = require("html-webpack-plugin");

module.exports = {
  entry: "./src/index.js",
  output: {
    filename: "bundle[contenthash].js",
    path: path.resolve(__dirname, "./dist"),
  },
  mode: "none",
  module: {
    rules: [
      {
        test: /\.(png|jpg)$/,
        type: "asset/resource",
      },
      {
        test: /\.(css)$/,
        use: [MiniCssExtractPlugin.loader, "css-loader"],
      },
      {
        test: /\.(scss)$/,
        use: [MiniCssExtractPlugin.loader, "css-loader", "sass-loader"],
      },
      {
        test: /\.(js$)/,
        use: {
          loader: "babel-loader",
          options: {
            presets: ["@babel/env"],
          },
        },
      },
    ],
  },
  plugins: [
    new TerserPlugin(),
    new MiniCssExtractPlugin({
      filename: "styles.[contenthash].css",
    }),
    new CleanWebpackPlugin({
      cleanOnceBeforeBuildPatterns: [
        "**/*",
        path.join(process.cwd(), "build/**/*"),
      ],
    }),
    new HtmlWepbackPlugin(),
  ],
};

 

Reference

https://webpack.js.org/plugins/terser-webpack-plugin/

 

TerserWebpackPlugin | webpack

webpack is a module bundler. Its main purpose is to bundle JavaScript files for usage in a browser, yet it is also capable of transforming, bundling, or packaging just about any resource or asset.

webpack.js.org

 

반응형

'Frontend' 카테고리의 다른 글

Async / Await Under the Hood  (1) 2021.06.29
[Webpack] Code Splitting  (2) 2021.06.17
[Webpack] Loaders  (0) 2021.06.11
[Webpack] Config file & Asset Modules  (0) 2021.06.11
[Webpack] Introduction & Setup  (0) 2021.06.09