Skip to main content

Chapter 2. 핵심 개념과 동작 원리

이 장에서는 Module Federation을 기준으로, 프론트엔드 개발자가 반드시 이해해야 하는 핵심 개념과 실제 동작 방식을 정리한다. 중요한 포인트는 서로 다른 애플리케이션이 빌드 타임이 아니라 런타임에 연결된다는 점이다. 이 특성 때문에 유연한 배포가 가능해지지만, 동시에 네트워크·버전·로딩 순서·장애 대응까지 함께 고려해야 한다.


2.1 주요 개념 정리

개념설명
Host다른 애플리케이션이 제공하는 모듈을 가져다 사용하는 쪽이다. 보통 메인 애플리케이션이 host 역할을 한다.
Remote자신이 가진 컴포넌트나 모듈을 외부에 노출하는 쪽이다. 예를 들어 공통 UI, 특정 도메인 기능, 별도 팀이 운영하는 앱이 remote가 될 수 있다.
Exposeremote가 외부에 공개할 모듈 목록이다. 예: Button, Header, useAuth 같은 컴포넌트/훅/유틸을 노출할 수 있다.
Sharedhost와 remote가 함께 사용할 의존성이다. 대표적으로 react, react-dom, 상태관리 라이브러리 등이 있다. 중복 로딩 방지와 런타임 일관성을 위해 중요하다.
Remote Entryremote가 어떤 모듈을 expose했고, shared dependency를 어떻게 다룰지 담고 있는 진입 파일이다. host는 이 파일을 먼저 로드해 remote의 정보를 파악한다. 일반적으로 remoteEntry.js 형태다.

개념 관계 한눈에 보기

  • host는 remote를 소비한다.
  • remote는 expose된 모듈을 제공한다.
  • remoteEntry는 remote의 메타데이터와 진입점 역할을 한다.
  • shared는 host와 remote가 같은 라이브러리를 중복 없이 쓰도록 협상한다.

2.2 로딩 흐름 이해

Module Federation의 핵심은 “필요한 순간에 원격 모듈을 가져와 실행한다”는 점이다. 전체 흐름은 다음과 같다.

1) host가 remoteEntry를 로드한다

사용자가 특정 화면에 진입하거나 특정 기능이 필요해지면, host는 먼저 remote의 remoteEntry.js를 네트워크로 가져온다. 이 단계에서 remote가 어떤 모듈을 노출하는지, shared dependency를 어떻게 선언했는지 확인할 수 있다.

2) remote 모듈을 탐색한다

host는 remoteEntry를 통해 "./Header", "./ProductCard"처럼 expose된 모듈 키를 조회한다. 즉, host는 remote 내부 파일 구조를 직접 아는 것이 아니라, 노출된 계약(contract) 을 기준으로 접근한다.

3) shared dependency를 협상한다

모듈을 실제로 실행하기 전에, 런타임은 host와 remote가 선언한 shared dependency를 비교한다. 이 과정에서 다음을 판단한다.

  • 이미 로드된 라이브러리를 재사용할 수 있는가
  • 버전 조건이 맞는가
  • singleton 정책을 지켜야 하는가
  • 즉시 로드할지, 필요 시점에 로드할지

4) 실제 컴포넌트/모듈을 실행한다

협상이 끝나면 remote가 제공하는 모듈 팩토리가 로드되고, 그 결과 실제 컴포넌트나 함수가 실행된다. 이 시점부터는 host 입장에서는 로컬 모듈처럼 사용할 수 있지만, 실제로는 네트워크와 런타임 초기화를 거친 결과물이다.


2.3 Shared Dependency 동작 방식

shared 설정은 Module Federation에서 가장 중요한 부분 중 하나다. 잘못 설정하면 React가 중복 로딩되거나, 버전 충돌이 발생하거나, 런타임 오류가 생길 수 있다.

singleton

singleton: true는 해당 라이브러리를 애플리케이션 전체에서 하나의 인스턴스만 사용하겠다는 의미다.

대표 사례:

  • react
  • react-dom
  • styled-components 일부 환경
  • 전역 컨텍스트에 민감한 라이브러리

