테마
장애 범위 축소
마이크로 프론트엔드에서 단일 장애 지점(SPOF)을 회피하고 장애 영향을 최소화하는 전략과 한계를 정리한다.
학습 목표
- 마이크로 프론트엔드의 핵심 목표인 단일 장애 지점 회피의 의미를 설명할 수 있다.
- 에러 바운더리, 폴백 UI, 그레이스풀 디그레이데이션의 구현 전략을 제시할 수 있다.
- 모니터링과 알림 체계를 통해 장애를 조기에 감지하는 방법을 이해한다.
- 마이크로 프론트엔드 장애 대응의 한계와 트레이드오프를 인식한다.
1. 마이크로 프론트엔드의 두 가지 핵심 목표
마이크로 프론트엔드의 존재 이유를 두 가지로 정리할 수 있다.
| 목표 | 설명 |
|---|---|
| 단일 장애 지점(SPOF) 회피 | 하나의 마이크로 앱에 문제가 생겨도 나머지는 정상 동작한다 |
| 복잡도의 관리 가능한 분할 | 전체 앱의 복잡도를 팀이 감당할 수 있는 수준으로 쪼갠다 |
이 두 목표는 하나만 달성해서는 의미가 없다. 코드를 분리했지만 모든 리소스를 같은 서버에서 서빙한다면, 서버 장애 시 전체가 다운되므로 SPOF를 회피한 것이 아니다.
2. 에러 바운더리 (Error Boundary)
개념
에러 바운더리는 하위 컴포넌트 트리에서 발생한 JavaScript 오류를 잡아내어 전체 앱의 크래시를 방지하는 React 패턴이다. 마이크로 프론트엔드에서는 각 원격 모듈을 에러 바운더리로 감싸서 하나의 마이크로 앱 오류가 전체로 전파되는 것을 차단한다.
구현 전략
javascript
// RemoteAppWrapper.jsx
class RemoteErrorBoundary extends React.Component {
state = { hasError: false, error: null };
static getDerivedStateFromError(error) {
return { hasError: true, error };
}
componentDidCatch(error, errorInfo) {
// 모니터링 서비스로 에러 보고
errorReporter.captureException(error, {
extra: {
componentStack: errorInfo.componentStack,
remoteName: this.props.remoteName,
},
});
}
render() {
if (this.state.hasError) {
return <FallbackUI remoteName={this.props.remoteName} />;
}
return this.props.children;
}
}
// Shell에서 사용
function App() {
return (
<Layout>
<RemoteErrorBoundary remoteName="auth">
<Suspense fallback={<Loading />}>
<AuthApp />
</Suspense>
</RemoteErrorBoundary>
<RemoteErrorBoundary remoteName="dashboard">
<Suspense fallback={<Loading />}>
<DashboardApp />
</Suspense>
</RemoteErrorBoundary>
</Layout>
);
}에러 바운더리 배치 원칙
| 위치 | 역할 | 폴백 UI |
|---|---|---|
| Shell 최상위 | 전체 앱 크래시 방지 | 전체 에러 페이지 |
| 각 마이크로 앱 감싸기 | 개별 앱 장애 격리 | 해당 영역 대체 UI |
| 각 프래그먼트 감싸기 | 프래그먼트 로딩 실패 격리 | 빈 영역 또는 기본 UI |
| 비핵심 기능 감싸기 | 부가 기능 장애 격리 | 기능 숨김 |
3. 폴백 UI와 그레이스풀 디그레이데이션
폴백 UI 설계
마이크로 앱 로딩 실패 시 보여주는 대체 UI는 사용자 경험의 핵심이다.
그레이스풀 디그레이데이션 단계
"우아한 성능 저하(Graceful Degradation)"란 일부 기능이 실패하더라도 전체 서비스가 멈추지 않고 가능한 범위 내에서 계속 동작하는 것을 말한다.
| 단계 | 상태 | 사용자 경험 |
|---|---|---|
| 1단계: 정상 | 모든 마이크로 앱 정상 로드 | 전체 기능 사용 가능 |
| 2단계: 부분 장애 | 일부 마이크로 앱 로드 실패 | 실패한 영역에 폴백 UI 표시, 나머지 정상 |
| 3단계: 핵심만 동작 | 다수 마이크로 앱 실패 | Shell과 핵심 기능만 동작, 비핵심 기능 숨김 |
| 4단계: 최소 서비스 | Shell만 동작 | 안내 페이지와 고객센터 연결만 제공 |
구현 패턴: 동적 임포트 + 재시도
javascript
// 재시도 로직이 포함된 동적 임포트
function loadRemoteApp(remoteName, retries = 3) {
return new Promise((resolve, reject) => {
const attempt = (remaining) => {
import(/* webpackChunkName: "[request]" */ `${remoteName}/App`)
.then(resolve)
.catch((error) => {
if (remaining > 0) {
setTimeout(() => attempt(remaining - 1), 1000 * (4 - remaining));
} else {
reject(error);
}
});
};
attempt(retries);
});
}4. 인프라 레벨 장애 회피
코드 레벨의 에러 바운더리만으로는 충분하지 않다. 서버, 네트워크, CDN 레벨에서도 SPOF를 제거해야 한다.
인프라 분리 원칙
| 원칙 | 구체적 방법 |
|---|---|
| 서버 분리 | 각 마이크로 앱의 정적 리소스를 별도 서버 또는 별도 CDN 버킷에 배포 |
| CDN 다중화 | 메인 CDN 장애 시 폴백 CDN으로 자동 전환 |
| 도메인 분리 | auth.cdn.example.com, dashboard.cdn.example.com 등 도메인 분리 |
| 헬스체크 | 각 마이크로 앱 서버의 상태를 주기적으로 확인 |
| 캐시 전략 | CDN 캐시를 활용하여 오리진 서버 장애 시에도 캐시된 버전 서빙 |
비용 트레이드오프
마이크로 프론트엔드에서 인프라를 분리하면 서버 비용이 증가한다. 이는 피할 수 없는 트레이드오프다.
- 마이크로 앱이 5개이면 최소 5개의 배포 대상이 필요하다.
- 이중화까지 하면 10개 이상의 서버/CDN 구성이 필요할 수 있다.
- Vercel, Cloudflare Pages, AWS S3 + CloudFront 같은 SaaS를 활용하면 비용을 낮출 수 있지만, 그래도 모놀리식보다는 비용이 높다.
분리한 만큼 비용이 늘어나므로, 무조건 쪼개는 것이 아니라 관리가 가능한 수준으로 적절하게 나누는 것이 중요하다.
5. 모니터링과 알림
장애를 완전히 예방할 수는 없다. 빠르게 감지하고 대응하는 것이 핵심이다.
모니터링 대상
마이크로 앱별 모니터링
| 지표 | 임계값 예시 | 알림 조건 |
|---|---|---|
| 원격 모듈 로드 실패율 | 5% 초과 | 즉시 알림 |
| JS 에러율 | 1% 초과 | 5분 내 알림 |
| LCP (Largest Contentful Paint) | 2.5초 초과 | 30분 추이 확인 후 알림 |
| API 응답 시간 | p95 > 3초 | 10분 내 알림 |
| 에러 바운더리 트리거 횟수 | 시간당 100회 초과 | 즉시 알림 |
실무 도구
| 도구 | 용도 |
|---|---|
| Sentry | 프론트엔드 에러 추적, 소스맵 연동 |
| Datadog RUM | Real User Monitoring, 성능 지표 수집 |
| Grafana + Prometheus | 인프라 메트릭 시각화 |
| PagerDuty | 온콜 알림 및 인시던트 관리 |
| Lighthouse CI | 배포 시 성능 회귀 자동 감지 |
장애 대응 프로세스
- 감지: 모니터링 시스템이 이상 지표를 감지하여 알림을 발송한다.
- 격리: 문제가 있는 마이크로 앱을 이전 버전으로 롤백하거나 기능 플래그로 비활성화한다.
- 분석: 에러 로그, 배포 이력, 변경 사항을 확인하여 원인을 파악한다.
- 수정: 원인을 수정하고 테스트한 후 재배포한다.
- 회고: 장애 원인, 대응 과정, 개선 사항을 팀 전체와 공유한다.
6. 마이크로 프론트엔드의 한계 인식
무조건 쪼개는 것이 답이 아니다
마이크로 프론트엔드를 도입할 때 가장 흔한 실수는 필요 이상으로 쪼개는 것이다.
- 쪼갠 만큼 관리 포인트가 늘어난다.
- 코드 관리는 쉬워지지만 배포 관리는 복잡해진다.
- 인프라 비용이 분리한 만큼 증가한다.
- 중복 코드가 발생하는 것을 두려워하면 안 된다. 독립 배포를 위해 어느 정도의 코드 중복은 감수해야 한다.
적절한 분할의 기준
| 분할 기준 | 적절 | 과도 |
|---|---|---|
| 팀 규모 | 팀당 2-3개 마이크로 앱 | 팀당 10개 이상 |
| 복잡도 | 각 앱이 관리 가능한 크기 | 이미 단순한 것을 더 쪼갬 |
| 배포 주기 | 팀별 배포 주기가 다름 | 모든 앱을 동시에 배포 |
| 장애 격리 | 앱 간 독립적 운영 | 공통 서버에서 전부 서빙 |
중복 코드에 대한 인식 전환
개발자 입장에서 중복 코드를 유지하는 것은 잘못된 일처럼 느껴진다. 하지만 마이크로 프론트엔드에서는 독립 배포를 위해 중복을 감수해야 하는 경우가 있다.
- 중복된 코드조차 잘 관리하는 수준이라면 문제가 없다.
- 중복을 줄이기 위해 공통 패키지를 만드는 순간, 배포의 독립성이 훼손될 수 있다.
- 빌드타임에 공유되는 패키지는 보수적으로 변경하고, 마이크로 앱 내부의 비즈니스 로직은 빠르게 변경한다.
핵심 정리
| 전략 | 핵심 |
|---|---|
| SPOF 회피 | 인프라를 분리하여 하나의 장애가 전체로 파급되지 않도록 한다 |
| 에러 바운더리 | 각 마이크로 앱을 에러 바운더리로 감싸서 런타임 에러를 격리한다 |
| 그레이스풀 디그레이데이션 | 부분 장애 시 나머지 기능은 계속 동작하도록 설계한다 |
| 모니터링/알림 | 장애를 빠르게 감지하고 대응하는 체계를 구축한다 |
| 적절한 분할 | 관리 가능한 수준으로만 나누고, 중복 코드를 두려워하지 않는다 |
마이크로 프론트엔드는 은총알(Silver Bullet)이 아니다. 단일 장애 지점 회피와 복잡도의 관리 가능한 분할이라는 명확한 목표가 있을 때 의미가 있다. 이 목표가 필요하지 않은 프로젝트에서는 모놀리식이 더 나은 선택일 수 있다.
다음 단계
지금까지 마이크로 프론트엔드의 보안, 팀 운영, 배포, 개발 환경, 장애 대응까지 전반적인 운영 전략을 살펴보았다. 다음 장에서는 이러한 전략이 실제 프로덕트에 어떻게 적용되었는지 Dooray 프로젝트 사례를 통해 확인한다.