Files
2nd/10_Wiki/Topics/Frontend/Micro-frontends.md
T
koriweb d8a80f6272 chore(wiki): dangling 링크 canonical 정규화 (768파일/1200건)
이름만 다른(표기 변형) [[위키링크]]를 대상 문서의 canonical 제목으로 치환해
끊겼던 1,200개 링크를 연결. 제목/파일명 정규화 일치만 적용하고 별칭 매칭은
과병합 위험으로 제외(애매성 가드). 원본은 _link_reconcile_backup/ 에 백업.
도구: Datacollect/scripts/link_reconcile_apply.mjs

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 12:24:15 +09:00

264 lines
7.8 KiB
Markdown

---
id: wiki-2026-0508-micro-frontends
title: Micro frontends
category: 10_Wiki/Topics
status: verified
canonical_id: self
aliases: [MFE, Micro-frontend Architecture]
duplicate_of: none
source_trust_level: A
confidence_score: 0.9
verification_status: applied
tags: [frontend, architecture, modular]
raw_sources: []
last_reinforced: 2026-05-10
github_commit: pending
tech_stack:
language: TypeScript
framework: Module Federation / Single-SPA
---
# Micro frontends
## 매 한 줄
> **"매 microservices 매 frontend 적용 — 매 large UI 매 independent deployable team-owned vertical slices 매 분할"**. ThoughtWorks Tech Radar (2016) 매 이름 채택, 매 Webpack 5 Module Federation (2020) 매 mainstream 진입, 매 2026 매 large enterprise frontend (Spotify, IKEA, DAZN) 매 standard, 매 Vite Module Federation + Native Federation 매 modern stack.
## 매 핵심
### 매 핵심 가치 (vs monolith)
- **Independent deploy**: 매 team 매 자기 slice 매 release pipeline.
- **Tech heterogeneity**: 매 React + Vue + Svelte 매 공존 (단 cost ↑).
- **Team scalability**: 매 vertical team ownership — 매 Conway's law 매 align.
- **Incremental migration**: 매 legacy → modern 매 점진적 교체.
### 매 cost
- **Bundle duplication**: 매 framework 매 여러 번 load.
- **Cross-MFE state**: 매 shared store 매 design.
- **Routing coordination**: 매 shell + child route 매 sync.
- **Versioning skew**: 매 shared dep 매 version conflict.
- **DX overhead**: 매 dev 매 multi-repo / multi-server.
### 매 composition strategies
- **Build-time**: npm package — 매 monorepo + 매 publish (closest to monolith).
- **Server-side**: SSI/ESI/Tailor — 매 edge composition (Podium, Mosaic).
- **Run-time iframe**: 매 ultimate isolation — 매 worst UX.
- **Run-time Web Components**: 매 framework-agnostic embed.
- **Run-time Module Federation** (Webpack 5 / Vite): 매 sharing dep + dynamic import — 매 dominant 2026.
- **Run-time Single-SPA**: 매 lifecycle orchestration framework.
### 매 응용
1. Large e-commerce (header/cart/product/checkout 매 별도 team).
2. Multi-tenant SaaS (workspace + apps marketplace).
3. Legacy modernization (strangler fig pattern).
4. White-label platforms (per-customer customization).
## 💻 패턴
### Webpack Module Federation — host (shell)
```js
// shell/webpack.config.js
const { ModuleFederationPlugin } = require('webpack').container
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: 'shell',
remotes: {
cart: 'cart@https://cart.example.com/remoteEntry.js',
product: 'product@https://product.example.com/remoteEntry.js',
},
shared: {
react: { singleton: true, requiredVersion: '^18.0.0' },
'react-dom': { singleton: true, requiredVersion: '^18.0.0' },
},
}),
],
}
```
### Module Federation — remote (cart MFE)
```js
// cart/webpack.config.js
new ModuleFederationPlugin({
name: 'cart',
filename: 'remoteEntry.js',
exposes: {
'./CartWidget': './src/CartWidget',
'./useCart': './src/hooks/useCart',
},
shared: {
react: { singleton: true },
'react-dom': { singleton: true },
},
})
```
### Host consume (React lazy)
```tsx
// shell/App.tsx
import React, { lazy, Suspense } from 'react'
const CartWidget = lazy(() => import('cart/CartWidget'))
export function App() {
return (
<div>
<Header />
<Suspense fallback={<div>Loading cart</div>}>
<CartWidget />
</Suspense>
</div>
)
}
```
### Vite Module Federation
```ts
// cart/vite.config.ts
import federation from '@originjs/vite-plugin-federation'
export default {
plugins: [
federation({
name: 'cart',
filename: 'remoteEntry.js',
exposes: { './CartWidget': './src/CartWidget.tsx' },
shared: ['react', 'react-dom'],
}),
],
build: { target: 'esnext', minify: false, cssCodeSplit: true },
}
```
### Single-SPA root config
```ts
// root-config/index.ts
import { registerApplication, start } from 'single-spa'
registerApplication({
name: '@org/cart',
app: () => System.import('@org/cart'),
activeWhen: ['/cart'],
})
registerApplication({
name: '@org/product',
app: () => System.import('@org/product'),
activeWhen: ['/products'],
})
start()
```
### Single-SPA child lifecycle
```ts
// cart-app/src/main.ts
import { h, createApp } from 'vue'
import singleSpaVue from 'single-spa-vue'
import Root from './Root.vue'
const lifecycles = singleSpaVue({
createApp,
appOptions: { render: () => h(Root) },
})
export const bootstrap = lifecycles.bootstrap
export const mount = lifecycles.mount
export const unmount = lifecycles.unmount
```
### Web Components 매 framework-agnostic embed
```ts
// cart-mfe.ts
class CartElement extends HTMLElement {
connectedCallback() {
const root = this.attachShadow({ mode: 'open' })
// mount React/Vue/Svelte into shadow DOM
mountReact(<CartWidget />, root)
}
disconnectedCallback() { unmountReact(this.shadowRoot!) }
}
customElements.define('app-cart', CartElement)
```
```html
<!-- 매 host page (any framework) -->
<script src="https://cart.example.com/cart.js" type="module"></script>
<app-cart></app-cart>
```
### Cross-MFE communication — Custom Events
```ts
// 매 publish
window.dispatchEvent(new CustomEvent('cart:item-added', {
detail: { sku: 'A123', qty: 1 },
}))
// 매 subscribe
window.addEventListener('cart:item-added', (e) => {
console.log('item added:', (e as CustomEvent).detail)
})
```
### Shared shell store (BroadcastChannel)
```ts
const channel = new BroadcastChannel('app-state')
// MFE A
channel.postMessage({ type: 'auth/login', user })
// MFE B
channel.onmessage = ({ data }) => {
if (data.type === 'auth/login') updateLocalState(data.user)
}
```
### CSS isolation strategies
```
1. Shadow DOM (Web Components 매 자동 scope)
2. CSS Modules (build-time hash)
3. CSS-in-JS (runtime scope, e.g., styled-components)
4. PostCSS namespace plugin (legacy)
5. Tailwind prefix per MFE: `tw-cart-`, `tw-product-`
```
## 매 결정 기준
| 상황 | Approach |
|---|---|
| Single team, single product | Monolith (MFE overkill) |
| Multiple teams, shared shell | Module Federation |
| Legacy → React migration | iframe → Web Components → MF |
| Framework heterogeneity | Web Components / Single-SPA |
| Maximum isolation (security) | iframe (last resort) |
| Tight budget (LCP critical) | Build-time composition |
| SSR + MFE | Server-side composition (Tailor, Mosaic) |
| Vite stack | Native Federation / vite-plugin-federation |
**기본값**: 매 React/Vue 균일 stack 매 Module Federation, 매 framework 혼재 매 Single-SPA + Web Components.
## 🔗 Graph
- 부모: [[프론트엔드 및 UIUX 표준|Frontend-Architecture]] · [[Microservices]]
- 변형: [[Module-Federation]] · [[Web-Components]]
- Adjacent: [[Monorepo]] · [[Vite]]
## 🤖 LLM 활용
**언제**: 매 large team (50+ FE devs), 매 independent deploy 필요, 매 legacy modernization, 매 multi-tenant white-label.
**언제 X**: 매 small team (<10), 매 single-page simple SPA, 매 LCP-critical landing — 매 monolith 적합.
## ❌ 안티패턴
- **매 component-level MFE**: 매 button 매 MFE — 매 latency overhead — 매 vertical slice 매 단위.
- **Shared global state via window**: 매 race condition + 매 hidden coupling.
- **Framework version skew**: 매 React 17 + 18 매 host — 매 hooks 깨짐.
- **No design system**: 매 visual inconsistency — 매 shared tokens/components 필수.
- **Synchronous load chain**: 매 cascade waterfall — 매 lazy + 매 parallel 매 host.
- **MFE 매 small team 매 도입**: 매 over-engineering — 매 monolith + 매 module boundary 매 충분.
## 🧪 검증 / 중복
- Verified (ThoughtWorks Tech Radar, micro-frontends.org, Module Federation docs).
- 신뢰도 A.
## 🕓 Changelog
| 날짜 | 변경 |
|---|---|
| 2026-05-08 | Phase 1 |
| 2026-05-10 | Manual cleanup — composition strategies + MF/Single-SPA/WC patterns + comm matrix |