React는 singleton으로 다루는 것이 사실상 필수에 가깝다. 중복 로딩되면 Hook 동작 이상, Context 불일치, Invalid hook call 같은 문제가 발생할 수 있다.

version matching

host와 remote가 같은 라이브러리를 shared로 선언하더라도, 실제 버전이 다를 수 있다. 런타임은 선언된 버전 범위를 비교해 어떤 버전을 사용할지 결정한다.

예를 들어:

  • host는 react@18.2.0
  • remote는 react@^18.0.0

이 경우 보통 호환 가능하므로 하나를 재사용할 수 있다. 반대로 메이저 버전 차이가 크면 협상에 실패하거나 예상치 못한 동작이 생길 수 있다.

eager / lazy

공유 라이브러리를 언제 로드할지 정하는 옵션이다.

  • eager

    • 초기에 즉시 로드한다.
    • 초기 번들 크기가 커질 수 있다.
    • 대신 특정 시점의 지연 로딩 복잡도를 줄일 수 있다.
  • lazy

    • 실제 필요할 때 로드한다.
    • 초기 로딩은 가벼워질 수 있다.
    • 대신 런타임 시점 네트워크 요청과 초기화 순서를 고려해야 한다.

대부분의 경우 lazy 전략이 유연하지만, 일부 핵심 라이브러리는 eager가 더 단순할 수 있다.

strictVersion

strictVersion: true는 선언한 버전 조건을 엄격히 강제하겠다는 의미다. 버전이 맞지 않으면 재사용하지 않거나, 런타임 에러로 이어질 수 있다.

이 옵션은 안정성을 높여주지만, 독립 배포 환경에서는 운영 부담도 커진다. 즉, 실수 조기 발견에는 좋지만 배포 유연성은 줄어들 수 있다.


2.4 런타임 특성

Module Federation은 전통적인 번들링 방식과 다르게, 여러 특성을 가진다.

빌드 타임이 아닌 런타임 결합

일반적인 프론트엔드 앱은 빌드 시점에 필요한 모듈이 모두 결정된다. 반면 Module Federation은 실행 시점에 원격 모듈을 가져와 결합한다.

장점:

  • 독립 배포 가능
  • 팀 간 개발 분리
  • 기능 단위 확장에 유리

단점:

  • 빌드 시점에 모든 문제를 검증하기 어렵다
  • 런타임 실패 가능성이 커진다

네트워크 실패 가능성

원격 모듈은 네트워크 요청으로 가져오기 때문에 다음 문제가 생길 수 있다.

  • remoteEntry.js 다운로드 실패
  • 청크 파일 404
  • CDN 장애
  • CORS 설정 문제
  • 타임아웃 또는 느린 응답

즉, 로컬 import처럼 보이지만 실제로는 외부 리소스 의존이다.

로딩 순서 이슈

shared dependency 협상과 remote 초기화는 순서에 민감하다. 특히 여러 remote를 동시에 로드하거나, 서로 다른 shared 설정이 섞이면 순서에 따라 결과가 달라질 수 있다.

예를 들어:

  • React가 먼저 공유 스코프에 등록되지 않았는데 remote가 먼저 실행됨
  • 서로 다른 remote가 다른 버전 정책을 선언함
  • eager/shared 초기화가 꼬여 예기치 않은 버전이 선택됨

캐시와 무효화 고려

remoteEntry.js와 실제 청크 파일은 캐시 전략이 매우 중요하다.

주의할 점:

  • remoteEntry.js가 오래 캐시되면 최신 expose 정보를 못 가져올 수 있다.
  • remoteEntry.js는 최신인데 하위 청크가 구버전이면 불일치가 발생할 수 있다.
  • 배포 시점에 캐시 무효화 전략이 없으면 런타임 장애로 이어질 수 있다.

따라서 보통은:

  • remoteEntry.js는 짧은 캐시 또는 no-cache
  • 청크 파일은 content hash 기반 장기 캐시 전략을 함께 설계한다.

2.5 개발자가 반드시 이해해야 할 제약

