Webpack 마이그레이션(v4 → v5)

김현조·2023년 5월 19일
5
post-thumbnail

마이그레이션한 이유

왜 webpack을 유지하고 버전을 업그레이드했을까

Webpack은 2023년 기준으로 vite, rollup 등에 자리를 내어주고 있지만 여전히 번들러로서 1순위를 지키고 있다. 필자의 경험에 비추어볼 때 webpack은 비교적 hot-reload 속도가 느리고, config가 복잡하다는 단점을 갖지만 어떤 라이브러리와 같이 사용해도 관련 레퍼런스가 많아 해결이 가능하다는 점이 장점으로 작용했다.

npm trends

현재 진행 중인 프로젝트의 경우 Phaser.js라는 JavaScript 기반 게임개발 프레임워크를 활용하는데 전임자분께서 webpack v4를 활용한 템플릿으로 프로젝트를 구성해놓신 상황이었다. Phaser.js는 weekly download 수가 약 15,000회 이하로 마이너한 프레임워크이기에 레퍼런스 존재 여부가 중요했다. 따라서 대부분의 레퍼런스가 따르는 webpack 프로젝트 구성을 유지하되, 프로젝트 일정에 맞추어 v5로 마이그레이션을 진행했다. Webpack v5는 2020년 말에 등장했으며, 아래와 같은 장점을 가진다.

  • 주요 loader, plugin 등의 기능이 내부적으로 포함됨
  • Tree shaking, node polyfill 삭제 등으로 번들 사이즈 개선
  • 영구 캐싱으로 빌드 성능 개선
  • 웹 플랫폼과의 호환성 향상

현 프로젝트는 번들사이즈가 총 5MB 이하여야 한다는 점이 중요했기 때문에 번들사이즈 감소의 장점을 얻고자했다. 또 url-loader, clean-webpack-plugin 등 자주 사용되는 패키지들을 default로 config에 추가할 수 있어 추후 다른 분이 이 프로젝트를 다시 열어보게 되더라도 직관적으로 이해할 수 있을 것이라 생각했다.

마이그레이션 과정

마이그레이션을 위해 했던 작업들과 발생 이슈 모음

관련 패키지 버전 올리기

webpack 버전 업그레이드와 함께 같이 사용하는 webpack-cli, webpack-dev-server의 버전을 업그레이드했다. 두 패키지는 모두 webpack을 peer dependency로 지정하고 있기 때문이다.

Peer Dependency란 무엇일까? Peer는 “동료”라는 뜻을 가졌으니 직역해보자면 동료 의존성이 된다. 라이브러리 개발을 해보았다면 사용해보았을 속성으로, 해당 라이브러리가 의존하는 라이브러리와 그 버전을 명시해놓은 내용이다. 예를 들어 react를 기반으로 UI 라이브러리를 개발한다고 가정해보자. 이때 이 UI 라이브러리를 react ^18.0.0 기준으로 개발했다면, 이 라이브러리를 사용하는 개발자들도 해당 버전의 react를 사용하여 예상치 못한 오류를 방지할 수 있도록 알려주는 것이다. npm 7버전을 기준으로 하위 버전에서는 경고만 해주었고, 상위 버전에서는 패키지 설치가 되지 않도록 막아준다.

Webpack-cli와 webpack-dev-server의 경우에도 webpack에 의존적인 패키지이기에 peer dependency에 맞추어 latest로 업그레이드해주었다.

불필요한 모듈 제거하기

Webpack v4에 익숙한 개발자라면 url-loader, file-loader, clean-webpack-plugin, terser-webpack-plugin 등을 들어보았거나 사용해보았을 것이다. 하지만 v5에서는 모두 필요없다! 각각의 역할과 변경점을 알아보자.

Loader

url-loader, file-loader, raw-loader가 모두 asset module로 대체되었다. 별도의 파일을 내보내고 URL을 추출하는 역할의 file-loader는 asset/resource로, asset의 dataURI를 내보내는 url-loader는 asset/inline으로, asset의 소스코드를 내보내는 raw-loader는 asset/source로 대체되었다. 또한 dataURI와 별도 파일 중 asset 크기에 따라 자동으로 선택하는 방식은 asset을 사용할 수 있다.

해당 프로젝트의 경우 모든 asset을 base64로 사용해야해서 asset/inline만을 사용했다. 다만 파일 사이즈 축소를 위해 2의 8제곱개의 색만을 지원하는 png8 이미지를 활용했는데, 해당 확장자의 경우 따로 mimetype을 지정하여 인코딩방식을 작성해주어야 했다. 작성된 config는 다음과 같다.

module.exports = {
  mode: "development",
  module: {
    rules: [
      {
        test: /\.(gif|png|webp|jpe?g|svg|xml|mp3|mp4)$/i,
        type: "asset/inline",
      },
      {
        generator: {
          dataUrl: {
            encoding: "base64",
            mimetype: "mimetype/png8",
          },
        },
        test: /\.(png8)$/i,
        type: "asset/inline",
      },
    ],
  },
};

Plugin

빌드시 기존 파일을 제거하고 새로운 파일을 생성해주는 역할의 clean-webpack-plugin은 webpack config의 output속성에 clean:true 를 지정해주는 것으로 대체 가능하다. 또한 빌드파일 minimize를 위해 사용하던 terser-webpack-plugin의 경우에도 webpack v5에는 default로 장착되어있으니 제거해도 좋다. 결과적으로 Plugin Config는 다음과 같다.

const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = {
  mode: "development",
  plugins: [
    new webpack.DefinePlugin({
      CANVAS_RENDERER: JSON.stringify(true),
      WEBGL_RENDERER: JSON.stringify(true),
    }),
    new HtmlWebpackPlugin({
      template: "./index.html",
    }),
    new webpack.EnvironmentPlugin({ DEBUG: "false", DEV: "false" }),
  ],
  output: {
    clean: true,
  },
};

마무리

이렇게 마이그레이션을 마치고 번들사이즈가 극적으로 줄어들었다면 더욱 의미있었겠지만, 이미 파일 사이즈가 작다보니 그렇지는 않았다. 하지만 마이그레이션을 하다보니 현재 프로젝트에서 사용하지 않는 패키지들도 install된 상태였다는 것을 알게되어 정리할 수 있었다. 또한 config가 간단해졌고, devDependency에 있던 여러 패키지들을 제거할 수 있어서 프로젝트의 의존성과 webpack 구성을 한눈에 알아볼 수 있게 되었다는 장점이 있었다.

언제나 최신버전이 정답은 아니지만 충분한 고민을 통해 좋은 결과를 낼 수 있어 의미있는 작업이었다!

출처

0개의 댓글