개발자
류준열

마이크로 프론트엔드끼리의 빌드된 타입 정보 공유

module federation을 통해 remote App을 container App에서 import 하면 타입이 공유되지 않는 현상이 발생힌다.

마이크로 프론트엔드간의 타입공유

@module-federation/typescript를 이용하여 타입이 공유되지 않는 문제를 해결할 수 있다.

프로젝트 구조는 다음과 같다.

// component-app: Button.tsx가 있는 곳
// main-app: component-app의 Button.tsx를 사용하는 container app

ts-example/
│
├── apps/
│   ├── component-app/
│   │   ├── dist/
│   │   ├── node_modules/
│   │   ├── src/
│   │   │   ├── components/
│   │   │   │   └── Button.tsx
│   │   │   ├── App.tsx
│   │   │   ├── index.css
│   │   │   ├── index.html
│   │   │   └── index.ts
│   │   ├── .babelrc
│   │   ├── .gitignore
│   │   ├── compilation.config.js
│   │   ├── package.json
│   │   ├── tsconfig.json
│   │   └── webpack.config.js
│   │
│   ├── main-app/
│   │   ├── node_modules/
│   │   ├── src/
│   │   │   ├── App.tsx
│   │   │   ├── declaration.d.ts
│   │   │   ├── index.css
│   │   │   ├── index.html
│   │   │   └── index.ts
│   │   ├── .babelrc
│   │   ├── .gitignore
│   │   ├── compilation.config.js
│   │   ├── package.json
│   │   ├── tsconfig.json
│   │   └── webpack.config.js
│
├── node_modules/
├── package.json
├── pnpm-lock.yaml
└── pnpm-workspace.yaml

두 앱의 Module federation 세팅 과정은 생략한다.

모노레포가 아닐 때

component-app에서 expose할 타입폴더 만들기

component-app에서 패키지를 설치해준다.

pnpm add @module-federation/typescript
webpack.config.js 수정

기존 /component-app/webpack.config.js에서 주석친 부분만 추가한다.

const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");
// 추가
const { FederatedTypesPlugin } = require("@module-federation/typescript");
//

const federationConfig = {
  name: "component_app",
  filename: "remoteEntry.js",
  remotes: {},
  exposes: {
    "./Button": "./src/components/Button",
  },
  shared: {
    ...deps,
    react: {
      singleton: true,
      requiredVersion: deps.react,
    },
    "react-dom": {
      singleton: true,
      requiredVersion: deps["react-dom"],
    },
  },
};

module.exports = (_, argv) => ({
  ...
  plugins: [
     new ModuleFederationPlugin(federationConfig),
     new FederatedTypesPlugin({ federationConfig }), // 추가
     new HtmlWebPackPlugin({
       template: "./src/index.html",
     }),
   ],
  ...
})
빌드 후 생성되는 타입 폴더 확인

위와 같이 webpack.config.js를 수정하고 빌드하면 다음과 같이 dist 내에 @mf-types가 생성된다. 빌드 후 생성된 @mf-types

빌드된 파일을 localhost:3001에 띄우고 /@mf-types에 접근했을때도 폴더를 확인할 수 있다. localhost:3001/@mf-types

main-app에서 component-app의 타입 사용하기

main-app에서 패키지를 설치해준다.

pnpm add @module-federation/typescript
webpack.config.js 수정

webpack.config.js에서 주석친 부분을 추가해준다.


const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");
// 추가
const { FederatedTypesPlugin } = require("@module-federation/typescript");
//


const federationConfig = {
  name: "main_app",
  filename: "remoteEntry.js",
  remotes: {
    component_app: "component_app@http://localhost:3001/remoteEntry.js",
  },
  exposes: {},
  shared: {
    ...deps,
    react: {
      singleton: true,
      requiredVersion: deps.react,
    },
    "react-dom": {
      singleton: true,
      requiredVersion: deps["react-dom"],
    },
  },
};

module.exports = (_, argv) => ({
	...
	plugins: [
    new ModuleFederationPlugin(federationConfig),
    new FederatedTypesPlugin({ federationConfig }), // 추가
    new HtmlWebPackPlugin({
      template: "./src/index.html",
    }),
  ],
})
component-app에서 빌드한 타입 파일이 main-app 내부에 생성되는 것 확인

이렇게 main-app의 webpack.config.js를 변경하고 빌드된 component-app을 localhost:3001에 켜놓은 상태로 main-app을 실행하면 main-app 내부에 폴더가 하나 생성되는데 자세히 살펴보면 component-app에서 빌드된 @mf-types 폴더와 동일하다.

빌드 후 생성된 @mf-types

main-app 안에 생성된 폴더

빌드된 타입 정보를 두 마이크로 프론트간에 공유할 수 있는 것이다.

tsconfig.json에서 path 설정

마지막으로 tsconfig.json에서 path를 설정해준다. main-app 내부에 생성된 @mf-types의 경로를 지정해주면 된다.

{
  "paths": {
    "*": ["./@mf-types/*"]
  }
}

타입에러가 사라진것을 확인 할 수 있다.

모노레포일때

모노레포일때는 그냥 main-app의 tsconfig.json에서 path를 다음과 같이 설정해주면 된다.

{
  "paths": {
    "component_app/Button": ["../component-app/src/components/Button"]
  }
}