Webpack 5.106

Webpack 5.106에서는 플러그인 유효성 검사 후크, CSS Modules용 내장 런타임 스타일 삽입, CommonJS 구조 분해 할당을 위한 더욱 효율적인 트리 셰이킹, WebAssembly 모듈의 source phase import, 그리고 JavaScript 파싱 속도 향상을 위한 oxc-parser와의 실험적 통합 기능이 도입되었습니다.

이번 릴리스의 새로운 기능을 살펴보세요:

Plugin Validation with compiler.hooks.validate

Webpack은 최상위 validate 옵션과 compiler.hooks.validate hook을 추가하여 webpack 설정, 플러그인 및 로더 전반에 걸쳐 스키마 유효성 검사 방식을 표준화했습니다.

지금까지는 플러그인이 스키마 유효성 검사를 Webpack 빌드 라이프사이클에 통합하는 통일된 방법이 없었습니다. 각 플러그인이 자체적으로 유효성 검사를 처리했습니다. 새로운 compiler.hooks.validate는 이러한 문제를 해결합니다. 이 hook은 플러그인 작성자가 자신의 유효성 검사 로직을 등록할 수 있는 표준 API와, 이를 실행하기 위한 compiler.validate(...)를 제공합니다. 즉, webpack의 core config부터 이 hook을 채택한 모든 플러그인의 유효성 검사까지 하나의 validate 플래그로 제어되며, 동일한 패턴을 따르게 됩니다.

module.exports = {
  // 스키마 유효성 검사를 비활성화합니다(webpack 설정, 플러그인 및 로더).
  validate: false,
};

기본값은 빌드 모드에 따라 다릅니다.

모드experiments.futureDefaultsvalidate 기본값
developmentfalsetrue
developmenttruetrue
productionfalsetrue
productiontruefalse

플러그인 개발자에게 유효성 검사 통합은 간단합니다. compiler.hooks.validate에 탭을 등록하기만 하면 webpack이 나머지를 처리해 줍니다. 사용자가 validate: false로 설정하면 유효성 검사를 완전히 건너뛰는 것도 포함됩니다.

class MyPlugin {
  constructor(options = {}) {
    this.options = options;
  }

  apply(compiler) {
    compiler.hooks.validate.tap("MyPlugin", () => {
      compiler.validate(
        () => require("./schema/MyPlugin.json"),
        this.options,
        { name: "My Plugin", baseDataPath: "options" },
        (options) => require("./schema/MyPlugin.check")(options),
      );
    });

    // ...여기서는 일반적인 플러그인 로직이 적용됩니다...
  }
}

module.exports = MyPlugin;

validatetrue일 때 문제가 발생하면 webpack은 컴파일 시점에 명확한 오류를 발생시킵니다.

validatefalse이면 해당 검사는 완전히 생략됩니다. 하지만 빌드 과정에서 나중에 덜 명확한 오류가 발생할 수 있으므로 이 옵션은 주의해서 사용해야 합니다.

CSS Modules with Runtime Style Injection

Webpack은 이제 CSS 모듈에 대해 exportType: "style"을 지원합니다(experiments.css: true가 활성화된 경우). 이를 통해 CSS를 <style>(HTMLStyleElement)로 Webpack 런타임에서 직접 DOM에 삽입할 수 있습니다. 이는 style-loader의 일반적인 사용 사례를 포함하므로, 이 모드를 사용하면 더 이상 style-loader를 사용하여 스타일을 삽입할 필요가 없습니다.

또한, CSS 모듈 내보내기 정보(예: *.module.css의 클래스 이름 매핑)는 그대로 유지됩니다.

CSP 호환성을 위해 Webpack 런타임(__webpack_require__.nc)에 nonce가 구성된 경우, 이 모드에서 삽입되는 <style>nonce 속성을 통해 동일한 nonce를 받습니다(Webpack은 애플리케이션에서 제공한 nonce를 재사용하며, 자동으로 생성하지 않습니다).

