728x90
SMALL

들어가며

MF(Module Federation)은 Webpack 5의 핵심 기능으로, 여러 독립적인 애플리케이션 간에 코드와 리소스를 실시간으로 공유할 수 있게 해주는 기술입니다. 이 기술은 마이크로프론트엔드 아키텍처를 구현하는 데 매우 효과적인 도구이며, 대규모 프론트엔드 애플리케이션을 더 작고 관리하기 쉬운 단위로 분할할 수 있게 해줍니다.

이에 대한 자세한 설명은 Micro Frontends와 MSA를 위한 Module Federation 글을 참고하시면 이해에 도움이 되실 것 같습니다.

문제 상황

마이크로프론트엔드 환경에서 MF를 사용하면서 다음과 같은 실제 문제들을 경험했습니다:

  1. 로컬 개발 시 연결된 모든 서비스를 실행해야 하는 번거로움
  2. 원격 서버 장애 발생 시 연관된 서비스까지 중단되는 문제
  3. 컴포넌트 로딩 실패에 대한 적절한 폴백(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>
);
}

실무 적용 시 이점

  1. 운영 안정성
    • 원격 서비스 장애 시에도 전체 서비스 가용성 유지
    • 점진적 성능 저하로 사용자 경험 보호
  2. 팀 생산성
    • 독립적인 개발 환경 구성 가능
    • 마이크로프론트엔드 애플리케이션 간 의존성 최소화
  3. 유지보수성
    • 명확한 에러 추적으로 신속한 문제 해결
    • 타입 시스템을 통한 리팩토링 안정성

마치며

MF 환경에서 이 유틸리티를 사용함으로써, 개발 환경에서의 생산성 향상, DX(Developer Experience) 그리고 프로덕션 환경에서의 안정성 확보라는 두 가지 목표를 모두 달성할 수 있었습니다.

'Frontend > Module Federation' 카테고리의 다른 글

BroadcastChannel API란?  (2) 2024.11.10
Micro Frontends와 MSA를 위한 Module Federation  (2) 2024.01.07
끄적끄적 개발자