ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 🧩 Rspack 기반 Module Federation 튜토리얼 (React 기반 MFA)
    React 2025. 6. 19. 22:23

    🎯 들어가며

    React로 Micro-Frontend architecture(MFA)를 구성할 때 Webpack 기반의 Module Federation은 꽤 강력한 옵션이다.
    그런데 최근 등장한 Rspack은 Webpack과 API가 유사하면서도 훨씬 빠른 빌드 성능을 자랑한다.
    이번 글에서는 Rspack 환경에서 Module Federation과 Hot Module Replacement(HMR) 를 적용하여 MFE 구조를 만드는 방법을 간단하게 기록해봤다.

    • Rspack으로 Module Federation 구성하기
    • React 앱 간 모듈 공유 (Remote ↔ Host)
    • HMR이 잘 적용되는 구조로 설정하기

    공식 문서와 module-federation-examples/rspack_hmr 예제를 바탕으로 예제를 만들었다.

     

    📁 프로젝트 구조 미리보기

    rspack-mf-hmr/
    ├── host/            # Module을 소비하는 앱
    │   ├── src/
    │   │   ├── App.tsx
    │   │   ├── bootstrap.tsx   👈 React 마운트 분리
    │   │   └── index.tsx       👈 bootstrap 로드 전용
    │   └── rspack.config.js
    ├── remote/          # Module을 제공하는 앱
    │   ├── src/
    │   │   ├── Button.tsx
    │   │   └── index.ts
    │   └── rspack.config.js
     

    ⚙️ 1. 기본 설정

    먼저 Host/Remote 각각의 앱을 셋업한다.

    # 루트 디렉토리 생성
    mkdir rspack-mf-hmr && cd rspack-mf-hmr
    
    # Host 앱 초기화
    mkdir host && cd host
    npm init -y
    npm install react react-dom
    npm install --save-dev @rspack/core @rspack/cli swc-loader html-webpack-plugin
    
    # Remote 앱 초기화
    cd ..
    mkdir remote && cd remote
    npm init -y
    npm install react react-dom
    npm install --save-dev @rspack/core @rspack/cli swc-loader
     

    🧱 2. Remote 앱 구성 (버튼 제공)

    remote/src/Button.tsx

    import React from "react";
    
    const Button = () => {
      return <button style={{ padding: 10 }}>Remote Button</button>;
    };
    
    export default Button;

     

    👆 아주 단순한 버튼 컴포넌트다. 이걸 외부에서 소비할 수 있도록 만들어보자.

    remote/src/index.ts

    export { default as Button } from "./Button";

    👆 기본적으로 외부에서 import("remote/Button")으로 접근할 수 있도록 index에서 export 해주자.

    remote/rspack.config.js

    const path = require("path");
    const { ModuleFederationPlugin } = require("@rspack/core").container;
    
    module.exports = {
      entry: "./src/index.ts",
      output: {
        path: path.resolve(__dirname, "dist"),
        filename: "bundle.js",
        publicPath: "http://localhost:3001/",
        uniqueName: "remote",
      },
      mode: "development",
      devServer: {
        port: 3001,
        hot: true,
      },
      plugins: [
        new ModuleFederationPlugin({
          name: "remote",
          filename: "remoteEntry.js",
          exposes: {
            "./Button": "./src/Button.tsx",
          },
          shared: {
            react: { singleton: true },
            "react-dom": { singleton: true },
          },
        }),
      ],
      resolve: { extensions: [".tsx", ".ts", ".js"] },
      module: {
        rules: [
          {
            test: /\.tsx?$/,
            use: "swc-loader",
          },
        ],
      },
    };

    📌 React와 ReactDOM은 싱글톤으로 공유해야 충돌 없이 동작한다.

     

    🏠 3. Host 앱 구성 (버튼 소비)

    host/public/index.html

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <title>Host App</title>
      </head>
      <body>
        <div id="root"></div>
      </body>
    </html>

    host/src/App.tsx

    import React from "react";
    const RemoteButton = React.lazy(() => import("remote/Button"));
    
    const App = () => (
      <React.Suspense fallback="Loading...">
        <h1>Host App</h1>
        <RemoteButton />
      </React.Suspense>
    );
    
    export default App;

    👆 Remote에서 Button 컴포넌트를 lazy load하고, suspense로 fallback UI까지 지정해주자.

    host/src/bootstrap.tsx

    import React from "react";
    import { createRoot } from "react-dom/client";
    import App from "./App";
    
    const root = createRoot(document.getElementById("root")!);
    root.render(<App />);

    👆 React 18 이상에서의 마운팅 방식이다. Module Federation과 혼용 시 충돌 방지를 위해 분리한다.

    host/src/index.tsx

    // React 18 + Module Federation 호환을 위한 분리된 부트스트랩
    import("./bootstrap");

    👆 bootstrap.tsx를 dynamic import하면, 모듈 페더레이션의 런타임과 React의 마운트 순서를 조정할 수 있다.

    host/rspack.config.js

    const path = require("path");
    const { ModuleFederationPlugin } = require("@rspack/core").container;
    const HtmlWebpackPlugin = require("html-webpack-plugin");
    
    module.exports = {
      entry: "./src/index.tsx",
      output: {
        path: path.resolve(__dirname, "dist"),
        filename: "bundle.js",
        publicPath: "http://localhost:3000/",
        uniqueName: "host",
      },
      mode: "development",
      devServer: {
        port: 3000,
        hot: true,
      },
      plugins: [
        new ModuleFederationPlugin({
          name: "host",
          remotes: {
            remote: "remote@http://localhost:3001/remoteEntry.js",
          },
          shared: {
            react: { singleton: true },
            "react-dom": { singleton: true },
          },
        }),
        new HtmlWebpackPlugin({ template: "./public/index.html" }),
      ],
      resolve: { extensions: [".tsx", ".ts", ".js"] },
      module: {
        rules: [
          {
            test: /\.tsx?$/,
            use: "swc-loader",
          },
        ],
      },
    };
     
     

    🚀 실행 & 확인하기

    # Remote 앱 실행
    cd remote
    npx rspack dev
    
    # Host 앱 실행
    cd ../host
    npx rspack dev

    ✅ HMR 동작 확인

    remote/src/Button.tsx 파일을 수정한 뒤 저장하면, Host 앱에서도 자동으로 반영되는지 확인해보자.

     

    🤔 bootstrap.tsx 분리 이유는?

    HMR 충돌 방지Module Federation이 React 마운트를 제어하는 경우 충돌 우려 → 분리 필수
    React 18 지원createRoot()는 DOM 준비 이후에 실행되어야 안전
    테스트 용이마운트와 로직을 분리하면 유닛 테스트 등에서 유리

    이 패턴은 Webpack/Rspack 환경에서 Module Federation + React 18을 함께 쓸 때 거의 필수다.


    📚 참고 자료

     

    ✨ 마무리하며

    Rspack은 빠른 속도와 Webpack 호환성을 모두 잡은 빌드 도구다.
    여기에 Module Federation까지 접목하면 확장성 높은 MFE 아키텍처를 구현할 수 있다.
    이번 글을 통해 Remote ↔ Host 앱 간 컴포넌트 공유와 HMR 연동까지 알아보았고, 다음 단계로는 SSR이나 동적 Remote 로딩, 다중 Remote 연결 같은 추가 기능을 설정해봐야 한다.

     

    ✅ 왜 Rspack에서 MFE를 시도했는가?

    • Webpack보다 훨씬 빠른 빌드 시간 → 개발 경험 개선
    • MFE + HMR을 자연스럽게 적용할 수 있는 환경 제공

    ✅ 기존 Webpack 대비 성능 차이?

    • Rspack은 Rust로 작성되어 빌드/번들링 속도가 Webpack보다 최대 10배 빠릅니다 (특히 HMR 적용 시 체감 큼)

    ✅ 실무에서 어떻게 활용 가능할까?

    • 사일로 팀 구조에서 앱을 나누어 독립 배포하고 싶을 때
    • 다양한 FE 앱이 공통 UI 컴포넌트를 공유할 때
    • 디자인 시스템을 별도 Remote 앱으로 운영하고 싶을 때

    도움이 되셨다면 ⭐️, 댓글로 궁금한 점을 남겨주세요!

     

    🍋 실제 작성한 예제 코드

    https://github.com/citron03/practice-rspack-1/commit/237d2a0f8c79824f7877e0ef5bdc75aa36a7c60c

    feat: example rspack host & remote button · citron03/practice-rspack-1@237d2a0

    1+# Dependency directories

    github.com

     

Designed by Tistory.