Skip to content

마이크로프론트엔드 선택적 배포 전략

마이크로앱이나 프래그먼트를 추가 개발한 후, 빌드타임 의존성과 런타임 의존성의 차이를 이해하고, 변경 유형에 따라 배포 범위를 최소화하는 전략을 학습한다.

학습 목표

  • 마이크로앱 추가 개발 후 배포 범위를 정확히 결정할 수 있다
  • UIKit 같은 공유 패키지 변경 시 영향 범위(의존하는 모든 앱)를 파악하고 대응할 수 있다
  • 프래그먼트 추가 개발 후 독립 배포가 가능한 조건을 이해한다
  • 빌드타임 의존성과 런타임 의존성의 차이에 따른 배포 범위 차이를 구분할 수 있다

1. 배포 범위를 결정하는 핵심 원리

마이크로프론트엔드에서 배포 범위는 "무엇이 변경되었는가"에 의해 결정된다. 변경의 종류에 따라 재빌드/재배포해야 하는 범위가 달라진다.

의존성의 두 가지 유형

구분빌드타임 의존성런타임 의존성
포함 시점빌드 시 번들에 인라인브라우저 실행 시 네트워크 로딩
변경 영향의존하는 모든 앱 재빌드 필요해당 서비스만 재배포
예시UIKit, Shell Router, 타입 정의remoteEntry.js, API 응답
배포 범위넓음 (연쇄 배포)좁음 (독립 배포)

2. 시나리오 1: 마이크로앱 내부 로직 변경

가장 단순한 경우다. 포스팅 앱 내부에서 새로운 기능을 추가하거나 버그를 수정한 경우, 포스팅 앱만 재빌드하고 재배포하면 된다.

변경 범위와 배포 범위

bash
# 변경된 파일
apps/posting/src/pages/PageHome.tsx  # 새로운 UI 추가
apps/posting/src/apis.ts             # API 호출 수정

# 빌드 및 배포
pnpm --filter posting build          # posting만 빌드
# dist/ 를 CDN에 업로드             # posting만 배포

다른 마이크로앱(edu, network, job)은 포스팅과 런타임에만 통합되므로 전혀 영향을 받지 않는다. Shell 역시 importRemote로 포스팅을 로딩하므로 재빌드가 필요 없다.


3. 시나리오 2: UIKit 공유 패키지 변경 (빌드타임 의존성)

UIKit에 새로운 컴포넌트(예: Profile)를 추가하거나, 기존 컴포넌트의 인터페이스를 변경하면, UIKit을 빌드타임에 참조하는 모든 앱이 영향을 받는다.

커리어업 프로젝트의 UIKit 변경 시나리오

Profile 컴포넌트를 UIKit으로 이동하는 경우를 살펴보자.

typescript
// packages/ui-kit/src/components/profile.tsx (신규)
import type { User } from "@earth0/sba.js";

interface UserType extends User {
  connectionCount: number;
  postCount: number;
}

interface ProfileProps {
  user: UserType;
}

export const Profile: React.FC<ProfileProps> = ({ user }) => {
  return (
    <div className={styles.profileTop}>
      <img src={user.profileImageUrl} className={styles.profileImage} />
      <span className={styles.profileName}>{user.name}</span>
      <span className={styles.profileEmail}>{user.email}</span>
    </div>
  );
};
typescript
// packages/ui-kit/src/index.ts
export { Button } from "./components/button";
export { Icon } from "./components/icon";
export { Profile } from "./components/profile"; // 신규 추가

UIKit 변경 시 영향 범위 분석

Shared Singleton과 ShareScope 문제

Module Federation에서 UIKit을 singleton: true로 설정한 경우, 모든 마이크로앱이 동일한 UIKit 인스턴스를 공유한다. UIKit에 새 컴포넌트를 추가한 후 일부 앱만 재빌드하면, 앱마다 참조하는 UIKit 버전이 달라져 런타임 에러가 발생한다.

javascript
// apps/edu/webpack.config.js
new ModuleFederationPlugin({
  shared: {
    "@career-up/ui-kit": {
      singleton: true,
      // shareScope 미지정 시 기본 "default" 스코프 사용
    }
  }
});

해결 전략: ShareScope 분리

javascript
// apps/edu/webpack.config.js (수정 후)
new ModuleFederationPlugin({
  shared: {
    "@career-up/ui-kit": {
      singleton: true,
      shareScope: "v2"  // 새 버전 스코프
    }
  }
});