Module Federation은 강력하지만, “그냥 import만 분리한 기술”로 보면 위험하다. 다음 제약을 반드시 이해해야 한다.

React 중복 로딩 문제

가장 대표적인 문제다. host와 remote가 각각 다른 React 인스턴스를 사용하면 다음 문제가 생길 수 있다.

  • Hook 호출 오류
  • Context 값 공유 실패
  • 컴포넌트 트리 일관성 붕괴
  • 라이브러리 내부 상태 오작동

따라서 react, react-dom은 대체로 shared + singleton으로 관리해야 한다.

전역 상태 공유 난이도

Redux, Zustand, Recoil 같은 상태를 여러 앱 간에 공유하려면 생각보다 복잡하다.

이유:

  • 서로 다른 배포 단위를 하나의 상태 저장소처럼 다뤄야 함
  • 초기화 순서와 생명주기가 다름
  • 상태 변경 책임이 여러 앱에 분산됨
  • 디버깅 포인트가 늘어남

그래서 전역 상태를 완전히 공유하기보다는,

  • 이벤트 기반 통신
  • 명시적인 API 계약
  • 최소한의 공유 상태만 유지 전략이 더 현실적인 경우가 많다.

타입 안정성 한계

TypeScript를 쓰더라도 host와 remote가 서로 독립 배포되면 타입 안정성이 완전하지 않다.

문제 포인트:

  • host가 기대하는 타입과 remote의 실제 구현이 다를 수 있음
  • 배포 시점이 다르면 계약이 어긋날 수 있음
  • 빌드 성공이 런타임 안전을 보장하지 않음

즉, 타입 선언은 도움이 되지만 런타임 계약 보장 장치는 아니다. 실무에서는 다음 보완책이 필요하다.

  • 공용 타입 패키지 운영
  • expose API 최소화
  • 계약 변경 시 버전 정책 명확화
  • 통합 테스트/스모크 테스트 추가

런타임 장애가 UI로 전파될 수 있음

원격 모듈 로드 실패는 곧 UI 오류로 이어질 수 있다. 예를 들어 헤더, 결제 버튼, 상품 카드 같은 핵심 UI가 remote라면, 해당 모듈 실패가 곧 사용자 화면 장애가 된다.

따라서 다음이 필요하다.

  • Error Boundary 적용
  • 로딩 fallback UI 제공
  • remote 로드 실패 시 대체 화면 설계
  • 모니터링 및 알림 체계 구축
  • 재시도 전략 또는 graceful degradation 설계

2.6 정리

Module Federation의 핵심은 다음 네 가지로 요약할 수 있다.

  1. host는 remote의 remoteEntry를 런타임에 로드해 필요한 모듈을 가져온다.
  2. shared dependency는 단순 중복 제거가 아니라, 버전·싱글톤·로딩 시점까지 포함한 협상 과정이다.
  3. 이 구조는 독립 배포에 강하지만, 네트워크·캐시·로딩 순서·런타임 장애를 함께 관리해야 한다.
  4. 특히 React 중복 로딩, 전역 상태 공유, 타입 안정성, 장애 전파 문제는 반드시 실무 관점에서 대비해야 한다.

문서에 바로 넣기 좋은 한 문단 요약

Module Federation은 여러 프론트엔드 애플리케이션이 런타임에 서로의 모듈을 가져와 사용할 수 있게 해주는 구조다. 이때 host는 remote의 remoteEntry를 먼저 로드해 expose된 모듈 정보를 확인하고, shared dependency에 대한 협상을 거친 뒤 실제 컴포넌트를 실행한다. 이 방식은 독립 배포와 팀 분리에 유리하지만, React 중복 로딩, shared 버전 충돌, 네트워크 실패, 캐시 불일치, 타입 안정성 한계, 런타임 장애 전파 같은 문제를 함께 고려해야 한다. 따라서 Module Federation은 단순한 코드 분리 기술이 아니라, 런타임 통합 아키텍처로 이해해야 한다.

원하면 이 내용을 이어서 “Chapter 2를 문서체로 더 다듬은 버전” 또는 “예제 코드까지 포함한 버전”으로 재작성해주겠다.