ModuleFederationPlugin

ModuleFederationPlugin을 사용하면 런타임에 다른 독립 빌드와 함께 모듈을 제공하거나 사용할 수 있습니다.

import webpack from "webpack";

const { ModuleFederationPlugin } = webpack.container;

export default {
  plugins: [
    new ModuleFederationPlugin({
      // 여러 옵션을 타입스크립트로 작성합니다.
      runtime: string | false,
    }),
  ],
};

Options

name

string

이 빌드가 생성하는 컨테이너의 이름입니다. 기본 script 리모트 타입에서는 컨테이너가 할당되는 전역 변수이기도 하며, remoteEntry.js가 로드된 후 소비자가 컨테이너에 접근하는 방법입니다.

페이지의 빌드들 사이에서 고유해야 합니다. 이것은 output.uniqueName과는 별개이며, 그것도 고유해야 합니다 — 모든 빌드에는 고유한 이름이 필요합니다를 참고하세요.

filename

string

output.path에 상대적인 경로로 나타낸 컨테이너 엔트리의 파일 이름입니다. 관례적으로 remoteEntry.js를 사용합니다.

이 빌드가 방출하는 파일의 이름입니다. 소비하는 리모트들의 파일 이름과는 무관합니다 — 그것들은 각 리모트가 선택한 것으로 remotes 아래의 URL에 나타납니다.

remotes

string[] object

이 빌드가 소비하는 컨테이너들입니다. 각 항목은 요청 스코프 — import할 때 사용하는 이름 — 를 컨테이너 위치에 매핑합니다.

new ModuleFederationPlugin({
  remotes: {
    // request scope        container location
    app1: "app1@http://localhost:3001/remoteEntry.js",
  },
});

이후 다음과 같이 import합니다.

import Button from "app1/Button";

위치 문자열의 두 부분은 첫 번째 @를 기준으로 분리됩니다.

부분의미
@ 앞 — app1리모트의 name, 즉 컨테이너가 할당된 전역 변수
@ 뒤 — http://localhost:3001/remoteEntry.jsfilename이 제공되는 URL

따라서 설정에 이름이 "두 번" 나타나는 경우, 보통은 우연히 일치하는 두 가지 다른 것입니다. 키는 여러분이 import할 때 사용하는 것으로 자유롭게 변경할 수 있지만, @ 앞 부분은 리모트가 자신에게 붙인 이름과 일치해야 합니다. 둘은 달라도 됩니다.

new ModuleFederationPlugin({
  remotes: {
    // imported as "checkout/…", served by a container that calls itself "shop"
    checkout: "shop@http://localhost:3001/remoteEntry.js",
  },
});

객체 대신 배열을 전달하면 키 없이 webpack이 각 요청 스코프를 위치에서 추론합니다.

정적으로 설정된 리모트는 직접 아무것도 로드하지 않아도 됩니다. webpack이 <script>를 주입하는 코드를 방출하고 전역 변수가 나타날 때까지 기다리다가, 나타나지 않으면 ScriptExternalLoadError를 보고합니다. 컨테이너를 수동으로 로드하는 것은 빌드 시 위치를 알 수 없는 동적 리모트에서만 필요합니다.

항목은 external(위치)과 shareScope를 가진 객체일 수도 있으며, 하나의 리모트가 나머지와 다른 공유 스코프가 필요한 경우에 사용합니다.

remoteType

string

externalsType으로 리모트를 가져오는 방법입니다.

설정하지 않으면 이 플러그인 자체의 library.type으로 폴백하지만, 그것이 유효한 externals 타입인 경우에만 그렇고, 그렇지 않으면 위에서 설명한 name@url 형식인 'script'로 폴백합니다. library 자체는 기본값이 { type: 'var', name }이므로, 둘 다 설정하지 않은 설정은 'script'를 사용합니다.

runtime

특정한 이름으로 새로운 런타임 청크를 만듭니다.

webpack.config.js

import webpack from "webpack";

const { ModuleFederationPlugin } = webpack.container;

export default {
  plugins: [
    new ModuleFederationPlugin({
      runtime: "my-runtime-name",
    }),
  ],
};

exposes