module.exports = {
  experiments: { css: true },
  module: {
    rules: [
      {
        test: /\.css$/,
        type: "css/module",
        parser: {
          exportType: "style",
        },
      },
    ],
  },
};

Better Tree Shaking for CommonJS Destructuring

이제 Webpack은 CommonJS의 require(및 module.require)에서 직접 사용되는 구조 분해 할당을 정적으로 분석하고, exports 객체 전체를 사용하는 것으로 보수적으로 가정하는 대신 구조 분해된 속성만 "참조된 내보내기"로 처리할 수 있습니다. 이는 최적화된 빌드에서 데드 코드 제거를 개선하고, CommonJS 모듈을 사용하는 코드베이스의 번들 크기를 줄일 수 있습니다.

여러 함수를 export하는 모듈이 있고, 그중 하나만 구조 분해해서 가져오는 사용처가 있다고 생각해 보세요.

// math.js
exports.add = (a, b) => a + b;
exports.divide = (a, b) => a / b;
exports.multiply = (a, b) => a * b;
exports.subtract = (a, b) => a - b;
// app.js
const { add } = require("./math");

console.log(add(2, 3));

이전 버전에서 webpack은 require("./math")를 전체 exports 객체를 참조하는 것으로 처리했습니다. add 함수만 사용되었음에도 불구하고 네 가지 함수가 모두 번들에 포함되었습니다.

// 번들 출력(5.105 — 간소화됨)
const math = {
  add: (a, b) => a + b,
  subtract: (a, b) => a - b,
  multiply: (a, b) => a * b,
  divide: (a, b) => a / b,
};

Webpack은 5.106 버전부터 구조 분해 할당 패턴을 인식하고 add만 참조된 것으로 표시합니다. 사용되지 않는 내보내기는 최적화 과정에서 제거됩니다.

// 번들 출력(5.106 — 간소화됨)
const math_add = (a, b) => a + b;
// subtract, multiply, divide는 트리쉐이킹됩니다.

이 방법은 module.require와 함께 사용해도 작동합니다.

const { a, b } = module.require("./module");

Source Phase Imports for WebAssembly (Experimental)

Webpack은 이제 WebAssembly 모듈을 가져올 때 TC39 Source Phase Imports에 대한 실험적 지원을 포함합니다.

이 제안은 현재 3단계에 있으며, 모듈을 즉시 평가하는 대신 source phase에서 가져오는 방법을 도입합니다. WebAssembly의 경우, 이는 컴파일된 WebAssembly.Module을 먼저 얻은 다음 나중에 사용자 정의 가져오기를 통해 인스턴스를 생성할 수 있음을 의미합니다.

webpack 5.106에서는 experiments.sourceImport를 통해 WebAssembly의 source import에 대한 실험적 지원이 제공됩니다. 다음 마이너 릴리스에서는 JavaScript 지원으로 초점이 옮겨질 예정입니다.

experiments.sourceImport를 사용하여 이 기능을 활성화하세요.

module.exports = {
  experiments: {
    asyncWebAssembly: true,
    sourceImport: true,
  },
};

그런 다음 정적 또는 동적 source phase 구문을 사용하세요.

// 정적 형태
import source wasmModule from "./module.wasm";

// 동적 형태
const wasmModule2 = await import.source("./module.wasm");

const instance = await WebAssembly.instantiate(wasmModule);

Webpack 저장소에서 전체 예제를 확인할 수도 있습니다. https://github.com/webpack/webpack/tree/main/examples/wasm-simple-source-phase

Getting Started with create-webpack-app

create-webpack-app은 이제 새로운 webpack 프로젝트를 생성하는 데 권장되는 방법입니다. 이전에는 이 기능이 webpack-cli 내의 webpack init 명령으로 제공되었지만, webpack-cli 7 버전 출시와 함께 별도의 독립형 패키지로 분리되었습니다.

