FEInterview Prep

architecture

마이크로 프론트엔드 — 대규모 프론트엔드 아키텍처

"팀이 커지면 모놀리식 프론트엔드에서 어떤 문제가 생기나요?"는 대기업 면접 질문입니다. 네이버, 카카오, 토스처럼 100명 이상 개발자가 하나의 서비스를 개발할 때 마이크로 프론트엔드는 자율성과 독립 배포를 가능하게 합니다. Module Federation, iframe, Web Components 중 어떤 방식이 언제 적합한지 알면 시스템 설계 면접에서 차별화됩니다.

시작 전
이해도
매우 낮음

학습 개요

탄생 배경

해결하려 했던 문제

마이크로서비스로 백엔드를 분리했지만 프론트엔드는 여전히 하나의 거대한 앱으로 남는 경우가 많습니다. 100명이 하나의 React 앱을 개발하면: PR 충돌, 빌드 시간 증가, 의존성 업데이트 공포, 한 팀의 배포가 전체 릴리즈를 차단, 기술 선택 자유도 없음 등의 문제가 발생합니다. 팀의 자율성을 저해합니다.

역사적 맥락

마이크로 프론트엔드 개념은 2016년 ThoughtWorks Technology Radar에서 처음 등장했습니다. 초기에는 iframe 기반(격리는 완벽하지만 UX 제한)이나 서버 사이드 includes(SSI)로 구현됐습니다. 2019년 Webpack 5의 Module Federation이 등장하며 런타임 모듈 공유가 가능해져 마이크로 프론트엔드 구현이 현실적이 됐습니다. single-spa 프레임워크가 라우팅 기반 앱 구성을 대중화했습니다.

이전에는 어떻게 했나

모놀리포(Monorepo) + 패키지 분리(Nx, Turborepo)는 코드 분리는 하되 하나의 빌드로 관리하는 절충안입니다. 빌드 시 결합되므로 마이크로 프론트엔드만큼의 격리는 없지만 복잡성도 낮습니다. 도메인 중심 폴더 구조(Feature-Sliced Design)로 논리적 분리만 하는 방식도 있습니다.

멘탈 모델

동작 원리

마이크로 프론트엔드 구현 방법: 1. Module Federation (Webpack 5) - 런타임에 다른 앱의 모듈을 동적 로드 - Shell App이 여러 Remote App을 조합 - React 버전 공유 가능 (shared 설정) - 가장 현실적인 현대 접근법 2. iframe - 완전한 격리 (스타일, JS) - UX 제한 (스크롤, 히스토리, 크기) - 성능 비용 - 레거시 시스템 통합에 유용 3. Web Components - 프레임워크 독립적 커스텀 엘리먼트 - Shadow DOM으로 스타일 격리 - 프레임워크 간 호환성 보장 4. 서버 사이드 컴포지션 - 서버에서 각 팀의 HTML 조각을 조합 - Edge Side Includes(ESI), Varnish 5. npm 패키지 + 모노레포 - 빌드 타임 결합, 런타임 분리 없음 - 버전 관리 필요

핵심 구성 요소

Shell App (Host)

라우팅, 공통 레이아웃, 원격 모듈 로드를 담당하는 컨테이너 앱

Remote App

독립 배포되는 마이크로 프론트엔드 단위. 자체 빌드 파이프라인 보유

Module Federation

Webpack 5 기능. 런타임에 원격 모듈을 지연 로드하고 모듈 공유

Shared Dependencies

React 등 공통 라이브러리를 한 번만 로드. 버전 충돌 관리 필요

Event Bus

마이크로 프론트엔드 간 통신. CustomEvent 또는 전역 이벤트 버스

single-spa

마이크로 프론트엔드 라우팅 프레임워크. 여러 SPA를 하나로 오케스트레이션

흐름 설명


[Module Federation 구조]

shell-app (webpack.config.js)
  ModuleFederationPlugin({
    name: 'shell',
    remotes: {
      checkout: 'checkout@http://checkout.example.com/remoteEntry.js',
      products: 'products@http://products.example.com/remoteEntry.js',
    },
    shared: { react: { singleton: true }, 'react-dom': { singleton: true } },
  })

products-app (webpack.config.js)
  ModuleFederationPlugin({
    name: 'products',
    filename: 'remoteEntry.js',
    exposes: { './ProductList': './src/ProductList' },
  })

// shell-app에서 사용
const ProductList = React.lazy(() => import('products/ProductList'));

[팀 경계]
팀 A: /checkout/* → checkout-app 독립 배포
팀 B: /products/* → products-app 독립 배포
팀 C: shell-app + 공통 컴포넌트 관리
    

코드 예제


// shell-app: 원격 마이크로 프론트엔드 로드
import React, { Suspense } from 'react';

// 런타임에 원격 앱에서 동적 로드
const CheckoutApp = React.lazy(() => import('checkout/App'));
const ProductsApp = React.lazy(() => import('products/App'));

function App() {
  return (
    <Router>
      <Suspense fallback={<Loading />}>
        <Routes>
          <Route path="/products/*" element={<ProductsApp />} />
          <Route path="/checkout/*" element={<CheckoutApp />} />
        </Routes>
      </Suspense>
    </Router>
  );
}

// 마이크로 프론트엔드 간 통신 — 이벤트 버스
// shared/eventBus.ts
class EventBus {
  dispatch(event: string, data: unknown) {
    window.dispatchEvent(new CustomEvent(event, { detail: data }));
  }
  subscribe(event: string, handler: (data: unknown) => void) {
    const wrapper = (e: CustomEvent) => handler(e.detail);
    window.addEventListener(event, wrapper as EventListener);
    return () => window.removeEventListener(event, wrapper as EventListener);
  }
}

// products-app에서 장바구니에 추가
eventBus.dispatch('cart:add', { productId: 123, quantity: 1 });

// checkout-app에서 수신
eventBus.subscribe('cart:add', ({ productId, quantity }) => {
  addToCart(productId, quantity);
});
    

비교 분석

마이크로

vs

모놀리식 프론트엔드

비교 관점
이 방식
모놀리식 프론트엔드
팀 자율성
높음 — 팀별 독립 배포, 기술 선택
낮음 — 하나의 코드베이스, 통합 배포
복잡성
높음 — 통신, 버전 관리, 공유 의존성
낮음 — 단일 빌드 시스템
성능
런타임 모듈 로드 비용
최적화된 단일 번들
적합 규모
팀 50명+, 여러 도메인
팀 ~50명, 단일 도메인

마이크로

vs

Module Federation vs iframe

비교 관점
이 방식
Module Federation vs iframe
격리
Module Federation: JS 격리 없음
iframe: 완전한 격리
UX
Module Federation: 네이티브 앱처럼 매끄러움
iframe: 스크롤, 크기 조정 제한
모듈 공유
Module Federation: 가능 (React 공유)
iframe: 불가능
CSP 복잡성
Module Federation: 낮음
iframe: X-Frame-Options 관리 필요

트레이드오프

이상적인 사용 사례

면접 질문