기사 원문: How Microfrontends Work: From iframes to Module Federation
마이크로프론트엔드는 팀이 대규모 프론트엔드 애플리케이션을 구축하고 배포하는 방식을 변화시키고 있습니다. 이 튜토리얼에서는 전통적인 접근 방식부터 현대적인 Module Federation 구현까지 아키텍처 전반을 살펴봅시다.
튜토리얼을 마치면 마이크로프론트엔드가 팀에 맞는 적절한 솔루션인지 평가할 수 있을 겁니다.
목차
- 마이크로프론트엔드란?
- 전통적인 마이크로프론트엔드 패턴
- 서버 측 구성
- iframe
- 빌드 타임 통합 – Package
- 현대적인 마이크로프론트엔드 패턴
- Module Federation
- Single SPA
- 상세 비교
- Module Federation 도입 시 주의할 점
- 설정 복잡도
- 런타임 고려사항
- 운영 시 고려사항
- 결론
- 다음 단계
마이크로프론트엔드란?
백엔드의 마이크로서비스 아키텍처와 비슷한 개념으로 마이크로프론트엔드는 프론트엔드에서도 마이크로서비스 아키텍처와 동일한 많은 이점을 얻을 수 있습니다.
팀의 자율성을 높이고 배포 위험을 줄이며 개발을 여러 팀에 나눠 진행하기 위해 마이크로프론트엔드를 채택할 수 있습니다. 각 팀마다 기술 스택, 배포 주기 및 워크플로우를 독자적으로 관리해도 사용자에게는 하나의 완성된 서비스로 보입니다.
전반적인 아이디어는 하나의 모놀리식 UI에서 벗어나 별도의 팀이 독립적으로 소유, 관리 및 배포할 수 있는 분리된 UI 코드베이스로 전환하는 것입니다.
마이크로프론트엔드를 가장 쉽게 이해하는 방법은 다음과 같습니다.
하나의 UI 요소를 다른 UI에 통합
무엇이 UI 요소가 될 수 있는지 궁금할 수 있습니다. 몇 가지 예시를 살펴보겠습니다.
- 페이지 – 특정 팀이 담당하는 웹사이트의 일부입니다. 예를 들어 Auth 팀은 로그인/회원가입 페이지를 마케팅 팀은 마케팅 페이지를 담당할 수 있습니다.
- 컴포넌트 – 헤더와 푸터 같은 컴포넌트도 마이크로프론트엔드의 좋은 후보입니다. 비교적 정적이지만 웹사이트 전반에 걸쳐 일관성을 유지해야 하며 서로 다른 페이지를 담당하는 팀과 연동될 수 있습니다.
- 위젯 – 예를 들어 추천 위젯은 추천 팀이 담당할 수 있으며 맥락에 따라 페이지의 다양한 위치에 통합될 수 있습니다. 이는 정적 컴포넌트와 다른데 추천 위젯은 맥락에 따라 API를 통해 관련 데이터를 직접 가져오기도 하기 때문입니다(해당 API 역시 추천 팀이 담당합니다).
전통적인 마이크로프론트엔드 패턴
마이크로프론트엔드의 정의를 읽고 나서 "요즘 (Google 같은 대형 기업을 제외하고) 누가 모놀리식 UI로 개발하지?" 라고 생각할 수도 있습니다. 그렇다면 여러분의 팀은 이미 다음과 같은 전통적인 마이크로프론트엔드 중 하나를 사용하고 있을 가능성이 높습니다.
서버 측 구성 (Server-Side Composition)
다양한 조직에서 가장 흔하게 접하는 방식입니다. 웹사이트를 라우트 패턴이나 페이지 단위로 분리하는 방식이 핵심입니다. 예를 들어 /account/*로 시작하는 모든 라우트(/account/login, /account/signup 등)는 계정 팀이 담당하는 모듈로 연결할 수 있습니다. 마찬가지로 마케팅 섹션이라면 /blog/*처럼 별도의 라우트 접두사를 붙일 수 있습니다.
이 방식은 일반적으로 리버스 프록시 레이어(예: NGINX)에서 구현하며 경로 매칭을 기반으로 트래픽을 적절한 하위 UI 서비스로 라우팅합니다.

iframes
또 다른 방식은 iframe을 사용하는 것인데 이 방법에는 단점이 적지 않습니다.
페이지 단위로 동작하는 서버 측 구성과 달리 iframe은 페이지 내부에 위젯 형태로 통합할 수 있습니다. <iframe> 태그를 사용하면 다른 웹사이트를 현재 웹사이트의 일부로 불러올 수 있습니다.

대표적인 예시로 트위터 피드나 구글 맵을 띄운 웹사이트를 들 수 있습니다. 이는 외부 위젯을 iframe으로 불러온 사례지만 기업 내부에서 특정 위젯을 iframe 기반으로 구성하는 경우도 있습니다.
빌드 타임 통합 - Packages
컴포넌트를 UI 라이브러리로 배포해 다른 애플리케이션에 제공하는 방식입니다.
여러 페이지나 위젯, 또는 헤더 및 푸터처럼 정적 컴포넌트로 이루어진 완전한 앱을 통합할 때 유용하며 실제로도 흔히 활용됩니다.
일반적으로 한 팀이 컴포넌트를 패키지로 배포하면 다른 팀이 해당 패키지의 특정 버전을 사용합니다.

Widget 컴포넌트는 앱의 의존성 설치 단계에서 가져옵니다. 웹 앱은 이 위젯을 자체 컴포넌트처럼 활용할 수 있으며 하나의 모듈로 함께 빌드되어 사용자에게 전달됩니다.
현대적인 마이크로프론트엔드 패턴
Module Federation
Module Federation을 사용하면 런타임에 호스트 애플리케이션 안으로 리모트 UI 조각을 불러올 수 있습니다. 전체 페이지, 위젯, 컴포넌트 등이 리모트 UI 조각이 될 수 있습니다.
Module Federation은 원래 Webpack 5의 기능으로 런타임에 원격 소스에서 JavaScript 코드를 불러오도록 번들러의 기능을 확장했습니다.
이를 발전시킨 버전인 Module Federation 2.0은 RSPack, Vite 등 다른 주요 번들러에서도 사용할 수 있습니다.
Webpack 5를 사용하고 있더라도 Module Federation 2.0을 권장합니다. 기존 Webpack 5 구현에 존재하는 몇 가지 흔한 문제들을 해결해주기 때문입니다.

Module Federation의 주요 구성 요소들을 이해하기 위해 예시를 하나 살펴보겠습니다.
블로그 애플리케이션은 콘텐츠 팀이 담당하고 위젯은 추천 팀이 담당한다고 가정해 봅시다.
이제 콘텐츠 팀이 자신들의 애플리케이션 안에 추천 위젯을 통합하고자 합니다. 두 팀은 서로 다른 도메인에 호스팅된 별도의 코드베이스를 가지고 있으며 콘텐츠 팀의 앱은 website.com에, 추천 팀의 앱은 recommendation.com에 배포되어 있습니다.
Module Federation으로 이 MFE(MicroFrontend) 통합을 달성하는 방법은 다음과 같습니다.
Remote
JavaScript 파일(예: 유틸리티, 컴포넌트 등)을 외부에 제공하는 remote 역할을 담당합니다.
이 예시에서는 추천 팀이 담당하는 위젯이 remote 역할을 맡으며 위젯을 외부에 제공하기 위한 설정이 필요합니다.
new ModuleFederationPlugin({
name: 'recommendation',
exposes: {
'./Widget': './src/Widget.js',
}
})Remote Entry
Remote entry는 remote의 진입점(entry point) URL입니다. 하나의 remote는 여러 JavaScript 파일을 외부에 제공할 수 있으며 remoteEntry 파일은 그 모든 파일을 파악하고 있습니다.
Module Federation은 기본적으로 remote entry 파일을 루트 경로에서 제공합니다. 이 예시에서는 추천 팀이 https://recommendation.com/remoteEntry.js에 remote entry를 제공할 수 있습니다.
Host
Remote Entry를 이용해 하나 이상의 remote로부터 JavaScript를 가져오는 독립적인 웹사이트입니다. remote entry는 특정 remote가 외부에 제공하는 컴포넌트, 유틸리티 등 여러 요소를 담은 앱의 네임스페이스라고 생각하면 이해하기 쉽습니다.
이 예시에서는 콘텐츠 팀이 Host 역할을 맡으며 remotes 설정 안에 추천 팀의 remote entry를 정의합니다.
new ModuleFederationPlugin({
name: 'content-blog',
remotes: {
"recommendation": 'recommendation@https://recommendation.com/remoteEntry.js',
},
// ... other configs
})Shared
Host와 remote 모두 SemVer 형식으로 의존성을 지정할 수 있으며 이는 런타임에 자동으로 조정되어 공유됩니다. React와 같이 싱글톤이어야 하는 공통 프레임워크 의존성이나 공유 가능한 서드파티 라이브러리 등이 이에 해당합니다.
shared를 올바르게 설정하면 remote에서 UI를 가져올 때 host에 이미 있는 라이브러리나 코드를 클라이언트가 중복으로 다운로드하는 것을 방지할 수 있으며 이는 Module Federation 성능 최적화의 핵심입니다.
const deps = require("./package.json").dependencies;
new ModuleFederationPlugin({
shared: {
...deps,
react: {
singleton: true,
requiredVersion: deps.react,
}
},
// ... other configs
})Imports and Usage
Module Federation을 사용하면 마치 JavaScript 파일이 로컬에 있는 것처럼 import해서 사용할 수 있습니다. remote entry와 필요한 의존성을 가져오는 작업은 Module Federation이 런타임에 내부적으로 처리합니다.
// Import is of the format - <remote>/<expose-from-remote>
import Widget from 'recommendation/Widget';
// Render somewhere, making sure to handle loading via Suspense
// & errors via error boundary in React
<ErrorBoundary>
<Suspense fallback={<Loading />}
<Widget />
</Suspense>
</ErrorBoundary>Module Federation을 간단하게 요약하면
런타임에 원격 서버에서 JavaScript 코드(컴포넌트, 유틸리티 등)를 가져오면서도 의존성을 공유하고 성능을 유지하는 것입니다.
Single SPA
마이크로프론트엔드를 검색하면 Single SPA가 인기 있는 솔루션으로 자주 등장합니다. 하지만 이 도구가 필요한 상황은 따로 있습니다. 바로 여러 프레임워크(예: React + Angular + Vue)로 구성된 컴포넌트를 하나의 애플리케이션에 통합하는 것입니다. 실제 동작 방식은 다음과 같습니다.
Single SPA는 URL 라우트에 따라 애플리케이션 전체를 마운트하거나 언마운트하는 JavaScript 라우터 역할을 합니다. 각각의 "single-spa application"은 프레임워크별로 구성된 앱으로 해당 라우트가 활성화될 때 로드됩니다.
// Register applications with Single SPA
registerApplication({
name: '@mycompany/react-app',
app: () => System.import('@mycompany/react-app'),
activeWhen: ['/react-app']
});
registerApplication({
name: '@mycompany/angular-app',
app: () => System.import('@mycompany/angular-app'),
activeWhen: ['/angular-app']
});상세 비교
| 기준 | Module Federation | 서버 측 구성 | iframe | 빌드 타임 통합 (Package) |
|---|---|---|---|---|
| 독립적 배포 | 💚 마이크로프론트엔드는 클라이언트에서 런타임에 로드됩니다. 각 팀이 독립적으로 배포할 수 있으며 변경 사항이 즉시 반영됩니다. | 💚 각 라우트 패턴이 개별 앱의 배포 주소를 가리키므로 배포는 독립적으로 유지됩니다. | 💚 iframe도 런타임에 로드되므로 독립적인 배포가 가능합니다. | 💔 배포가 호스트 애플리케이션에 종속됩니다. 패키지에 변경이 생기면 새 버전을 배포하고 호스트 앱에서 버전을 올려야 합니다. |
| 성능 | 💚 의존성 공유와 최적화된 로딩을 지원하여 SPA 성능을 유지할 수 있습니다. | 💔 애플리케이션 간 이동 시 전체 페이지를 다시 로드해야 하므로 SPA의 이점을 살릴 수 없습니다. | 💔 완전히 격리된 환경에서 모든 의존성을 별도로 불러오므로 전체적인 페이지 로딩 속도가 느려집니다. | 💚 패키지 통합 시 의존성 중복 제거(deduping)를 통해 어느 정도 공유할 수 있지만 적절한 개발 도구가 필요합니다. 그렇지 않으면 동일한 의존성이 중복으로 포함될 수 있습니다. |
| 확장성 및 유지보수 | 💚 대규모 환경에서도 잘 동작합니다. 페이지 전체를 페더레이션 컴포넌트로 구성할 수 있으며 가장 작은 단위의 컴포넌트도 서로 다른 리모트에서 가져올 수 있습니다. | 💔 동일한 앱처럼 보이게 하려면 헤더/푸터 등을 중복으로 구현해야 하는 경우가 많습니다. 또한 앱 진입점을 라우트 기반으로만 분리할 수 있어 세밀하게 조합하기가 어렵습니다. | 💔 페이지 전체를 구성하는 데는 적합하지만 일부만 구성하기에는 한계가 있습니다. 규모가 커질수록 앱이 느려지고 SEO 최적화나 반응형 레이아웃 구성 시 문제가 생길 수 있습니다. | 💔 규모가 커질수록 패키지 배포 관리, 업그레이드, 버전 충돌 문제가 발생합니다. CI 도구로 어느 정도 완화할 수 있지만 버전 업그레이드 및 기능, 성능 영향 검증에 여전히 손이 많이 갑니다. |
| 초기 설정 난이도 | 💔 현재 앱을 어떻게 구성하고 있느냐에 따라 초기 설정 비용이 높을 수 있습니다. Module Federation을 원하는 대로 세팅하고 싶거나 문제가 발생했을 때 빌드 도구에 대한 깊은 이해가 필요할 수 있습니다. 자세한 내용은 다음 섹션에서 다룹니다. | 💚 리버스 프록시 레이어 외에 별다른 결합이 없어 구현이 비교적 간단합니다. 리버스 프록시가 트래픽을 각 서비스로 적절히 라우팅하는 역할을 담당합니다. | 💔 통합 자체는 비교적 쉽지만 처리해야 할 엣지 케이스가 많아 생각보다 시간이 오래 걸릴 수 있습니다. 대표적인 예시로는 iframe과 호스트 앱 간 통신, 레이아웃 문제, iframe 경계를 벗어나는 렌더링(예: 토스트), 크로스 도메인 제한과 보안, SEO 및 접근성(a11y) 문제 등이 있습니다. | 💔 새 패키지 배포 파이프라인 구축, 변경 이력 관리, 버전 업그레이드, 버전 충돌 해결에 상당한 노력이 필요합니다. 패키지를 변경할 때마다 호스트 앱의 전이적 의존성(간접적으로 설치되는 패키지)에 의도치 않은 영향이 없는지 확인해야 합니다. |
| 인증 및 권한 | 💚 앱 설정에 따라 Module Federation 컴포넌트가 데이터 페칭을 위해 리모트 앱의 서버를 직접 호출할 수 있습니다. 리모트가 호스트와 다른 도메인에 있는 경우 CORS 처리와 인증 쿠키 전송 설정이 필요할 수 있습니다. | 💚 각 앱이 중앙 인증 서비스와 독립적으로 연동할 수 있습니다. | 💔 iframe에서 부모 웹사이트의 인증 쿠키 등 브라우저 정보에 접근하기 어려울 수 있습니다. 특히 iframe URL이 애플리케이션과 다른 도메인/서브도메인에 있는 경우 인증 처리를 위한 별도의 우회 방법이 필요할 수 있습니다. | 💚 패키지 컴포넌트는 호스트 애플리케이션 내 프록시 API를 통해 호출하거나 독립 서비스의 API를 직접 호출하는 방식 중 선택할 수 있습니다. |
| devloop | 💚 Module Federation 2.0은 성숙한 개발 환경을 제공합니다. 여러 앱에 걸쳐 소스맵과 핫 리로딩(HMR)을 지원하며 기본적으로 원활한 통합 경험을 제공합니다. 로컬에서 리모트 앱 주소를 자유롭게 지정해 실제 환경처럼 테스트할 수도 있습니다. | 💔 두 서비스가 제대로 연결되는지 확인하려면 두 서비스와 리버스 프록시를 로컬에서 모두 실행해야 하며 설정이 까다로울 수 있습니다. | 💔 로컬 테스트만으로는 크로스 도메인 환경에서 발생할 수 있는 iframe 관련 문제를 정확히 재현하기 어렵습니다. | 💔 개발 중인 패키지 변경 사항을 호스트 앱에서 로컬로 테스트하려면 별도의 개발 워크플로우가 필요합니다. 일반적으로 패키지를 사전 배포하거나, 로컬 패키지를 링크하거나, yalc 같은 도구를 활용하는 방식으로 처리합니다. |
| 종합 추천 ✨ | 독립적인 배포와 릴리스 주기를 원하는 여러 팀이 낮은 결합도로 협업하는 앱에 적합합니다. | 더 큰 비즈니스 도메인 안에서 비교적 독립적으로 운영되는 앱(서브도메인 단위)에 적합합니다. "사용자가 각 앱을 얼마나 자주 오가는가?"를 생각해보세요. 자주 오가지 않는다면 적합한 방식입니다. | 여러 제약으로 인해 권장하지 않습니다. 다만 서드파티 통합에는 어울릴 수 있습니다. Twitter가 피드 일부를 iframe으로 외부 사이트에 제공하는 방식이 대표적인 예인데, 다른 방식보다 훨씬 간편합니다. | 변경사항을 꼼꼼하게 통제해야 하는 앱에 적합합니다. 호스트 앱이 패키지를 직접 업그레이드하고, 사용자에게 릴리스하기 전에 충분히 검증할 수 있습니다. |
Module Federation 도입 시 주의할 점
Module Federation을 사용할 때 가장 큰 트레이드오프는 초기 설정 비용입니다. 이는 앞선 비교표에서 간략히 다룬 바 있습니다.
Module Federation으로 통합할 때 예상할 수 있는 다른 어려운 점은 다음과 같습니다.
설정 복잡도
- 번들러 별 고려사항 - 앱에 따라 Module Federation을 제대로 세팅하려면 번들러가 내부적으로 어떻게 동작하는지 알아야 할 수 있습니다. 예를 들어 Webpack 5에서 리모트가 페더레이션 컴포넌트를 외부에 제공하면서 동시에 자체적인 화면도 렌더링한다면 이를 위한 적절한 청크 설정이 필요합니다. Module Federation은 기본적으로 특정 청크 최적화 전략을 전제로 하며 remoteEntry를 앱의 루트에서 노출하기 때문입니다.
- 공유 의존성 - 번들 크기와 로딩 성능을 최적화하려면 공유할 수 있는 의존성을 파악해 최대한 많이 공유해야 합니다. 또한 런타임에서 발생할 충돌을 막기 위해 React 같은 핵심 라이브러리는 싱글톤으로 설정해야 합니다.
런타임 고려사항
- 크로스 도메인 - 리모트가
remote.website.com처럼host.website.com과 다른 서브도메인에 배포되어 있다면 호스트 안에서 리모트 컴포넌트가 데이터를 페칭할 때 CORS 문제가 생길 수 있어 서버에 적절한 CORS 설정이 필요합니다. 또한 리모트로 요청할 때 브라우저가 인증 쿠키를 함께 전송하도록 fetch의credentials설정도 맞춰야 합니다. - 스타일 충돌 - 리모트 앱의 스타일이 호스트 앱의 스타일을 덮어쓰거나 반대로 리모트 앱의 컴포넌트가 호스트의 스타일을 의도치 않게 상속받지 않도록 해야 합니다. styled-components 사용이나 virtual DOM 활용 등 여러 방법이 있습니다.
운영 시 고려사항
- 모니터링 및 분석 - 요구사항에 따라 에러 모니터링 서비스 같은 모니터링 스크립트를 공유 인스턴스로 사용할지 아니면 마이크로프론트엔드 컨텍스트 안에서 별도로 실행할지 결정해야 합니다. 리모트에서는
index파일이 렌더링되는 게 아니라 컴포넌트가 노출되는 방식이라 이 부분이 까다로울 수 있습니다. - 배포 및 캐싱 - 마이크로프론트엔드 리모트 번들은 안정성을 위해 서버에서 직접 제공하기보다 S3 버킷에 호스팅하는 것을 권장합니다. 또한
remoteEntry.js를 제외한 파일에는 장기 캐싱을 적용하는 것이 좋습니다.remoteEntry.js의 파일 이름 뒤에는 일반적으로 해시가 붙지 않으며 로드할 다른 의존성의 링크를 담고 있습니다.
결론
마이크로프론트엔드는 여러 팀이 함께 프론트엔드를 개발할 때 확장성 있는 해결책이며 그 중에서도 Module Federation이 가장 유연한 현대적 방식으로 자리잡고 있습니다.
서버 측 구성 같은 전통적인 방식도 특정 상황에서는 여전히 유효하지만 Module Federation은 복잡한 애플리케이션에 필요한 런타임 유연성과 성능을 제공합니다.
최종 선택은 팀 구조, 기술적 요구사항, 그리고 복잡한 구현을 감수할 수 있는지에 따라 달라집니다. 마이크로프론트엔드가 처음이라면 더 단순한 방식부터 시작하고 필요에 따라 Module Federation으로 나아가는 것을 고려해보세요.
다음 단계
이번 게시글에서는 전체적인 그림을 그리는 데 초점을 맞췄습니다. 다음에는 Module Federation을 더 깊이 다루며 기술적으로 어려운 점과 해결 방법을 자세히 살펴볼 예정입니다. 기대해주세요!