728x90
SMALL
들어가며
MF(Module Federation)은 Webpack 5의 핵심 기능으로, 여러 독립적인 애플리케이션 간에 코드와 리소스를 실시간으로 공유할 수 있게 해주는 기술입니다. 이 기술은 마이크로프론트엔드 아키텍처를 구현하는 데 매우 효과적인 도구이며, 대규모 프론트엔드 애플리케이션을 더 작고 관리하기 쉬운 단위로 분할할 수 있게 해줍니다.
이에 대한 자세한 설명은 Micro Frontends와 MSA를 위한 Module Federation 글을 참고하시면 이해에 도움이 되실 것 같습니다.
문제 상황
마이크로프론트엔드 환경에서 MF를 사용하면서 다음과 같은 실제 문제들을 경험했습니다:
- 로컬 개발 시 연결된 모든 서비스를 실행해야 하는 번거로움
- 원격 서버 장애 발생 시 연관된 서비스까지 중단되는 문제
- 컴포넌트 로딩 실패에 대한 적절한 폴백(fallback) 처리 부재
이러한 문제들을 해결하기 위해 안전한 동적 컴포넌트 로딩 유틸리티를 구현하게 되었습니다. 이 글에서는 이러한 문제들을 해결하기 위한 안전하게 컴포넌트를 불러오는 방법을 소개합니다.
컴포넌트 구현하기
다음은 안전한 컴포넌트 로딩을 위한 유틸리티 함수입니다. 이 구현은 TypeScript의 타입 안전성과 React의 지연 로딩 기능을 결합하여 안정적인 컴포넌트 로딩을 제공합니다.
import React from 'react';
const IS_PRODUCTION = process.env.NODE_ENV === 'production';
/**
* 복잡한 보일러플레이트 없이 컴포넌트를 안전하게 동적 임포트하기 위한 유틸리티
* @param module 동적으로 불러올 모듈을 반환하는 함수
* @param moduleName 모듈 내 특정 컴포넌트 이름 (기본값: 'default')
*/
export function safeFederatedComponent<
Module extends Record<string, any>,
ModuleName extends keyof Module = 'default',
>(
module: () => Promise<Module>,
moduleName: ModuleName = 'default' as ModuleName
) {
return React.lazy(async () => {
try {
const result = await module();
const component = result[moduleName];
return { default: component };
} catch (e) {
return {
default: (() => {
if (IS_PRODUCTION) {
return <div />;
}
console.error(
`Failure of dynamic import: ${module}`,
`Module name: ${String(moduleName)}`
);
return (
<div
data-debug={`Failure of dynamic import: ${module}`}
data-debug-module-name={moduleName}
/>
);
}) as Module[ModuleName],
};
}
});
}
타입 시스템 설명
Module extends Record<string, any>
: 임포트할 모듈의 타입을 정의합니다. 모든 키-값 쌍을 허용하되, 타입 안전성을 제공합니다.ModuleName extends keyof Module
: 모듈 내에서 실제로 존재하는 컴포넌트 이름만 허용합니다.default as ModuleName
: 모듈명을 지정하지 않을 경우 기본값으로 'default' 사용합니다.
코드 동작 원리
단계 | 주요 기능 | 세부 내용 |
---|---|---|
1. 초기화 | 컴포넌트 준비 | • React.lazy를 통한 지연 로딩 컴포넌트 래핑 • 제네릭 타입 파라미터를 통한 타입 정보 설정 • 비동기 로딩을 위한 Promise 기반 함수 준비 |
2. 로딩 프로세스 | 모듈 가져오기 | • 원격 모듈에 대한 동적 import 시도 • 성공 시 지정된 컴포넌트 추출 • React.lazy 형식에 맞춰 default export 변환 |
3. 에러 핸들링 | 안전성 확보 | • 환경별 대체 UI 생성 (프로덕션: 빈 div, 개발: 디버그 정보) • 개발 환경에서 상세 에러 로깅 |
사용 예시
// 원격 컴포넌트 로딩
const RemoteComponent = safeFederatedComponent(() => import('remote-app/Component'));
// 특정 컴포넌트 이름으로 로딩
const NamedComponent = safeFederatedComponent(
() => import('remote-app/Components'),
'SpecificComponent'
);
// 사용
function App() {
return (
<Suspense fallback={<Loading />}>
<RemoteComponent />
</Suspense>
);
}
실무 적용 시 이점
- 운영 안정성
- 원격 서비스 장애 시에도 전체 서비스 가용성 유지
- 점진적 성능 저하로 사용자 경험 보호
- 팀 생산성
- 독립적인 개발 환경 구성 가능
- 마이크로프론트엔드 애플리케이션 간 의존성 최소화
- 유지보수성
- 명확한 에러 추적으로 신속한 문제 해결
- 타입 시스템을 통한 리팩토링 안정성
마치며
MF 환경에서 이 유틸리티를 사용함으로써, 개발 환경에서의 생산성 향상, DX(Developer Experience) 그리고 프로덕션 환경에서의 안정성 확보라는 두 가지 목표를 모두 달성할 수 있었습니다.
'Frontend > Module Federation' 카테고리의 다른 글
BroadcastChannel API란? (2) | 2024.11.10 |
---|---|
Micro Frontends와 MSA를 위한 Module Federation (2) | 2024.01.07 |