즉, 이제 webpack-cli를 먼저 설치할 필요 없이 단 하나의 명령으로 새로운 webpack 프로젝트를 생성할 수 있습니다.

npx create-webpack-app

CLI는 사용자가 프로젝트에 맞는 구성 요소를 선택할 수 있도록 대화형 설정 과정을 안내합니다.

$ npx create-webpack-app

? Which of the following JS solutions do you want to use? Typescript
? Do you want to use webpack-dev-server? Yes
? Do you want to simplify the creation of HTML files for your bundle? Yes
? Do you want to add PWA support? No
? Which of the following CSS solutions do you want to use? CSS only
? Will you be using PostCSS in your project? Yes
? Do you want to extract CSS for every file? Only for Production
? Which package manager do you want to use? npm

[create-webpack] ℹ️  Initializing a new Webpack project...
[create-webpack] ✅ Project dependencies installed successfully!

생성된 프로젝트에는 정상적으로 작동하는 webpack.config.js 파일, 개발 서버 구성, 그리고 선택한 옵션에 대한 로더/플러그인 설정이 포함되어 있습니다. 따라서 바로 개발을 시작할 수 있습니다.

cd my-project
npm start

이 접근 방식은 create-react-app이나 create-vite와 같은 도구에서 널리 사용되는 방식과 동일합니다. 즉, 수동 설정 없이 단 하나의 npx 명령으로 처음부터 작동하는 프로젝트를 생성할 수 있습니다.

명시적인 방식을 선호하는 경우 init 하위 명령을 사용할 수도 있습니다.

npx create-webpack-app init

create-webpack-app은 프로젝트 기본 구조 생성 외에도 사용자 정의 로더 및 플러그인에 필요한 기본 코드를 생성할 수 있습니다.

npx create-webpack-app loader

npx create-webpack-app plugin

Context Support for VirtualUrlPlugin

VirtualUrlPlugin(webpack.experiments.schemes.VirtualUrlPlugin을 통해)에서 이제 가상 모듈 내의 상대 경로 가져오기를 해석하는 데 사용되는 기본 디렉터리를 정의하는 context 옵션을 지원합니다. 이 기능은 experiments.schemes API의 일부이므로 현재 실험적입니다.

이를 통해 가상 모듈은 실제 파일처럼 동작하게 됩니다. 예를 들어 import "./utils"와 같은 코드는 compiler.context에 의존하여 잘못된 해석이 발생하는 대신, 일관되게 해석됩니다.

context는 가상 모듈별로(모듈 정의 내에서) 설정하거나 플러그인 수준의 기본값으로 설정할 수 있습니다. 기본값은 auto이며, 이 경우 가상 모듈 ID 또는 경로에서 컨텍스트를 추론하려고 시도합니다. 그렇지 않으면 compiler.context를 사용합니다. 개념적으로 모듈에 context를 설정하면 webpack은 상대 경로 해석 시 해당 가상 모듈을 마치 지정된 디렉터리에 있는 것처럼 처리합니다.

예를 들어, context: path.join(__dirname, "src/components")를 사용하여 virtual/table.js라는 가상 모듈 ID를 정의하면, 해당 모듈 내부의 import "./utils"는 마치 파일이 src/components/table.js이고 src/components/utils.js를 가져오는 것처럼 해석됩니다.

const path = require("node:path");
const webpack = require("webpack");

module.exports = {
  plugins: [
    new webpack.experiments.schemes.VirtualUrlPlugin(
      {
        "src/components/button.js": {
          context: "auto",
          source() {
            return "import { trim } from './utils'; export const button = trim('button ');";
          },
        },
        "virtual/table.js": {
          context: path.join(__dirname, "src/components"),
          source() {
            return "import { trim } from './utils'; export const table = trim('table ');";
          },
        },
      },
      { context: "auto" },
    ),
  ],
};

