Skip to content

프래그먼트 공유를 위한 준비와 설계

프래그먼트(Fragment) 개념을 복습하고, Auth0 Client Provider를 shell-router 공유 패키지로 이관하며, peer dependency와 Module Federation expose 설정으로 프래그먼트 공유 인프라를 구축한다.

학습 목표

  • 마이크로프론트엔드에서 프래그먼트의 개념과 역할을 명확히 이해한다
  • Auth0 Client Provider를 shell-router 패키지로 이관하여 전 서비스에서 동일한 인증 컨텍스트를 공유하는 방법을 익힌다
  • peer dependency를 활용한 React 공유 전략을 구현할 수 있다
  • 프래그먼트를 위한 Module Federation expose 설정 방법을 이해한다

1. 프래그먼트란 무엇인가

프래그먼트(Fragment)는 다른 마이크로앱 내부에 임베드되는 코드 조각이다. 마이크로앱이 전체 페이지를 담당한다면, 프래그먼트는 페이지의 한 영역만 담당한다. 예를 들어, 포스팅 페이지에 "추천 1촌 목록"이나 "추천 채용 공고"를 표시하는 위젯이 프래그먼트다.

마이크로앱 vs 프래그먼트:

특성마이크로앱프래그먼트
단위전체 페이지페이지 내 영역 (위젯)
라우팅자체 라우트 보유자체 라우트 없음 (호스트에 의존)
로딩Shell이 라우트 전환 시 로드호스트 마이크로앱 내부에서 로드
소유권해당 팀이 완전 소유제공팀이 소유하지만 다른 팀 페이지에 임베드
API 호출독립적독립적 (자체 토큰 사용)

2. Auth0 Client Provider를 shell-router로 이관

프래그먼트가 다른 마이크로앱 안에서 API를 호출하려면 Auth0 토큰이 필요하다. 기존에는 각 마이크로앱이 독립적으로 Auth0ClientProvider를 가지고 있었는데, 이를 shell-router 공유 패키지로 이관하면 프래그먼트도 동일한 인증 컨텍스트를 사용할 수 있다.

2-1. shell-router에 Provider와 Hook 추가

typescript
// packages/shell-router/src/providers/Auth0ClientProvider.tsx
import React from "react";
import { Auth0Client } from "@auth0/auth0-spa-js";

const Auth0ClientContext = React.createContext<Auth0Client | null>(null);
export { Auth0ClientContext };

type Auth0ClientProviderProps = React.PropsWithChildren<{
  options: {
    domain: string;
    clientId: string;
    redirectUri: string;
  };
}>;

const Auth0ClientProvider: React.FC<Auth0ClientProviderProps> = ({
  children,
  options: { domain, clientId, redirectUri }
}) => {
  const auth0Client = new Auth0Client({
    domain,
    clientId,
    authorizationParams: { redirect_uri: redirectUri }
  });

  return (
    <Auth0ClientContext.Provider value={auth0Client}>
      {children}
    </Auth0ClientContext.Provider>
  );
};

export default Auth0ClientProvider;

핵심 변경점: process.env에서 직접 환경변수를 읽지 않고, options props로 외부에서 주입받는다. 이렇게 하면 각 마이크로앱이 자신의 .env 값을 전달할 수 있다.

2-2. 각 마이크로앱에서 Provider 교체

typescript
// 변경 전 (각 앱 자체 Provider)
import Auth0ClientProvider from "./providers/Auth0ClientProvider";

// 변경 후 (shell-router에서 가져옴)
import { Auth0ClientProvider } from "@career-up/shell-router";

// routes.tsx에서 options 전달
<Auth0ClientProvider
  options=&#123;&#123;
    domain: process.env.REACT_APP_AUTH0_DOMAIN as string,
    clientId: process.env.REACT_APP_AUTH0_CLIENT_ID as string,
    redirectUri: process.env.REACT_APP_AUTH0_CALLBACK_URL as string,
  &#125;&#125;
>
  {/* ... */}
</Auth0ClientProvider>

3. peer dependency로 React 공유 설정

shell-router에서 @auth0/auth0-spa-js를 사용하므로, 이를 peer dependency로 등록하여 각 마이크로앱이 설치한 버전을 공유한다.

json
// packages/shell-router/package.json
{
  "peerDependencies": {
    "react": "^18.0.0",
    "react-dom": "^18.0.0",
    "@auth0/auth0-spa-js": "^2.0.0"
  },
  "devDependencies": {
    "@auth0/auth0-spa-js": "^2.0.0"
  }
}
typescript
// packages/shell-router/vite.config.ts (또는 빌드 설정)
export default {
  build: {
    lib: { /* ... */ },
    rollupOptions: {
      external: [
        "react",
        "react-dom",
        "@auth0/auth0-spa-js"  // 외부 의존성으로 처리
      ],
      output: {
        globals: {
          react: "React",
          "react-dom": "ReactDOM",
          "@auth0/auth0-spa-js": "Auth0SpaJs"
        }
      }
    }
  }
};

