테마
통합 방식 설계: 런타임과 빌드타임
Webpack Module Federation을 이용한 런타임 통합과 Vite 기반 공통 패키지의 빌드타임 통합을 설계하고, 공통 모듈(UIKit, Shell Router)의 인터페이스를 정의한다.
학습 목표
- 런타임 통합(Module Federation)에서 Host/Remote 역할 분담을 이해한다
- Shell과 마이크로앱의 독립 렌더 라이프사이클이 갖는 장단점을 파악한다
- 빌드타임 공유 패키지의 설계 원칙과 package.json 인터페이스를 이해한다
- UIKit과 Shell Router의 모듈 구성과 주요 인터페이스를 설계할 수 있다
- Module Federation의 shared 설정으로 중복 로드를 방지하는 방법을 안다
1. 런타임 통합: Module Federation
1.1 Host와 Remote 역할 분담
커리어업의 런타임 통합은 Webpack Module Federation을 기반으로 한다. Shell이 유일한 Host이고, 4개의 마이크로앱이 Remote가 된다.
| 역할 | 앱 | 포트 | 책임 |
|---|---|---|---|
| Host | Shell | 3000 | GNB, 인증, 라우팅, Remote 앱 로드 |
| Remote | Posting | 3001 | 포스팅 기능 전체, 마운트 함수 expose |
| Remote | Network | 3002 | 네트워킹 기능 전체, 일촌 프래그먼트 expose |
| Remote | Education | 3003 | 교육 기능 전체 |
| Remote | Jobs | 3004 | 채용 기능 전체, 추천 프래그먼트 expose |
Webpack Module Federation을 선택한 이유는 프로덕션 환경에서의 안정성이다. Vite의 Module Federation 플러그인은 아직 프로덕션에서 충분히 검증되지 않았으므로, 런타임 앱은 Webpack을 사용한다. 빌드타임 패키지는 간편한 Vite를 사용한다.
1.2 독립 렌더 라이프사이클
커리어업의 각 마이크로앱은 Shell과 별도의 React 렌더 라이프사이클을 갖는다. Shell이 제공하는 마운트 영역(DOM 요소)에 각 마이크로앱이 자체적으로 ReactDOM.createRoot()를 호출하여 독립된 React 트리를 구성한다.
| 구분 | 하위 컴포넌트 방식 | 독립 렌더 방식 (커리어업 채택) |
|---|---|---|
| 렌더링 | Shell의 React 트리 안에 포함 | 별도의 React 트리 생성 |
| Props 전달 | Shell에서 직접 props 전달 가능 | 이벤트 시스템으로 데이터 교환 |
| 상태 공유 | Context API 사용 가능 | 사용 불가, 이벤트 기반 통신 필요 |
| 리렌더링 | Shell 리렌더 시 함께 리렌더 | Shell 리렌더와 독립적 |
| 독립성 | 낮음 (Shell에 종속적) | 높음 (완전히 독립적) |
| 기술 유연성 | Shell과 동일 React 버전 필수 | 이론적으로 다른 프레임워크도 가능 |
독립 렌더 방식을 채택하면 추가 작업(이벤트 시스템 구축)이 필요하지만, 마이크로앱의 독립성을 최대한 보장할 수 있다. 이는 MFE 아키텍처의 핵심 가치인 팀 자율성과 독립 배포에 부합한다.
2. 빌드타임 공유: 공통 패키지
2.1 빌드타임 vs 런타임 공유 구분
커리어업에서 공유되는 코드는 두 가지 방식으로 나뉜다:
| 공유 방식 | 대상 | 도구 | 특징 |
|---|---|---|---|
| 빌드타임 | UIKit, Shell Router, Utils | Vite (라이브러리 모드) | 앱 빌드 시 포함, npm 패키지 형태 |
| 런타임 | 마이크로앱, 프래그먼트 | Webpack Module Federation | 브라우저에서 동적 로드 |
2.2 패키지 인터페이스 원칙
빌드타임 공유 패키지는 package.json에 명시된 인터페이스만으로 소통한다. 패키지 내부 구현은 캡슐화되고, 소비자(앱)는 export된 모듈만 사용할 수 있다.
json
{
"name": "@careerup/ui-kit",
"main": "./dist/index.js",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"exports": {
".": {
"import": "./dist/index.mjs",
"require": "./dist/index.js",
"types": "./dist/index.d.ts"
},
"./styles": "./dist/global.css"
},
"peerDependencies": {
"react": "^18.0.0",
"react-dom": "^18.0.0"
}
}핵심 포인트:
exports필드로 외부에 공개할 진입점을 명시한다peerDependencies로 React를 선언하여 앱 전체에서 한 번만 로드되도록 한다types필드로 TypeScript 타입 정의를 제공한다
2.3 Module Federation shared 설정
빌드타임 패키지가 여러 마이크로앱에 포함되면, 런타임에 같은 코드가 여러 번 로드될 수 있다. Module Federation의 shared 설정으로 이를 방지한다.
javascript
// webpack.config.js 예시
new ModuleFederationPlugin({
shared: {
react: { singleton: true, requiredVersion: '^18.0.0' },
'react-dom': { singleton: true, requiredVersion: '^18.0.0' },
'@careerup/ui-kit': { singleton: true },
'@careerup/shell-router': { singleton: true },
},
});singleton: true로 설정하면 런타임에 해당 모듈이 단 한 번만 로드되고, 모든 마이크로앱이 동일한 인스턴스를 공유한다.
3. 공통 모듈 설계: UIKit
3.1 UIKit의 구성
UIKit(@careerup/ui-kit)은 커리어업 전체의 디자인 일관성을 보장하는 공통 컴포넌트 라이브러리이다.
| 모듈 | 설명 |
|---|---|
| global.css | CSS Reset, 기본 타이포그래피, CSS Custom Properties(색상, 간격 등) |
| Button | 공통 버튼 컴포넌트 (primary, secondary, ghost 등) |
| Card | 콘텐츠 카드 컴포넌트 (리스트 아이템 등에 범용 사용) |
| Icon | SVG 아이콘 컴포넌트 세트 |
| Layout | 헤더, 사이드바, 콘텐츠 영역 등 레이아웃 컴포넌트 |
3.2 UIKit 설계 원칙
| 원칙 | 설명 |
|---|---|
| 기술 비의존성 | Emotion, Styled Components 등 특정 CSS-in-JS에 의존하지 않는다 |
| 기본 CSS 사용 | CSS Modules 또는 순수 CSS로 작성하여 어떤 앱에서든 사용 가능 |
| cu- 접두사 | 모든 클래스에 cu- 접두사를 붙여 다른 앱 스타일과 충돌 방지 |
| Peer Dependency | React를 peerDependency로 선언하여 중복 번들링 방지 |
| Vite 라이브러리 모드 | Vite의 라이브러리 모드로 빌드하여 ES Module과 CommonJS 동시 지원 |
4. 공통 모듈 설계: Shell Router
4.1 Shell Router의 구성
Shell Router(@careerup/shell-router)는 Shell과 마이크로앱 사이의 라우팅 동기화와 인증 정보 교환을 담당하는 핵심 인프라 패키지이다.
4.2 모듈별 상세 인터페이스
라우팅 훅
| 모듈 | 사용처 | 입력 | 출력/동작 |
|---|---|---|---|
useAppEvent | 마이크로앱 | Memory Router의 navigate 함수 | Shell의 네비게이션 이벤트를 수신하여 Memory Router 경로 변경, 내부 경로 변경 시 Shell에 알림 |
useShellEvent | Shell | Browser Router의 navigate 함수 | 마이크로앱의 네비게이션 이벤트를 수신하여 Browser URL 변경, URL 변경 시 마이크로앱에 알림 |
컴포넌트
| 모듈 | 사용처 | 설명 |
|---|---|---|
AppRoutingManager | 마이크로앱 | useAppEvent 훅을 내부적으로 사용하는 래퍼 컴포넌트. React Router의 <Outlet />과 연결하여 마이크로앱의 라우팅을 처리한다. 마이크로앱은 이 컴포넌트만 import하면 된다. |
팩토리 함수
| 모듈 | 사용처 | 설명 |
|---|---|---|
mount(el, App) | 마이크로앱의 expose | 마운트 대상 DOM 요소와 루트 컴포넌트를 받아 ReactDOM.createRoot로 렌더링하는 함수를 생성한다. Shell이 이 함수를 호출하여 마이크로앱을 삽입한다. |
인증 모듈
| 모듈 | 사용처 | 설명 |
|---|---|---|
useUserEvent | Shell | 마이크로앱의 사용자 정보 요청 이벤트를 수신하고, 현재 로그인된 사용자의 토큰과 정보를 이벤트로 응답한다. |
getUserAsync | 마이크로앱 | Shell에 사용자 정보를 요청하는 비동기 함수. Promise를 반환하며, Shell의 응답 이벤트를 받을 때까지 대기한다. |
5. Webpack Module Federation 설정 계획
5.1 Shell (Host) 설정 구조
javascript
// apps/shell/webpack.config.js
new ModuleFederationPlugin({
name: 'shell',
remotes: {
posting: 'posting@http://localhost:3001/remoteEntry.js',
networking: 'networking@http://localhost:3002/remoteEntry.js',
education: 'education@http://localhost:3003/remoteEntry.js',
jobs: 'jobs@http://localhost:3004/remoteEntry.js',
},
shared: {
react: { singleton: true, requiredVersion: '^18.0.0' },
'react-dom': { singleton: true, requiredVersion: '^18.0.0' },
'react-router-dom': { singleton: true },
'@careerup/ui-kit': { singleton: true },
'@careerup/shell-router': { singleton: true },
},
});5.2 마이크로앱 (Remote) 설정 구조
javascript
// apps/posting/webpack.config.js
new ModuleFederationPlugin({
name: 'posting',
filename: 'remoteEntry.js',
exposes: {
'./App': './src/App',
'./mount': './src/mount',
},
shared: {
react: { singleton: true, requiredVersion: '^18.0.0' },
'react-dom': { singleton: true, requiredVersion: '^18.0.0' },
'react-router-dom': { singleton: true },
'@careerup/ui-kit': { singleton: true },
'@careerup/shell-router': { singleton: true },
},
});5.3 프래그먼트를 expose하는 Remote
네트워킹과 채용 앱은 자신의 앱 전체(./App)뿐만 아니라, 프래그먼트도 함께 expose한다:
javascript
// apps/networking/webpack.config.js - exposes 부분
exposes: {
'./App': './src/App',
'./mount': './src/mount',
'./FriendWidget': './src/fragments/FriendWidget', // 프래그먼트
},6. 전체 통합 아키텍처 요약
핵심 정리
- 런타임 통합: Shell이 유일한 Host, 4개 마이크로앱이 Remote. Webpack Module Federation으로 결합한다
- 독립 렌더 라이프사이클: 각 마이크로앱은 별도의
ReactDOM.createRoot()로 독립 React 트리를 형성한다. Shell의 리렌더와 무관하게 동작한다 - 빌드타임 공유: UIKit과 Shell Router는 Vite 라이브러리 모드로 빌드하고, package.json 인터페이스로만 소통한다
- UIKit: 기술 비의존적인 기본 CSS로 공통 컴포넌트를 작성하고,
cu-접두사로 스타일 충돌을 방지한다 - Shell Router: 라우팅 동기화(
useAppEvent/useShellEvent)와 인증 공유(useUserEvent/getUserAsync)를 담당하는 핵심 인프라 패키지이다 - shared 설정: Module Federation의
singleton: true로 React, UIKit, Shell Router의 중복 로드를 방지한다 - 빌드타임 패키지는 어떤 빌드 도구로든 만들 수 있지만, 패키지 제작에 간편한 Vite를 사용한다
다음 단계
다음 문서 ../11-공통-모듈-구현/01-UI-라이브러리-개발에서는 UIKit 패키지를 실제로 구현한다. Vite 라이브러리 모드 설정, global.css 작성, Button/Card/Icon 컴포넌트 개발, 그리고 빌드 및 다른 앱에서의 사용법을 다룬다.