위 레포지토리는 vite + react 기반 module federation 예제입니다. yarnberry monorepo
, module federation
구축 가이드와 이슈 해결법을 기록하였습니다.
- Yarn Berry 기반의 모노레포를 생성
yarn init -2
- 루트 경로의
package.json
workspaces 셋팅
{
"name": "module-federation-example-monorepo",
"packageManager": "[email protected]",
"workspaces": ["packages/*"]
}
- 프로젝트 생성
mkdir packages
cd packages
# 프로젝트 생성 (본 레포에서는 vite-react-typescript 기반 원격, 호스트 두가지 생성)
yarn create vite --template react-ts
- 의존성 설치
# root
yarn install
- Project간의 설정 공유
// tsconfig.base.json
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
},
"include": ["src"],
"references": [{ "path": "./tsconfig.node.json" }]
}
// packages/*/tsconfig.json
{
"extends": "../../tsconfig.base.json",
"include": ["./src"]
}
- scripts 셋팅
// packages/*/package.json
"name": "@monorepo/remote",
"scripts": {
"dev": "vite --port 5173 --strictPort",
"build": "tsc && vite build",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview --port 5173 --strictPort"
},
// ./package.json
"scripts": {
"dev:remote": "yarn workspace @monorepo/remote dev",
"build:remote": "yarn workspace @monorepo/remote build",
"deploy:remote": "yarn workspace @monorepo/remote deploy",
"dev:host": "yarn workspace @monorepo/host dev",
"build:host": "yarn workspace @monorepo/host build"
},
yarn berry는 npm과 모듈을 불러오는 방식이 다르기 때문에 typescript 오류가 발생함
# 루트 경로
yarn add -D typescript prettier eslint
# vscode 사용시 sdk 설치 (위 커맨드 이후)
yarn dlx @yarnpkg/sdks vscode
Module Federation은 Webpack5에서 추가된 기능이다. 여러 개의 애플리케이션을 따로 빌드한 다음 런타임에 통합하여 하나의 애플리케이션으로 동작하게 한다는 개념이다. 마이크로 프런트엔드의 빌드타임 통합방식은 모노레포를 사용함으로써 가능했지만 결국 여러 패키지를 한 번에 같이 빌드해야 했던 한계가 있었다. 이제는 Module Federation이 등장함으로써 보다 자연스럽게 런타임에 통합할 수 있다.
Module federation은 Webpack5에서 나온 기능이지만 본 레포에서는 vite에서 이 기능을 사용할 수 있도록 구현된 vite-plugin-federation 플러그인을 사용합니다.
- remote: 원격 앱, 모듈을 내보는 역할
- host: 모듈을 받아서 사용하는 역할
- remote앱의 원격 모듈 정의
리모트 앱을 빌드하면 remoteEntry.js라는 파일이 생성되며 Expose한 원격 모듈을 호스트 앱에서 로딩할 수 있도록 인터페이스를 정의
// packages/remote/vite.config.ts
export default defineConfig({
plugins: [
react(),
federation({
name: "remote-app",
filename: "remoteEntry.js",
// Modules to expose
exposes: {
"./RemotedComponent": "./src/components/RemotedComponent",
},
shared: ["react", "react-dom"],
}),
],
});
-
Build & Preview 빌드 후 빌드 파일을 실행하여 링크에 액세스하면 매니페스트 파일이 표시
-
host앱에서 원격 모듈에 접근
// packages/host/vite.config.ts
export default defineConfig({
plugins: [
react(),
federation({
name: "host-app",
remotes: {
remoteApp: "http://localhost:5175/assets/remoteEntry.js",
},
shared: ["react", "react-dom"],
}),
],
});
- 원격 모듈 사용
import reactLogo from "./assets/react.svg";
const RemotedComponent = React.lazy(() => import("remoteApp/RemotedComponent"));
function App() {
return (
<>
<h1>Host app</h1>
<RemotedComponent imgSrc={reactLogo} />
</>
);
}
export default App;
모듈 내보내기 하는 remote app 빌드시 에러 발생 -> vite.config.ts에 build 설정 추가
export default defineConfig({
optimizeDeps: {
esbuildOptions: {
target: "esnext",
},
},
build: {
target: "esnext",
},
// ...
});
TypeScript가 모듈 페더레이션을 통해 불러오는 'remoteApp/ReactLogo' 모듈의 타입 선언을 찾을 수 없다는 것을 의미, 모듈 페더레이션을 통해 불러오는 외부 모듈을 선언해야 한다.
// src/vite-env.d.ts
declare module "remoteApp/*";