---
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
- 부모: [[Frontend-Architecture]] · [[Microservices]]
- 변형: [[Module-Federation]] · [[Single-SPA]] · [[Web-Components]] · [[Bit]]
- 응용: [[Strangler-Fig-Pattern]] · [[Multi-Team-Frontend]]
- Adjacent: [[Monorepo]] · [[Vite]] · [[Webpack]] · [[Edge-Side-Includes]]

## 🤖 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 |