이 컨테이너가 다른 빌드에 제공하는 모듈들입니다. 속성 이름은 소비자가 import하는 공개 이름(import Button from "remote/Button")이고, 값은 노출할 모듈입니다.

webpack.config.js

new ModuleFederationPlugin({
  name: "remote",
  exposes: {
    "./Button": "./src/components/Button",
  },
});

항목은 객체일 수도 있습니다.

  • import (string): 노출할 모듈입니다.
  • name (string): webpack이 생성하는 청크의 이름입니다. 지정하지 않으면 모듈의 내부 ID를 기반으로 청크 이름이 지정되는데, 빌드가 변경됨에 따라 달라집니다. 설정하면 파일에 정적으로 참조할 수 있는 안정적인 이름을 부여합니다.
new ModuleFederationPlugin({
  name: "remote",
  exposes: {
    "./Button": {
      import: "./src/components/Button",
      name: "button-chunk",
    },
  },
});

Sharing libraries

설정의 'shared' 키를 사용하면 모듈 연합 사이에 공유되는 라이브러리를 정의할 수 있습니다. 패키지 이름은 package.json의 'dependencies' 섹션에 있는 이름과 동일합니다. 그러나 기본적으로 webpack은 라이브러리의 루트 레벨만 공유합니다.

import webpack from "webpack";

const { ModuleFederationPlugin } = webpack.container;

export default {
  plugins: [
    new ModuleFederationPlugin({
      // date-fns를 공유 모듈로 추가합니다.
      shared: ["date-fns"],
    }),
  ],
};

따라서 응용 프로그램에서 다음과 같은 작업을 수행할 수 있습니다.

import { format } from "date-fns";

format(new Date(2014, 1, 11), "MM/dd/yyyy");

그리고 webpack은 date-fns를 공유 라이브러리로 정의하는 모든 모듈 연합 사이에 date-fns를 자동으로 공유합니다. 그러나 패키지의 루트 레벨에 없는 항목(예: date-fns/locale/en-GB/index.js)에 액세스하려면 '공유' 설정에서 패키지 이름에 /를 추가해야 합니다.

import webpack from "webpack";

const { ModuleFederationPlugin } = webpack.container;

export default {
  plugins: [
    new ModuleFederationPlugin({
      // date-fns를 공유 모듈로 추가합니다.
      // 패키지의 모든 파일이 공유됩니다
      shared: ["date-fns/"],
    }),
  ],
};

The / 구문을 사용하면 패키지의 모든 파일에 액세스할 수 있습니다. 그러나 특히 개발 모드에서는 성능에 영향을 미치므로 필요한 경우에만 사용해야 합니다.

Specify package versions

공유 라이브러리의 버전을 지정하는 세 가지 방법이 있습니다.

Array syntax

이 구문을 사용하면 패키지 이름만 있는 라이브러리를 공유할 수 있습니다. 이 접근 방식은 프로토타이핑에 적합하지만 react 및 react-dom과 같은 라이브러리에 추가 요구 사항이 필요하기 때문에 대규모 프로덕션 환경으로 확장할 수 없습니다.

import webpack from "webpack";

const { ModuleFederationPlugin } = webpack.container;

export default {
  plugins: [
    new ModuleFederationPlugin({
      // lodash를 공유 모듈로 추가합니다.
      // version은 package.json으로부터 추론됩니다.
      // requiredVersion도 package.json으로부터 추론됩니다.
      // (결정할 수 없으면 경고가 발생합니다)
      // 만족하지 않는 공유 버전은 로컬 복사본으로 폴백됩니다.
      shared: ["lodash"],
    }),
  ],
};
Object syntax

이 구문을 사용하면 패키지 이름을 키로 정의하고 버전(semver)을 값으로 정의할 수 있는 각 공유 라이브러리를 보다 세부적으로 제어할 수 있습니다.

export default {
  plugins: [
    new ModuleFederationPlugin({
      shared: {
        // lodash를 공유 모듈로 추가합니다.
        // package.json 으로부터 버전을 추론합니다.
        // 4.17 이상 5 미만의 버전 중 최상위 버전을 사용하게 됩니다.
        lodash: "^4.17.0",
      },
    }),
  ],
};
Object syntax with sharing hints