Experimental JavaScript Parsing with oxc-parser

Webpack에 기본 JavaScript 파서를 oxc-parser 로 교체하는 방법을 보여주는 예제가 포함되었습니다. 이 통합은 순전히 실험적인 기능이며 프로덕션 환경에서의 사용은 권장하지 않습니다.

대신, 개발 환경이나 벤치마크 브랜치에서 사용하도록 고안되었으며, 커뮤니티에서 실제 프로젝트에서 대체 파싱 전략을 실험해 볼 수 있도록 합니다. 이를 통해 파싱 시간 및 빌드 성능 개선 가능성을 평가하고 호환성 문제를 식별할 수 있습니다.

예시

다음 구성은 custom parser.js 파일에만 적용되도록 제한합니다.

"use strict";

const oxcParse = require("./internals/oxc-parse");

/** @type {import("webpack").Configuration} */
module.exports = {
  mode: "production",
  entry: "./src/index.js",
  module: {
    rules: [
      {
        // 사용자 지정 파서를 JavaScript 파일에만 적용합니다.
        test: /\.js$/,
        parser: {
          parse: oxcParse,
        },
      },
    ],
  },
};

전체 예제는 webpack 저장소에서 확인할 수 있습니다. https://github.com/webpack/webpack/blob/main/examples/custom-javascript-parser/webpack.config.js

Ecosystem Updates

  • Webpack-cli가 새로운 메이저 버전인 7.0.0을 출시했습니다. 이제 지원되는 최소 Node.js 버전은 20.9.0이며, 설정 파일은 기본적으로 동적 import()를 통해 로드됩니다. 이를 통해 외부 로더 없이도 Node.js 타입 스트리핑을 통해 네이티브 TypeScript 설정 지원이 가능해졌습니다. --node-env 인수는 --config-node-env로 대체되었으며, 더 이상 사용되지 않는 프로그래밍 방식 API가 제거되었습니다. 또한, 설정 동결이 허용되고, 파일 시스템 캐시가 활성화된 경우 정상적인 종료가 개선되었으며, 전반적인 성능이 향상되었습니다. 자세한 내용은 릴리스을 참고하세요.
  • Webpack-dev-middleware가 새로운 메이저 버전인 8.0.0을 출시했습니다. 이제 지원되는 최소 Node.js 버전은 20.9.0이고 최소 webpack 버전은 5.101.0입니다. getFilenameFromUrl 함수는 이제 비동기식으로 작동하며, 변경 불가능한 자산 캐싱(cacheImmutable)이 기본적으로 활성화되었고, 새로운 forwardError 옵션을 통해 오류를 다음 미들웨어로 전달할 수 있습니다. 플러그인 사용 지원이 추가되었으며, 전반적인 성능이 향상되었습니다. 자세한 내용은 릴리스을 참고하세요.
  • Compression-webpack-plugin, html-minimizer-webpack-plugin, css-minimizer-webpack-plugin, image-minimizer-webpack-plugin 및 기타 플러그인들이 최소 지원 Node.js 버전을 20.9.0으로 맞추기 위해 새로운 주요 버전을 출시했습니다. 이는 최근 출시된 webpack-cli 7 및 webpack-dev-middleware 8의 주요 버전과 함께 webpack 생태계 전반의 일관성을 유지하기 위한 것입니다.

Bug Fixes

버전 5.105 이후로 여러 버그가 수정되었습니다. 자세한 내용은 changelog를 참고하세요.

Thanks

기여해주신 모든 분들과 스폰서께 진심으로 감사드립니다. Webpack 5.106을 가능하게 해주신 모든 분들께 감사드립니다. 코드 기여, 문서 작성, 재정적 후원 등 여러분의 지원은 Webpack이 모두를 위해 지속적으로 발전하고 개선되는 데 큰 도움이 됩니다.

Edit this page·
« Previous
Blog

1 Contributor

bjohansebas