d8a80f6272
이름만 다른(표기 변형) [[위키링크]]를 대상 문서의 canonical 제목으로 치환해 끊겼던 1,200개 링크를 연결. 제목/파일명 정규화 일치만 적용하고 별칭 매칭은 과병합 위험으로 제외(애매성 가드). 원본은 _link_reconcile_backup/ 에 백업. 도구: Datacollect/scripts/link_reconcile_apply.mjs Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
264 lines
7.8 KiB
Markdown
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 |
|