이 구문을 사용하면 각 공유 패키지에 힌트를 추가로 제공할 수 있습니다. 패키지의 이름을 키로, 공유 동작을 수정하기 위한 힌트를 포함한 객체를 값으로 정의하여 제공할 수 있습니다.

import { readFileSync } from "node:fs";

const deps = JSON.parse(
  readFileSync(new URL("./package.json", import.meta.url)),
).dependencies;

export default {
  plugins: [
    new ModuleFederationPlugin({
      shared: {
        // react를 공유 모듈로 추가합니다.
        react: {
          requiredVersion: deps.react,
          singleton: true,
        },
      },
    }),
  ],
};

Sharing hints

eager

boolean

이 힌트를 사용하면 webpack이 비동기식 요청을 통해 라이브러리를 가져오는 대신 제공된 모듈이나 대체 모듈을 직접 포함할 수 있습니다. 즉, 초기 청크에서 이 공유 모듈을 사용할 수 있습니다. 또한 이 힌트가 활성화되면 제공된 모든 모듈과 대체 모듈이 항상 다운로드된다는 점에 주의해야 합니다.

import

false | string

제공된 모듈은 공유 유효범위에 배치되어야 합니다. 이 제공된 모듈은 공유 유효범위에 공유 모듈이 없거나 버전이 유효하지 않은 경우 대체 모듈 역할도 합니다. (이 힌트의 값은 기본적으로 프로퍼티 이름으로 설정됩니다.)

packageName

string

설명 파일에서 필요한 버전을 결정하는 데 사용되는 패키지 이름입니다. ‘packageName’은 요청으로부터 패키지 이름을 자동으로 결정할 수 없는 경우에만 필요합니다.

requiredVersion

false string

이 필드는 필수 패키지 버전을 지정합니다. "^1.2.3"과 같은 시멘틱 버전 관리를 허용합니다. 또한 버전이 URL로 제공되는 경우(예: "git+ssh://git@github.com:foo/bar.git#v1.0.0") 버전을 검색합니다.

webpack은 전체 범위 문법을 구현하므로, 필수 버전은 정확한 버전(1.2.3), 부분 버전(1.2, 모든 패치와 일치), 비교자(>=1.2.3 <2), 캐럿 또는 틸드 범위(^1.2.3, ~1.2.3), 하이픈 범위(1.2.3 - 2.3.4) 또는 이들을 ||로 결합한 것일 수 있습니다. 스코프가 제공하는 모든 것을 허용하려면 false로 설정하세요.

공유 스코프는 동일한 모듈의 여러 버전을 동시에 보유할 수 있습니다 — 각 빌드는 자신이 가진 버전을 제공하고, 소비자는 자신의 requiredVersion을 만족하는 가장 높은 버전을 얻습니다. 이것이 서로 다른 메이저 버전의 라이브러리가 필요한 두 빌드가 한 페이지에서 작동할 수 있는 이유이며, 두 버전 모두 전송하는 비용이 듭니다. singleton: true는 하나의 인스턴스를 유지하기 위해 그 보장을 포기하며, 선택된 버전이 소비자를 만족시키지 않을 때 경고를 발생시킵니다.

shareKey

string

요청된 공유 모듈은 공유 스코프에서 이 키로 조회됩니다. 기본값은 shared에서 사용한 키, 즉 요청 자체이므로 shared: ['lodash']는 lodash로 저장되고 조회됩니다.

빌드가 import하는 이름과 공유할 때 사용해야 하는 이름이 다를 때 설정하세요. 두 빌드는 키와 스코프가 모두 일치할 때만 모듈을 공유합니다. 따라서 lodash-es를 import하는 애플리케이션이 다른 빌드가 lodash로 제공한 것을 재사용할 수 있게 하는 것이 바로 이 키입니다.

new ModuleFederationPlugin({
  shared: {
    "lodash-es": {
      shareKey: "lodash",
    },
  },
});
shareScope

string

공유 스코프의 이름으로, 플러그인 자체의 shareScope 옵션이 기본값이며, 설정되지 않은 경우 'default'입니다. 스코프는 공유 모듈의 하나의 네임스페이스입니다. 빌드는 제공하는 모듈을 스코프에 넣고 필요한 모듈을 스코프에서 조회하므로, 다른 스코프 이름을 사용하는 빌드는 키가 일치하더라도 서로 공유하지 않습니다.

