20명의 개발자를 위해 깔끔한 Angular 앱으로 시작한 레포지토리가 3년 후 매일 50명 이상의 엔지니어가 커밋하는 상황을 상상해 보세요. 모든 풀 리퀘스트가 같은 번들을 건드립니다. 체크아웃 흐름의 버그 수정이 온보딩 마법사를 우연히 깨뜨리는데, 둘 다 누군가가 조용히 변경한 공유 유틸리티를 import하기 때문입니다. 릴리즈 캘린더는 테트리스 게임처럼 보입니다 — 다섯 팀이 하나의 예약된 배포 창에 기능을 넣으려고 줄을 섭니다. 모두가 서로를 기다리고 있으며, 아무도 원하는 만큼 빠르게 출시하지 못합니다.
이것이 프론트엔드 아키텍트들이 마이크로 프론트엔드라는 문구를 속삭이기 시작하는 고통입니다. 아이디어는 매혹적으로 들립니다: 마이크로서비스가 백엔드 팀을 모놀리스에서 해방시켰다면, UI도 같은 방식으로 할 수 없을까요? 각 팀에 자체적으로 독립 배포 가능한 프론트엔드 슬라이스를 주고, 브라우저(또는 서버)가 그것들을 다시 하나의 매끄러운 제품으로 이어붙이도록 합니다.
하지만 비유는 양면이 있습니다. 마이크로서비스는 진짜 자유 — 그리고 진짜 운영 고통을 가져왔습니다. 마이크로 프론트엔드도 다르지 않습니다. 멀티팀 회사에서 Angular 호스트와 React 기반 기능 모듈을 출시한 경험으로, 솔직한 버전을 전달하고 싶습니다: 무엇이 작동했고, 무엇이 아팠는지, 그리고 분리를 시작하기 전에 답해야 할 질문들.
마이크로 프론트엔드란 무엇인가
이 용어는 팀들이 마이크로서비스 혁명이 브라우저에 도달하지 않았다는 것을 깨달으면서 2016~2017년경에 대중화되었습니다. 전통적인 단일 페이지 애플리케이션에서는 하나의 모놀리식 프론트엔드 번들이 모든 것을 소유합니다: 라우팅, 상태, 모든 컴포넌트, 모든 팀의 기능. 마이크로 프론트엔드는 같은 분해 아이디어를 UI 레이어에 적용합니다.
마이크로 프론트엔드는 독립적으로 구축 가능하고, 독립적으로 배포 가능한 UI의 조각으로, 하나의 팀이 처음부터 끝까지 소유합니다. 그 팀은 자체 레포지토리(또는 최소한 자체 빌드 파이프라인), 자체 릴리즈 주기, 합의된 경계 내에서의 자체 기술 선택을 제어합니다. 런타임에는 쉘 애플리케이션 — 때로는 호스트 또는 컨테이너라고도 불림 — 이 이 조각들을 사용자가 보는 통합 제품으로 구성합니다.
핵심 변화는 소유권입니다. 공유 코드가 있는 모노레포에서는 공유 유틸리티의 변경이 어느 팀의 기능이든 깰 수 있습니다; 조율이 지속적입니다. 마이크로 프론트엔드로는, 팀 A의 체크아웃 모듈 배포가 팀 B의 대시보드 팀의 승인이 필요하지 않습니다. 그들 사이의 계약은 공유 소스 코드가 아닌 합의된 통합 인터페이스입니다.
통합 접근법
"마이크로 프론트엔드"는 단일 기술이 아닌 기법의 패밀리입니다. 조각들을 어떻게 이어붙이는지는 팀 자율성, 성능, 운영 복잡성에 엄청난 결과를 미칩니다. 다섯 가지 주요 접근법이 있습니다:
| 접근법 | 작동 방식 | 핵심 트레이드오프 |
|---|---|---|
| 빌드 타임 통합 (npm 패키지) | 각 마이크로 프론트엔드가 버전화된 npm 패키지로 발행됩니다. 쉘이 의존성으로 설치하고 모든 것을 함께 번들합니다. | 설정하기 단순하지만, 팀들이 릴리즈를 조율해야 합니다 — 원격을 업데이트하면 쉘이 다시 배포해야 합니다. 진정한 독립성이 아닙니다. |
| 서버 사이드 구성 | 역방향 프록시나 엣지 레이어(예: Nginx, Next.js 엣지 함수, Zalando의 Tailor)가 다른 서비스에서 HTML 조각을 가져와 브라우저에 도달하기 전에 조립합니다. | SEO와 첫 번째 페인트에 훌륭하지만; 인프라 복잡성과 서비스 간의 새로운 지연 표면을 추가합니다. |
| Module Federation을 통한 런타임 | Webpack 5(또는 Rspack/Vite 동등물)는 빌드가 모듈을 노출하여 쉘이 런타임에 별도의 CDN URL에서 재번들링 없이 로드할 수 있게 합니다. | 진정한 독립성 — 원격이 쉘을 건드리지 않고 배포 — 하지만 버전 스큐, 공유 의존성 협상, 디버깅 도구가 어렵습니다. |
| iframe | 각 마이크로 프론트엔드가 자체 iframe에서 실행됩니다. 격리가 거의 완벽합니다; 통신은 postMessage를 통해 이루어집니다. |
견고한 격리와 보안 경계이지만, 끔찍한 UX(스크롤, 포커스, 딥링크, 접근성 모두 커스텀 배관이 필요); 구식으로 느껴집니다. |
| 웹 컴포넌트 | 각 마이크로 프론트엔드가 커스텀 HTML 요소(<checkout-app>)를 노출합니다. 쉘이 요소를 페이지에 넣으면; 브라우저가 라이프사이클을 처리합니다. |
설계상 프레임워크 독립적이지만, SSR 지원이 미성숙하고, 복잡한 공유 상태는 여전히 전역 이벤트 버스나 컨텍스트 솔루션이 필요합니다. |
실제로, Module Federation이 진정한 런타임 독립성을 원하는 대규모 팀들의 주요 선택이 되었습니다. 그것을 제공하는 Webpack 5 플러그인은 Rspack으로 포팅되었고 Vite 동등물(예: @originjs/vite-plugin-federation)도 있습니다. 오늘날 누군가가 "마이크로 프론트엔드를 하고 있습니다"라고 말하면, 거의 항상 Module Federation을 의미합니다.
앱이 검색 결과에 순위를 매겨야 하고 마이크로 프론트엔드 아이디어를 처음 접하는 경우, 서버 사이드 구성이 브라우저가 닿기 전에 가장 깔끔한 HTML을 제공합니다. Module Federation이 강력한 수단이지만, 보상을 받기 전에 더 성숙한 배포 스토리가 필요합니다.
팀들이 채택하는 이유
마이크로 프론트엔드로 가는 동기는 제가 본 회사들에서 일관됩니다. 거의 항상 네 가지 압력 중 하나로 추적됩니다:
- 독립적 배포. 가장 많이 인용되는 이유입니다. 15개 팀이 하나의 프론트엔드 빌드를 공유할 때, 배포는 조율 세금입니다 — 모두가 다른 사람의 작업이 QA를 통과할 때까지 기다립니다. 마이크로 프론트엔드는 팀 A가 팀 B의 반쯤 완성된 기능이 준비될 때까지 기다리지 않고 화요일에 출시할 수 있게 합니다. 릴리즈 빈도가 올라가고; 릴리즈당 영향 범위가 내려갑니다.
- 팀 자율성과 소유권. 자신의 슬라이스를 처음부터 끝까지 구축, 테스트, 배포할 수 있는 팀은 더 빠르게 움직이고 더 책임감을 느낍니다. "풀스택 소유권"은 프론트엔드가 모두가 걸려 넘어지는 공유 커먼스가 아닐 때 실제가 됩니다.
- 점진적 기술 마이그레이션. 이것은 과소평가됩니다. 5년 된 AngularJS 앱을 실행하다가 React로 이동하고 싶다면, 전체를 한 번에 재작성할 수 없습니다. 마이크로 프론트엔드는 나머지 AngularJS 앱이 계속 실행되는 동안 새 React 모듈로 한 번에 하나의 경로를 교체할 수 있게 합니다. 이것은 프론트엔드에 적용된 교살자 무화과 패턴입니다.
- 장애 격리. 체크아웃 마이크로 프론트엔드의 JavaScript 런타임 오류는 체크아웃 모듈을 충돌시키지, 전체 앱을 충돌시키지 않습니다. 쉘이 오류 경계를 잡고, 폴백을 표시하고, 사용자가 이동할 수 있게 합니다. 모놀리스에서는 잡히지 않은 오류가 종종 전체 페이지를 무너뜨립니다.
실제 비용
많은 블로그 포스트들이 조용해지는 곳이 여기입니다. 마이크로 프론트엔드는 진짜 청구서를 가지고 있으며, 등록하기 전에 그것을 명확하게 봐야 합니다.
번들과 의존성 중복. 세 개의 마이크로 프론트엔드가 각각 자체 React 복사본을 번들링한다면, 사용자는 React를 세 번 다운로드합니다. Module Federation의 공유 스코프 메커니즘이 런타임에 중복을 제거할 수 있지만, 버전이 드리프트할 때 팀 간에 올바르게 구성하는 것은 사소하지 않습니다. 순진한 마이크로 프론트엔드 아키텍처는 실제로 잘 최적화된 모놀리스에 비해 페이로드를 부풀릴 수 있습니다.
디자인 시스템 공유. 통합된 외관은 공유 컴포넌트 라이브러리를 요구합니다. 그 라이브러리가 빌드 타임에 소비된다면(npm), 그것을 업데이트하는 모든 마이크로 프론트엔드가 재배포해야 합니다. 런타임에 Module Federation을 통해 소비된다면, 버전화와 호환성 전략이 필요합니다. 어느 쪽이든, 디자인 시스템이 팀 간 조율 지점이 됩니다 — 제거하려고 했던 바로 그것.
전역 상태와 MFE 간 통신. 마이크로 프론트엔드는 독립적이어야 하지만, 사용자는 전체론적입니다. 쉘의 알림 배지는 체크아웃 모듈의 액션에 대해 알아야 합니다. 한 모듈의 인증 이벤트는 어디서든 전파되어야 합니다. 솔루션은 공유 Redux 스토어(강한 결합)부터 커스텀 이벤트 버스나 전역 window의 공유 컨텍스트 객체까지 다양합니다 — 이 모두가 안전하게 사용하려면 훈련이 필요한 폭발물입니다.
버전 스큐. 팀들이 독립적으로 배포하기 때문에, 어느 순간 쉘이 원격 엔트리 형식 v1을 기대하는 Module Federation 버전을 실행하는 동안, 팀 C의 최신 배포는 형식 v2를 내보낼 수 있습니다. 이런 불일치는 로컬에서 재현하기 어렵고 프로덕션에서 추적하기는 더 어려운 암호화된 런타임 오류로 나타납니다. 명시적인 호환성 계약과 우아한 저하 전략이 필요합니다.
런타임 성능. 여러 비동기 번들 로드, 런타임 모듈 협상, 원격 엔트리에 대한 폭포수 페치 — 마이크로 프론트엔드는 모든 것이 정적으로 번들된 모놀리스에는 없는 지연을 도입합니다. 신중한 프리로딩, CDN 전략, HTTP/2 푸시가 모두 도움이 되지만, 그것들은 적극적인 투자가 필요합니다.
무거운 운영과 관측성. 이제 N개의 배포 파이프라인, N개의 CDN 캐시 버스팅 규칙, N개의 빌드 구성, 그리고 무언가 모듈 경계를 넘어 잘못될 때 상관지을 N개의 오류 로그가 있습니다. DevOps 오버헤드는 실제입니다. 프로덕션에 가기 전에 분산 추적, 마이크로 프론트엔드 버전을 포함하는 구조화된 로깅, 공유 알림 대시보드가 필요합니다.
모놀리스를 독립적인 조각들로 분리해도 총 복잡성이 줄지 않습니다 — 소스 코드에서 통합 레이어, 운영 도구, 팀 간 계약으로 복잡성이 이동합니다. 조직이 그 운영 인프라에 투자할 준비가 되어 있지 않다면, 분산 모놀리스로 끝날 것입니다: 모놀리스의 모든 결합, 분산 시스템의 모든 운영 오버헤드.
Angular 호스트 + React 모듈 출시: 솔직한 이야기
몇 년 전 저는 대규모 Angular 플랫폼 — 수십만 줄, 다섯 개의 제품 스쿼드, 분기별 릴리즈 주기가 제품 프로세스라기보다 인질 상황처럼 느껴지는 — 을 Module Federation 아키텍처로 마이그레이션하는 팀의 일원이었습니다: Angular 쉘이 독립적으로 배포된 React 기능 모듈들을 호스팅했습니다.
진정으로 작동한 것들입니다.
- 독립적 배포 주기가 문화를 변화시켰습니다. 두 달 안에, 역사적으로 분기별로 출시했던 스쿼드들이 매주 출시하고 있었습니다. 피드백 루프가 압축되었습니다. 엔지니어들이 관측성에 더 신경쓰기 시작했는데, 자신의 배포가 나간 후 대시보드를 보는 것이 자신들이었기 때문입니다.
- 기능 격리가 회귀 표면을 크게 줄였습니다. 청구 모듈의 변경이 더 이상 사용자 설정 페이지를 깰 이론적 경로가 없었습니다. 회귀 테스트 스위트가 팀별로 범위가 줄었습니다; 통합 테스트가 전체 앱보다 계약 지점에 집중했습니다.
- 점진적 마이그레이션이 구원이었습니다. 6개월짜리 "빅뱅" 재작성 동결이 없었습니다. React 모듈들이 18개월에 걸쳐 한 번에 하나의 경로씩 Angular 뷰를 교체했으며, 그동안 제품이 계속 출시되었습니다. 비즈니스 이해관계자들이 전환을 거의 알아채지 못했습니다 — 그것이 원하는 바입니다.
아팠던 것들입니다.
- 공유 상태가 지속적인 두통이었습니다. 전역 window 객체에 커스텀 이벤트 버스로 끝났습니다 — 기능적이지만 취약합니다. 두 팀이 독립적으로 이벤트 페이로드 모양을 변경했을 때, 특정 네비게이션 시퀀스에서만 나타나는 조용한 버그가 생겼습니다. 결국 MFE 간 이벤트를 스키마 레지스트리가 있는 내부 API처럼 모델링했는데, 이것이 엄청 도움이 되었지만 구축하는 데 몇 주가 걸렸습니다.
- 중복 의존성이 번들을 부풀렸습니다. Angular와 React — 두 가지 다른 빌드 시스템 — 에 걸쳐 Module Federation의
shared구성을 올바르게 맞추는 데 실제 노력이 들었습니다. 몇 번의 스프린트 동안 사용자들이 RxJS의 두 복사본과 거의 두 복사본의 차팅 라이브러리를 다운로드하고 있었는데, 우리가 그것을 발견할 때까지. CI에서의 자동화된 번들 분석이 필수적이 되었습니다. - 버전 조율이 완전히 사라지지 않았습니다. 쉘의 Angular 버전과 React 모듈의 의존성 매트릭스가 느슨하게 호환 가능한 상태로 유지되어야 했습니다. Angular가 주요 업데이트를 릴리즈했을 때, 그냥 쉘을 업그레이드할 수 없었습니다; 먼저 모든 모듈의 피어 의존성을 감사해야 했습니다. 조율 비용이 모놀리스보다 낮았지만, 사라지지는 않았습니다.
최종 평결: 아키텍처는 그 조직, 그 규모에서 올바른 선택이었습니다. 하지만 솔직히 말하면, 운영 투자를 약 30퍼센트 과소평가했고, 공유 상태 문제는 계획한 것보다 두 배 오래 걸렸습니다. 열린 눈으로 들어가세요.
마이크로 프론트엔드를 사용하지 말아야 할 때
마이크로 프론트엔드가 상황에 맞지 않는다는 가장 명확한 신호는 프론트엔드 고통의 주요 원천이 팀 규모 조율이 아닌 완전히 다른 것일 때입니다.
- 단일 소규모 팀. 프론트엔드의 모든 사람이 하나의 화상 통화에 들어갈 수 있다면, 조율 문제가 없습니다. 구성할 코드베이스가 있습니다. 마이크로 프론트엔드는 팀 자율성 혜택 없이 모든 운영 오버헤드를 줄 것입니다. 대신 잘 강제된 모듈 경계가 있는 모듈러 모놀리스 프론트엔드를 사용하세요.
- 소규모 애플리케이션. 전체 프론트엔드가 몇 개의 경로와 몇 천 줄이라면, 분리하면 제품 자체보다 더 많은 인프라 표면 영역이 생깁니다. Module Federation의 구성과 멀티파이프라인 CD의 오버헤드가 어떤 혜택도 훨씬 초과합니다.
- 좋은 경계가 있는 모노레포가 충분할 때. Nx, Turborepo, 유사한 도구들이 런타임 구성 복잡성 없이 엄격한 모듈 경계를 강제하고, 영향받은 패키지만 빌드를 실행하며, 팀에 소유권 정도를 줄 수 있게 합니다. 20~80명 엔지니어 범위의 많은 회사들에게, 잘 구조화된 모노레포가 더 나은 트레이드오프입니다. 리팩터링하기도 훨씬 쉽습니다.
- 팀이 아직 기초에 익숙하지 않을 때. CI/CD 파이프라인이 불안정하거나, 관측성 스토리가 얇거나, 해결되지 않은 팀 간 소통 기능 장애가 있다면, 마이크로 프론트엔드 복잡성을 추가하면 그 모든 문제들이 증폭될 것입니다. 먼저 기반을 고치세요.
이것은 백엔드 결정과 밀접하게 유사합니다. 모든 백엔드가 마이크로서비스 플릿이어야 하는 것은 아닙니다 — 같은 추론이 여기에도 적용됩니다. 모놀리스 → 마이크로서비스 분석을 아직 읽지 않으셨다면, 백엔드를 분리하기 전에 던지는 질문들이 프론트엔드에도 거의 그대로 적용됩니다: 독립적인 확장 요구, 독립적인 실패 도메인, 그리고 진정으로 자율적인 팀이 있나요? 그렇지 않다면, 모놀리스 — 또는 Modular Monolith — 가 아마 더 나은 답일 것입니다.
엄격한 린팅 경계, 팀별 소유 패키지, 빠른 증분 빌드 시스템을 가진 잘 구성된 프론트엔드 모노레포는 운영 비용의 일부로 자율성 혜택의 70~80%를 제공합니다. Module Federation을 사용하기 전에, 현재 코드베이스의 더 깔끔한 모듈 경계가 실제 문제를 해결할지 물어보세요.
회사 규모별
이 결정에 대한 가장 유용한 렌즈 중 하나는 단순합니다: 프론트엔드 엔지니어가 몇 명이고, 팀들이 얼마나 독립적으로 운영해야 하는가?
| 조직 규모 | 일반적인 프론트엔드 팀 구조 | 권장 사항 | 실제 예시 패턴 |
|---|---|---|---|
| 소규모 (< 15명 FE 개발자) | 한두 개 스쿼드, 모두가 모두의 코드를 압니다. | 하지 마세요. 모듈러 모놀리스나 잘 구조화된 모노레포를 사용하세요. 마이크로 프론트엔드의 조율 오버헤드가 혜택을 초과합니다. | 초기 단계 SaaS 스타트업: 기능 폴더와 엄격한 import가 있는 Next.js 앱 — 빠르게 출시하고, 추론하기 쉬움. |
| 중간 규모 (15~60명 FE 개발자) | 세 개에서 여덟 개 스쿼드, 배포 경쟁과 소유권 마찰을 느끼기 시작함. | 어쩌면, 이음새에서. 가장 높은 마찰 경계만 추출하는 것을 고려하세요 — 예: 체크아웃, 리포팅 대시보드 — 모든 것을 분리하기보다. 빌드 캐싱이 있는 모노레포가 먼저 해결할 수도 있습니다. | 스케일업 이커머스: 대부분의 기능에는 모노레포, 결제 흐름에만 Module Federation 원격 (PCI 격리 팀, 별도 배포). |
| 대규모 / 엔터프라이즈 (60명+ FE 개발자) | 많은 스쿼드, 종종 시간대 전반에 걸쳐, 하드 팀 소유권 경계와 별도 로드맵. | 종종 정당화됨. 공유 배포의 조율 오버헤드가 실제이고 측정 가능합니다. 전담 플랫폼 팀이 있는 Module Federation 또는 서버 사이드 구성이 합리적인 투자입니다. | 대형 은행, 통신사, 글로벌 소매업체: 플랫폼 팀이 소유하는 쉘 앱; 각각 자체 CD 파이프라인과 버전 계약을 가진 기능 부족이 소유하는 수십 개의 마이크로 프론트엔드. |
Spotify의 엔지니어링 블로그는 "스쿼드 모델"과 함께 온 프론트엔드 아키텍처 결정을 문서화했습니다. Zalando는 서버 사이드 구성 접근법인 Tailor에 대해 공개적으로 작성했습니다. IKEA의 디지털 플랫폼 팀들은 규모에서의 Module Federation에 대해 논의했습니다. 이 회사들의 공통점은 기술이 아닙니다 — 규모입니다: 그렇지 않으면 매일 서로를 막을 진정으로 독립적인 로드맵을 가진 많은 자율 팀들.
스펙트럼의 다른 쪽 끝에서, 20~50명의 개발자 범위에서 제가 본 가장 생산적인 프론트엔드 조직 중 일부는 엄격하게 강제된 모듈 경계와 빠른 증분 CI를 갖춘 단일 Nx 모노레포에서 모든 것을 실행합니다. 빠른 빌드, 명확한 소유권, 그리고 런타임 구성 버그 제로 — 런타임 구성이 없기 때문입니다. 회사가 성장함에 따라 6개월마다 마이크로 프론트엔드 질문을 다시 살펴보며, 그것이 정확히 맞습니다.
핵심 요약
- 마이크로 프론트엔드는 마이크로서비스 분해 아이디어를 UI에 적용합니다: 독립적으로 구축 가능하고, 독립적으로 배포 가능한 프론트엔드 슬라이스, 각각 하나의 팀이 소유하며, 런타임(또는 빌드 타임, 또는 서버 사이드)에 하나의 제품으로 구성됩니다.
- Module Federation(Webpack 5 / Rspack / Vite)이 지배적인 런타임 접근법입니다 — 진정한 배포 독립성을 제공하지만 신중한 공유 의존성 구성과 버전 계약 관리가 필요합니다.
- 핵심 혜택은 배포 독립성, 팀 자율성, 점진적 프레임워크 마이그레이션, 장애 격리입니다. 규모에서 그것들이 진정으로 필요하지 않다면, 혜택 없이 비용을 지불하는 것입니다.
- 실제 비용은 번들 중복, 공유 디자인 시스템 거버넌스, MFE 간 상태 복잡성, 버전 스큐, 런타임 지연, 무거운 DevOps입니다. 이 중 어느 것도 치명적이지 않습니다 — 하지만 모두 적극적인 투자가 필요합니다.
- 소규모 팀, 소규모 앱, 또는 잘 구조화된 모노레포가 실제 마찰을 해결할 경우 마이크로 프론트엔드를 사용하지 마세요. Modular Monolith는 과소평가됩니다.
- 팀 토폴로지에 아키텍처를 맞추세요: 소규모 팀 → Modular Monolith; 중간 규모 → 모노레포 또는 외과적 MFE 분리; 대규모 엔터프라이즈 → 전담 플랫폼 팀과 함께하는 전체 마이크로 프론트엔드 플랫폼.
- 복잡성은 항상 어딘가로 갑니다. 마이크로 프론트엔드는 그것을 소스 코드에서 통합 계약과 운영 인프라로 옮깁니다. 커밋하기 전에 조직이 그 변화를 소유할 준비가 되었는지 확인하세요.
이 글은 프론트엔드와 백엔드 아키텍처에 관한 시리즈의 일부입니다. 여기서 처음 접하셨다면, 시리즈가 시작되는 곳으로 돌아가 읽어보시길 권장합니다 — Ports & Adapters — 이것은 헥사고날 아키텍처 패턴과 주변의 프레임워크나 딜리버리 메커니즘에 관계없이 핵심 비즈니스 로직을 깔끔하고 테스트 가능하게 유지하는 방법을 다룹니다. 아이디어들은 서로 보완적입니다: 단일 백엔드 서비스 내에 명확한 경계를 두는 것과 같은 사고방식이, 더 큰 단위로, 프론트엔드 팀 간의 경계에 적용됩니다. 더 많은 아키텍처 글이 올 예정입니다 — 구독하거나 곧 다시 확인해 주세요, 그리고 여기서 만나길 바랍니다.