peer dependency 체인:

마이크로앱 (posting, edu, network, job)
  ├── @career-up/shell-router (workspace:*)
  │     ├── peerDep: react ^18
  │     └── peerDep: @auth0/auth0-spa-js ^2
  ├── react 18.x (직접 설치)
  └── @auth0/auth0-spa-js 2.x (직접 설치)

4. useShellNavigate 훅 추가

프래그먼트에서 다른 마이크로앱의 라우트로 이동해야 할 때, react-router-domnavigate()는 해당 마이크로앱 스코프 내에서만 동작한다. Shell의 BrowserRouter를 제어하기 위해 커스텀 이벤트 기반 navigate를 구현한다.

typescript
// packages/shell-router/src/hooks/useShellNavigate.ts
import { useCallback } from "react";

export default function useShellNavigate() {
  return useCallback((pathname: string) => {
    window.dispatchEvent(
      new CustomEvent("shell:navigate", { detail: { pathname } })
    );
  }, []);
}
typescript
// packages/shell-router/src/hooks/useShellNavigateListener.ts
import { useEffect } from "react";
import { useNavigate } from "react-router-dom";

export default function useShellNavigateListener() {
  const navigate = useNavigate();

  useEffect(() => {
    const listener = (event: Event) => {
      const { pathname } = (event as CustomEvent).detail;
      navigate(pathname);
    };
    window.addEventListener("shell:navigate", listener);
    return () => window.removeEventListener("shell:navigate", listener);
  }, [navigate]);
}

5. shell-router export 및 빌드

typescript
// packages/shell-router/src/index.ts
export { default as injectFactory } from "./inject";
export { default as AppRoutingManager } from "./AppRoutingManager";
export { default as Auth0ClientProvider } from "./providers/Auth0ClientProvider";
export { default as useAuth0Client } from "./hooks/useAuth0Client";
export { default as useShellNavigate } from "./hooks/useShellNavigate";
export { default as useShellNavigateListener } from "./hooks/useShellNavigateListener";
export type { InjectFunctionType } from "./types";

빌드 후 각 마이크로앱에서 기존 providers/ 폴더와 hooks/ 폴더의 Auth0 관련 파일을 삭제하고, shell-router에서 import하도록 변경한다.

삭제 대상대체 import
src/providers/Auth0ClientProvider.tsximport { Auth0ClientProvider } from "@career-up/shell-router"
src/hooks/useAuth0Client.tsimport { useAuth0Client } from "@career-up/shell-router"

6. Module Federation expose 설정

프래그먼트를 노출하는 방법은 두 가지다.

방식설명장점단점
별도 런타임fragments/ 폴더에 새 MF 앱 생성의존성 격리, 불필요한 코드 유입 방지패키지 생성 비용
기존 앱 확장기존 마이크로앱의 exposes에 추가설정 간단, 같은 인프라 재사용불필요한 의존성 유입 가능
javascript
// 방식 1: 별도 런타임 (fragments/fragment-recommend-connections)
new ModuleFederationPlugin({
  name: "fragment_recommend_connections",
  filename: "remoteEntry.js",
  exposes: {
    "./container": "./src/containers/RecommendConnectionsContainer.tsx"
  },
  shared: { /* react, shell-router 등 */ }
})

// 방식 2: 기존 앱 확장 (apps/job)
new ModuleFederationPlugin({
  name: "job",
  filename: "remoteEntry.js",
  exposes: {
    "./injector": "./src/injector.tsx",
    "./fragment-recommend-jobs": "./src/fragments/RecommendJobsContainer.tsx"
  },
  shared: { /* ... */ }
})

핵심 정리

  1. 프래그먼트는 다른 마이크로앱에 임베드되는 코드 조각으로, 자체 API 호출과 상태 관리를 독립적으로 수행한다
  2. Auth0ClientProvider를 shell-router로 이관하면 마이크로앱과 프래그먼트 모두 동일한 인증 컨텍스트로 토큰을 획득할 수 있다
  3. options props로 환경변수를 외부 주입하면 빌드 타임 의존성 없이 Provider를 재사용할 수 있다
  4. @auth0/auth0-spa-js를 peer dependency + external로 설정하면 번들에 포함되지 않고 호스트 앱의 인스턴스를 공유한다
  5. useShellNavigate는 CustomEvent를 통해 Shell의 BrowserRouter를 제어하여 프래그먼트에서 다른 마이크로앱으로 이동할 수 있게 한다
  6. 프래그먼트 노출 방식은 "별도 런타임"과 "기존 앱 확장" 두 가지가 있으며, 프로젝트 상황에 맞게 선택한다

다음 단계