shareScopev2로 지정하면, 같은 스코프의 UIKit끼리만 공유한다. 아직 재빌드하지 않은 앱은 default 스코프의 구 버전 UIKit을 사용하므로 충돌이 발생하지 않는다.

추가로, 새 UIKit의 CSS가 올바르게 적용되도록 injector에서 CSS를 명시적으로 import한다.

typescript
// apps/edu/src/injector.tsx
import "@career-up/ui-kit/index.css";  // 추가

4. 시나리오 3: 프래그먼트 독립 배포 (런타임 의존성)

프래그먼트는 런타임에 remoteEntry.js를 통해 로딩되므로, 프래그먼트 내부만 변경했다면 해당 프래그먼트만 재빌드/재배포하면 된다.

SWR 의존성 추가 시나리오

추천 1촌 프래그먼트에 useSWR 훅을 도입하는 경우를 살펴보자.

typescript
// fragments/fragment-recommend-connections/src/containers/RecommendConnectionsContainer.tsx
import useSWR from "swr";

const serverUrl = process.env.REACT_APP_SERVER_URL!;

const RecommendConnectionsContainer: React.FC = () => {
  const { data, isLoading, error } = useSWR<ConnectionType[]>(
    `/connections?limit=3`,
    async (url: string) => {
      const token = await getAccessToken();
      const response = await fetch(`${serverUrl}${url}`, {
        headers: { Authorization: `Bearer ${token}` }
      });
      return response.json();
    }
  );

  return (
    <RecommendConnections connections={data ?? []} />
  );
};

프래그먼트 독립 배포 흐름

SWR shared 설정 주의사항:

새로운 의존성(SWR)을 추가할 때, webpack의 shared 설정에 버전을 명시해야 경고가 발생하지 않는다.

javascript
// fragments/fragment-recommend-connections/webpack.config.js
new ModuleFederationPlugin({
  shared: {
    react: { singleton: true, requiredVersion: "^18.2.0" },
    "react-dom": { singleton: true, requiredVersion: "^18.2.0" },
    swr: { requiredVersion: "^2.2.0" }  // 버전 명시
  }
});

5. 배포 범위 결정 의사결정 트리

변경이 발생했을 때, 아래 의사결정 트리를 따라 배포 범위를 결정한다.


6. 변경 유형별 배포 범위 요약

변경 유형구체적 예시배포 범위난이도
마이크로앱 내부 로직 수정포스팅 페이지 UI 변경해당 앱만낮음
프래그먼트 내부 로직 수정추천 1촌 렌더링 변경해당 프래그먼트만낮음
프래그먼트에 새 의존성 추가SWR 도입해당 프래그먼트만 (shared 버전 명시)낮음
공유 패키지에 컴포넌트 추가UIKit에 Profile 추가패키지 + 사용 앱 (ShareScope 분리)중간
공유 패키지 인터페이스 변경Button Props 변경패키지 + 의존하는 모든 앱높음
공유 패키지 대규모 리팩토링UIKit 메이저 버전 업전체 앱 재빌드 + 재배포매우 높음
Shell 라우팅 변경새 경로 추가Shell + 해당 마이크로앱중간

핵심 정리

  1. 빌드타임 의존성(UIKit, Shell Router) 변경은 해당 패키지를 참조하는 모든 앱의 재빌드가 필요하다. 반면 런타임 의존성(remoteEntry.js) 변경은 해당 서비스만 재배포하면 된다
  2. 공유 패키지에 새 컴포넌트를 추가한 경우, shareScope를 분리하면 아직 재빌드하지 않은 앱과 충돌 없이 점진적 배포가 가능하다
  3. 프래그먼트에 새 런타임 의존성(SWR 등)을 추가할 때는 webpack shared 설정에 버전을 명시해야 경고 없이 배포된다
  4. 공유 패키지의 인터페이스를 변경(Props 변경, 삭제)하면 의존하는 모든 앱의 연쇄 재빌드가 불가피하다. 이를 최소화하려면 인터페이스를 단순하게 유지하고 옵셔널 확장을 선호해야 한다
  5. 배포 범위를 결정할 때는 "빌드타임인가 런타임인가"를 먼저 판단하고, 의사결정 트리를 따라 최소 범위를 도출하는 것이 핵심이다

다음 단계

  • 03-배포-마무리와-전략.md: 선택적 배포의 중요성을 종합적으로 정리하고, 컴포넌트 인터페이스를 단순하게 유지해야 하는 이유, 배포 시나리오별 전략, 그리고 롤백 전략을 학습한다