테마
마이크로프론트엔드 배포 마무리와 전략
선택적 배포의 중요성을 종합 정리하고, 컴포넌트 인터페이스를 단순하게 유지해야 하는 설계 원칙과, 배포 시나리오별 전략 및 롤백 전략을 체계적으로 학습한다.
학습 목표
- 마이크로프론트엔드에서 선택적 배포가 왜 핵심인지 종합적으로 이해한다
- 공유 컴포넌트 인터페이스를 단순하게 유지해야 하는 구체적 이유를 파악한다
- 배포 시나리오별 전략을 정리하고 적절한 전략을 선택할 수 있다
- 롤백 전략을 수립하여 배포 실패 시 빠르게 복구할 수 있다
1. 선택적 배포의 중요성
마이크로프론트엔드의 핵심 가치는 독립 배포다. 하나의 팀이 자신의 서비스를 다른 팀의 배포 일정과 무관하게 배포할 수 있어야 한다. 이것이 불가능하면 마이크로프론트엔드 아키텍처를 도입한 의미가 퇴색된다.
전체 배포 vs 선택적 배포의 조직 영향
| 측면 | 전체 배포 | 선택적 배포 |
|---|---|---|
| 배포 주기 | 모든 팀이 합의한 일정에 종속 | 각 팀 독립적으로 배포 |
| 배포 위험도 | 모든 서비스가 동시에 변경 | 변경 범위가 좁아 위험 감소 |
| 장애 격리 | 하나의 장애가 전체에 영향 | 문제된 서비스만 롤백 |
| 릴리스 속도 | 가장 느린 팀에 맞춤 | 각 팀 자체 속도로 릴리스 |
| 조직 자율성 | 낮음 (조율 비용 높음) | 높음 (독립적 의사결정) |
현실에서의 트레이드오프
실무에서는 공유 패키지(UIKit 등) 변경 시 전체 배포가 필요한 상황을 완전히 피할 수 없다. 핵심은 그런 상황의 빈도를 최소화하는 것이다.
2. 컴포넌트 인터페이스를 단순하게 유지하는 이유
공유 컴포넌트의 인터페이스(Props)가 복잡해지면, 변경 시 영향 범위가 기하급수적으로 커진다. 인터페이스를 단순하게 유지하는 것은 배포 전략의 근간이다.
인터페이스 복잡도와 배포 영향의 관계
인터페이스 설계 원칙
1. 옵셔널 확장 우선 원칙
기존 인터페이스에 새 기능을 추가할 때, 필수(required) Props 대신 옵셔널(optional) Props로 추가한다.
typescript
// 나쁜 예: 필수 Props 추가 → 모든 소비자 코드 수정 필요
interface ProfileProps {
user: UserType;
showBadge: boolean; // 필수 → 기존 소비자 빌드 에러
badgeType: string; // 필수 → 기존 소비자 빌드 에러
}
// 좋은 예: 옵셔널 Props 추가 → 기존 소비자 영향 없음
interface ProfileProps {
user: UserType;
showBadge?: boolean; // 옵셔널 → 기존 소비자 그대로 동작
badgeType?: string; // 옵셔널 → 기존 소비자 그대로 동작
}2. 합성(Composition) 패턴 활용
단일 컴포넌트에 모든 기능을 넣지 말고, 작은 컴포넌트를 합성하는 구조로 설계한다.
typescript
// 나쁜 예: 하나의 거대한 컴포넌트
<UserCard
showProfile showStats showBadge showActions
onFollow={...} onMessage={...} onBlock={...}
statsLayout="horizontal" badgePosition="top-right"
/>
// 좋은 예: 합성 가능한 작은 컴포넌트
<UserCard>
<UserCard.Profile user={user} />
<UserCard.Stats layout="horizontal" />
<UserCard.Badge position="top-right" />
<UserCard.Actions>
<FollowButton onClick={...} />
</UserCard.Actions>
</UserCard>3. 프래그먼트 컨테이너 분리
UI 컴포넌트와 비즈니스 로직을 분리하여, UI 변경이 로직에 영향을 주지 않도록 한다.
typescript
// UIKit: 순수 UI 컴포넌트 (인터페이스 안정)
export const Profile: React.FC<{ user: UserType }> = ({ user }) => (
<div className={styles.wrapper}>
<img src={user.profileImageUrl} />
<span>{user.name}</span>
</div>
);
// 각 마이크로앱: 컨테이너에서 데이터 페칭
const ProfileContainer: React.FC = () => {
const [user, setUser] = useState<UserType | null>(null);
useEffect(() => { getUser().then(setUser); }, []);
return user ? <Profile user={user} /> : <Skeleton />;
};3. 배포 시나리오별 전략
시나리오 종합 매트릭스
| 시나리오 | 변경 대상 | 배포 전략 | 다운타임 | 롤백 난이도 |
|---|---|---|---|---|
| 마이크로앱 버그 수정 | 단일 앱 내부 | 해당 앱만 배포 | 없음 | 낮음 |
| 프래그먼트 기능 추가 | 단일 프래그먼트 | 해당 프래그먼트만 배포 | 없음 | 낮음 |
| UIKit 컴포넌트 추가 | 패키지 + 소비 앱 | ShareScope 분리 + 점진 배포 | 없음 | 중간 |
| UIKit 인터페이스 변경 | 패키지 + 모든 소비 앱 | 전체 빌드 + 순차 배포 | 짧음 | 높음 |
| Shell 라우팅 변경 | Shell + 새 앱 | Shell 배포 후 앱 배포 | 짧음 | 중간 |
| 인프라 엔드포인트 변경 | 환경 변수 | 환경별 .env 교체 + 전체 빌드 | 없음 | 낮음 |
점진적 배포 (Incremental Deployment)
UIKit 같은 공유 패키지가 변경되었을 때, 모든 앱을 한 번에 배포하지 않고 순차적으로 배포한다.
이 전략의 핵심은 각 단계에서 문제가 발생하면 해당 앱만 롤백하고, 나머지 앱은 영향을 받지 않는다는 것이다.
4. 롤백 전략
배포 후 문제가 발견되면 빠르게 이전 상태로 복구해야 한다. 마이크로프론트엔드에서 롤백은 기존 모놀리식보다 세밀하게 수행할 수 있다.
롤백 방식 비교
| 롤백 방식 | 동작 | 소요 시간 | 적용 대상 |
|---|---|---|---|
| CDN 아티팩트 교체 | 이전 버전 dist/를 CDN에 재배포 | 수 초 | 단일 앱/프래그먼트 |
| CDN 캐시 무효화 | CloudFront Invalidation 실행 | 수 분 | CDN 전체 |
| Git revert + 재빌드 | 코드 되돌리고 재빌드/재배포 | 수십 분 | 코드 수준 롤백 |
| 환경 변수 URL 전환 | 리모트 엔트리 URL을 이전 버전으로 | 즉시 (빌드 불필요) | Shell에서 로딩하는 리모트 |
CDN 기반 롤백 전략
정적 파일 서버(S3, GCS 등)에 버전별로 디렉토리를 분리하면, 롤백은 단순히 심볼릭 링크 또는 라우팅 규칙을 변경하는 것으로 완료된다.
bash
# CDN 디렉토리 구조
s3://cdn-bucket/
posting/
v1.2.3/ # 이전 안정 버전
remoteEntry.js
chunk-*.js
v1.3.0/ # 현재 배포 버전 (문제 발생)
remoteEntry.js
chunk-*.js
latest -> v1.2.3 # 심볼릭 링크를 이전 버전으로 변경
# 롤백 명령 (수 초 내 완료)
aws s3 cp s3://cdn-bucket/posting/v1.2.3/remoteEntry.js \
s3://cdn-bucket/posting/latest/remoteEntry.js
# CDN 캐시 무효화
aws cloudfront create-invalidation \
--distribution-id EXXXXX \
--paths "/posting/latest/*"런타임 URL 전환을 활용한 즉시 롤백
Shell이 importRemote로 마이크로앱을 로딩하는 경우, 환경 변수의 URL만 변경하면 빌드 없이 즉시 롤백이 가능하다.
typescript
// Shell: 환경 변수로 버전 제어
importRemote({
url: process.env.REACT_APP_MICRO_POSTING!,
// url: "https://cdn.example.com/posting/v1.2.3" ← 롤백 시 이전 버전 URL
scope: "posting",
module: "./injector"
});다만 이 방식은 Shell 자체의 재빌드가 필요하다. 완전히 빌드 없이 롤백하려면 Config Server 패턴을 도입한다.
typescript
// Shell: 런타임에 Config 서버에서 URL 조회
const config = await fetch("https://config.example.com/remote-urls.json");
const urls = await config.json();
importRemote({
url: urls.posting, // Config 서버에서 반환된 URL
scope: "posting",
module: "./injector"
});Config Server 패턴을 사용하면 remote-urls.json만 수정하여 빌드 없이 즉시 롤백할 수 있다.
5. 배포 자동화 체크리스트
프로덕션 배포 전 반드시 확인해야 할 항목을 정리한다.
배포 전 체크리스트
| 단계 | 체크 항목 | 확인 방법 |
|---|---|---|
| 빌드 | 공유 패키지 선행 빌드 완료 | pnpm build:packages 성공 |
| 빌드 | 변경된 앱 빌드 성공 | pnpm --filter <app> build 성공 |
| 테스트 | 유닛 테스트 통과 | pnpm --filter <app> test 성공 |
| 테스트 | E2E 테스트 통과 (있는 경우) | Cypress/Playwright 통과 |
| 환경 | 프로덕션 환경 변수 확인 | .env.production 엔드포인트 정확성 |
| 공유 | SharedScope 충돌 없음 | 스테이징 환경에서 검증 |
| 공유 | Shared 의존성 버전 명시 | webpack shared 설정 확인 |
| 배포 | 이전 버전 아티팩트 보존 | S3/GCS에 버전별 디렉토리 유지 |
| 배포 | CDN 캐시 무효화 실행 | CloudFront Invalidation 확인 |
| 확인 | remoteEntry.js 접근 가능 | HTTP 200 응답 확인 |
| 확인 | 콘솔 에러 없음 | 브라우저 DevTools 확인 |
6. 장기적 배포 전략 로드맵
마이크로프론트엔드의 배포 성숙도는 단계적으로 발전시킨다.
각 레벨은 조직의 규모와 마이크로프론트엔드의 복잡도에 따라 선택한다. 처음부터 Level 4를 목표로 하기보다는, Level 2에서 시작하여 필요에 따라 점진적으로 발전시키는 것을 권장한다.
7. 프로젝트 마무리 정리
커리어업 프로젝트를 통해 학습한 마이크로프론트엔드 배포의 핵심 교훈을 정리한다.
| 실습 내용 | 변경 대상 | 배포 전략 | 학습 포인트 |
|---|---|---|---|
| CI/CD 파이프라인 구축 | 빌드 스크립트 | Turborepo --filter + 선행 빌드 | 변경 감지 기반 자동화 |
| UIKit 컴포넌트 이동 | ui-kit + edu + posting | ShareScope 분리 + 점진 배포 | 빌드타임 의존성 관리 |
| 프래그먼트 SWR 도입 | fragment 단독 | 프래그먼트만 재배포 | 런타임 의존성의 독립성 |
| 환경 변수 엔드포인트 | 모든 앱의 .env | 환경별 .env 교체 | 환경 분리 전략 |
핵심 정리
- 마이크로프론트엔드의 궁극적 목표는 독립 배포다. 변경된 부분만 배포하는 선택적 배포가 기본 원칙이며, 전체 배포는 최후의 수단이어야 한다
- 공유 컴포넌트 인터페이스를 단순하게 유지하면 연쇄 배포 빈도가 줄어든다. 옵셔널 확장을 기본으로 하고, 필수 Props 추가는 메이저 버전 업으로 관리한다
- 점진적 배포 전략(ShareScope 분리 + 앱별 순차 배포)을 사용하면, UIKit 같은 공유 패키지 변경 시에도 안전하게 배포할 수 있다
- 롤백은 CDN 아티팩트 버전 교체가 가장 빠르다. Config Server 패턴을 도입하면 빌드 없이 즉시 롤백이 가능하다
- 프래그먼트는 인터페이스를 최소화하고 기능 단위로 응집시키면, 가장 높은 수준의 독립 배포를 달성할 수 있다
다음 단계
- ../15-성능-최적화/01-프론트엔드-성능-측정-개요.md: 마이크로프론트엔드 환경에서 프론트엔드 성능을 측정하는 주요 지표(Core Web Vitals)와 성능 최적화 전략을 학습한다