빌드 그룹을 분리하려면 변경하세요 — 예를 들어 페이지가 각자 라이브러리의 복사본을 가져야 하는 두 독립적인 리모트 세트를 호스팅할 때, 또는 위젯이 호스트의 버전을 채택해서는 안 될 때입니다. 제공하는 쪽과 소비하는 쪽 모두 동일한 스코프 이름을 사용해야 합니다.

singleton

boolean

‘singleton’은 공유 범위에서 단일 버전의 공유 모듈만 허용합니다.(기본적으로 비활성화됨) 일부 라이브러리는 전역 내부 상태를 사용합니다.(예: react, react-dom) 따라서 한 번에 하나의 라이브러리 인스턴스만 실행하는 것이 중요합니다.

공유 범위에 동일한 종속성을 갖는 여러 버전이 있는 경우, 가장 높은 시멘틱 버전이 사용됩니다.

strictVersion

boolean

‘strictVersion’을 사용하면 버전이 유효하지 않은 경우 webpack이 공유 모듈을 거부할 수 있습니다(로컬 fallback 모듈을 사용할 수 있고 공유 모듈이 singleton이 아닌 경우 기본값은 true로 설정되고, 그렇지 않으면 false로 설정되며 필수 버전이 지정되지 않은 경우 효과가 없습니다). 필요한 버전이 없으면 런타임 오류가 발생합니다.

version

false | string

제공된 모듈의 버전입니다. Webpack이 일치하는 낮은 버전을 대체할 수 있지만, 더 높은 버전은 대체할 수 없습니다.

Webpack은 기본적으로 디펜던시의 package.json 파일에 있는 버전을 사용합니다.

Additional examples

export default {
  plugins: [
    new ModuleFederationPlugin({
      // vue를 공유 모듈로 추가합니다.
      // package.json 으로부터 버전을 추론합니다.
      // 항상 공유 버전을 사용하지만 공유된 vue가 2.6.5 미만 또는 3 이상 버전인 경우 경고를 출력합니다.
      shared: {
        vue: {
          requiredVersion: "^2.6.5",
          singleton: true,
        },
      },
    }),
  ],
};
export default {
  plugins: [
    new ModuleFederationPlugin({
      // vue를 공유 모듈로 추가합니다.
      // 제공된 로컬 버전이 없습니다.
      // 공유된 vue가 2.6.5 미만 또는 3 이상 버전이면 경고를 내보냅니다.
      shared: {
        vue: {
          import: false,
          requiredVersion: "^2.6.5",
        },
      },
    }),
  ],
};
export default {
  plugins: [
    new ModuleFederationPlugin({
      // vue를 공유 모듈로 추가합니다.
      // 제공된 로컬 버전이 없습니다.
      // 공유된 vue가 2.6.5 미만 또는 3 이상 버전이면 오류가 발생합니다.
      shared: {
        vue: {
          import: false,
          requiredVersion: "^2.6.5",
          strictVersion: true,
        },
      },
    }),
  ],
};
export default {
  plugins: [
    new ModuleFederationPlugin({
      shared: {
        "my-vue": {
          // "my-vue" 가져오기로 참조할 수 있습니다.
          import: "vue", // "vue" 패키지는 제공된 대체 모듈로 사용됩니다.
          shareKey: "shared-vue", // "shared-vue"이름으로 공유된 모듈은 공유 유효범위에 배치됩니다.
          shareScope: "default", // "default"로 공유 유효범위가 적용됩니다.
          singleton: true, // 공유 모듈의 단일 버전만 허용됩니다.
          strictVersion: true, // 버전이 유효하지 않을 때 공유 버전을 사용하지 말아야 합니다. fallback이 없는 Singleton 또는 모듈은 넘겨집니다. 그렇지 않으면 fallback이 사용됩니다.
          version: "1.2.3", // 공유 모듈의 버전입니다.
          requiredVersion: "^1.0.0", // 공유 모듈의 최소 버전 입니다.
        },
      },
    }),
  ],
};

Further Reading

Edit this page·

5 Contributors

XiaofengXie16chenxsanburhanudaychristian24KhaledTaymour