Module Federation의 의존성 Singleton과 버전관리 성능이슈는 어떻게 이뤄질까?
Module Federation(이하 MF)을 도입할 때 가장 먼저 마주하는 벽은 의존성(Dependency) 관리 입니다.
Host와 Remote가 서로 다른 버전의 라이브러리를 사용하고 있다면 MF는 어떤 결정을 내릴까요? "React 18.3.1(Host) 위에 React 18.2.0(Remote)을 띄우는 상황" 을 가정하고 시뮬레이션을 돌려봅시다.
package.json이나 webpack.config.js에서 singleton: true를 설정한다는 것은 다음과 같은 강력한 선언입니다.
"이 라이브러리는 브라우저 탭 전체에서 **딱 하나(Single Instance)**만 존재해야 해!"
React는 내부적으로 전역 상태나 Hooks 로직을 관리합니다. 만약 한 페이지에 두 개의 React 인스턴스가 로드되면 다음과 같은 치명적인 문제가 발생합니다.
singleton: true가 설정되어 있을 때, MF는 "새로운 의존성 다운로드"를 극도로 꺼리는 모드로 진입합니다.
Host(18.3.1)가 이미 로드된 상태에서 Remote(18.2.0)가 진입할 때의 판단 로직은 다음과 같습니다.
MF 런타임이 질문합니다. "누가 이미 React를 로드했나?"
👉 Host가 이미 18.3.1 버전을 로드해서 들고 있음.
"Remote 너는 무슨 버전을 원해?"
👉 Remote: "나는 18.2.0이 필요해."
MF는 유의적 버전(Semver) 규칙에 따라 호환성을 체크합니다.
결론: 18.3.1은 18.2.0의 기능을 모두 포함하므로 호환된다.
"굳이 18.2.0을 또 받을 필요 없다. Host가 가진 18.3.1을 같이 쓰자!"
👉 네트워크 요청을 아끼고 메모리를 절약합니다.
만약 Host는 "하나만 쓰자(true)"고 하는데, Remote가 "나는 내 거 따로 쓸래(false)"라고 설정하면 어떻게 될까요?
의존성을 소비하는 쪽(Remote)의 설정이 우선시됩니다.
singleton: true 선언. "웬만하면 내 거 써라."singleton: false 선언.lodash 같은 유틸리티는 가능합니다.)shared에 명시하지 않는다면?webpack.config.js의 shared 옵션에 라이브러리를 아예 적지 않는 경우는 어떨까요?
이는 MF의 버전 관리 및 공유 기능을 사용하지 않겠다는 의미입니다.
// webpack.config.js
new ModuleFederationPlugin({
name: 'app1',
// shared에 lodash를 넣지 않음
shared: {
react: { singleton: true },
// lodash 생략
},
});
반드시 그렇지는 않습니다. "격리" 와 "최적화" 관점에서 장단점이 있습니다.
shared로 묶으면 웹팩은 라이브러리 전체를 공유 덩어리로 만드는 경향이 있습니다. 반면, 공유하지 않으면 각 앱이 자기가 쓰는 함수만 쏙쏙 뽑아서(Tree Shaking) 번들링하므로, 사이즈가 작은 라이브러리는 공유하지 않는 게 오히려 용량이 작을 수 있습니다.React, ReactDOM 처럼 거대한 라이브러리를 중복해서 받으면 초기 로딩 속도가 느려집니다.shared + singleton: true로 관리하세요.shared에서 빼서 각자 번들링하는 것이 더 효율적일 수 있습니다.