
Host (React 19)
├─ React DOM 트리
└─ App 컴포넌트
└─ Remote 컴포넌트 (React 18 or 19 인스턴스 사용) ❌
- Host는 React 19 인스턴스 사용
- Remote는 React 18,19 각 인스턴스를 사용하는 케이스
- React DOM tree에 다른 인스턴스 element를 넣으려고 해서 에러 발생
(multiple copies of the 'react' package is used)
Host (React 19)
├─ React DOM 트리
└─ App 컴포넌트
└─ Remote 컴포넌트 (React 19 인스턴스 사용) ❌
- Host와 Remote가 각자 React 19 포함
- 결국 두 개의 React 인스턴스 존재 → 마찬가지로 React DOM tree 에러 발생
(multiple copies of the 'react' package is used)
Shared React 19 (singleton)
├─ Host
└─ Remote
- Host와 Remote가 같은 React 인스턴스 사용
- React DOM tree에 문제 없이 렌더링 가능 ✅
“위 테스트를 통해 React는 싱글턴으로 관리되어야 한다는 것을 확인했으며, 다음으로 Shared 없이도 충돌을 제거할 수 있는 방법을 실험”
MFE 환경에서 Host React 19와 Remote React 16/18/19가 섞여 있을 때
일반적인 React 컴포넌트를 Host DOM Tree 안에 직접 렌더링하면 multiple React copies 에러 발생
충돌을 없애기 위해 Shared 설정 없이 mount/unmount 방식으로 Remote를 DOM 컨테이너 안에 렌더링하여 충돌을 제거하는 방식을 채택해 보았다.
Remote 앱을 단순 함수로 mount/unmount 제공
import { createRoot, type Root } from 'react-dom/client'
import App from './App.tsx'
import './App.css'
import './index.css'
let reactRoot: Root | null = null
export function mount(element: HTMLElement, props: Record<string, unknown> = {}) {
console.log('🔥 Mounting React 19 app with props:', props)
if (reactRoot) {
console.warn('⚠️ React 19 app already mounted, unmounting first...')
unmount(element)
}
// Create React 19 root and render
reactRoot = createRoot(element)
reactRoot.render(<App {...props} />)
console.log('✅ React 19 app mounted successfully')
}
export function unmount(element: HTMLElement) {
console.log('🧹 Unmounting React 19 app...')
if (reactRoot) {
reactRoot.unmount()
reactRoot = null
console.log('✅ React 19 app unmounted successfully')
}
// Clear the container
element.innerHTML = ''
}
// For backwards compatibility, also export as default
export default {
mount,
unmount
}
Host는 Remote를 독립 컨테이너(div) 안에서 mount
Remote React 인스턴스는 Host DOM Tree와 직접 섞이지 않음
import { useFederation } from '@/shared/context/federationContext.tsx'
import { useEffect, useRef } from 'react'
export const RemoteReact19 = () => {
const federation = useFederation()
const ref = useRef<HTMLDivElement>(null)
useEffect(() => {
let isMounted = false
let mount: ((el: HTMLElement, props?: any) => void) | undefined
let unmount: ((el: HTMLElement) => void) | undefined
const loadRemoteModule = async () => {
try {
console.log('🔄 Loading React 19 remote module...')
const remoteModule = await federation.loadRemote('mf2/mount') as {
mount: (el: HTMLElement, props?: any) => void
unmount: (el: HTMLElement) => void
}
mount = remoteModule.mount
unmount = remoteModule.unmount
if (ref.current && mount) {
console.log('🔥 Mounting React 19 app...')
mount(ref.current, {
message: 'Hello from Host!',
timestamp: new Date().toLocaleString()
})
isMounted = true
console.log('✅ React 19 app mounted successfully')
}
} catch (error) {
console.error('❌ Failed to load React 19 remote module:', error)
}
}
loadRemoteModule()
return () => {
if (unmount && ref.current && isMounted) {
console.log('🧹 Cleaning up React 19 app...')
unmount(ref.current)
}
}
}, [federation])
return (
<div style={{
padding: '20px',
color: 'var(--text-primary)',
backgroundColor: 'var(--bg-primary)'
}}>
<h1 style={{
color: 'var(--text-primary)',
marginBottom: '20px',
fontSize: '24px',
fontWeight: 'bold'
}}>React 19 페이지 영역</h1>
<div ref={ref}></div>
</div>
)
}
Host DOM 안에 Remote DOM을 마운트하지만, React Fiber Tree 단위에서 Host와 Remote가 독립적이므로 충돌이 없음.
(Fiber Tree: React 내부에서 컴포넌트 렌더링과 업데이트를 관리하는 자료구조)
Host React Fiber Tree (React 19)
┌───────────────────────────────────────────────────────────┐
│ <RemoteLoader /> (Host Component) │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ <div ref={domRef}> (Host DOM Container) │ │
│ │ │ │
│ │ [ Mounting Point ] │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ Remote React Fiber Tree (React 18 or 19) │ │
│ │ ┌───────────────────────────────────────────────┐ │ │
│ │ │ <RemoteApp /> │ │ │
│ │ │ │ │ │
│ │ │ • Independent State / Effect │ │ │
│ │ │ • Own React Runtime │ │ │
│ │ └───────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────┘ │
└───────────────────────────────────────────────────────────┘
mount/unmount 컨테이너를 독립적으로 관리 → 여러 Remote React 인스턴스 공존 가능