테마
프래그먼트 공유를 위한 준비와 설계
프래그먼트(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={{
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,
}}
>
{/* ... */}
</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-dom의 navigate()는 해당 마이크로앱 스코프 내에서만 동작한다. 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.tsx | import { Auth0ClientProvider } from "@career-up/shell-router" |
src/hooks/useAuth0Client.ts | import { 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: { /* ... */ }
})핵심 정리
- 프래그먼트는 다른 마이크로앱에 임베드되는 코드 조각으로, 자체 API 호출과 상태 관리를 독립적으로 수행한다
- Auth0ClientProvider를 shell-router로 이관하면 마이크로앱과 프래그먼트 모두 동일한 인증 컨텍스트로 토큰을 획득할 수 있다
optionsprops로 환경변수를 외부 주입하면 빌드 타임 의존성 없이 Provider를 재사용할 수 있다@auth0/auth0-spa-js를 peer dependency + external로 설정하면 번들에 포함되지 않고 호스트 앱의 인스턴스를 공유한다useShellNavigate는 CustomEvent를 통해 Shell의 BrowserRouter를 제어하여 프래그먼트에서 다른 마이크로앱으로 이동할 수 있게 한다- 프래그먼트 노출 방식은 "별도 런타임"과 "기존 앱 확장" 두 가지가 있으며, 프로젝트 상황에 맞게 선택한다
다음 단계
- 02-프래그먼트-컴포넌트-작성.md: 일촌 맺기 프래그먼트(네트워킹 팀)와 추천 채용 공고 프래그먼트(채용 팀)를 Container/Presentational 패턴으로 구현한다