Skip to content

모노레포 마이크로프론트엔드 CI/CD 파이프라인 구축

모노레포 환경에서 변경된 패키지만 감지하여 선택적으로 빌드하고, Turborepo의 --filter와 캐싱을 활용해 CI/CD 파이프라인을 자동화하는 전략을 학습한다.

학습 목표

  • 모노레포에서 변경 감지 기반 빌드 자동화의 필요성과 원리를 이해한다
  • Turborepo의 --filter 옵션을 활용하여 변경된 패키지만 빌드하는 방법을 익힌다
  • 빌드 타임 의존성(UIKit, Shell Router 등)을 선행 빌드하는 파이프라인 순서를 설계할 수 있다
  • GitHub Actions를 활용한 실제 CI/CD 워크플로 설정을 구현할 수 있다

1. 모노레포에서 빌드 자동화의 필요성

마이크로프론트엔드를 모노레포로 관리하면 수십 개의 워크스페이스가 하나의 저장소에 공존한다. 전체를 매번 빌드하면 시간과 비용이 낭비된다.

전체 빌드 vs 선택적 빌드 비교

항목전체 빌드선택적 빌드
빌드 범위모든 워크스페이스변경된 워크스페이스 + 의존 패키지
소요 시간10~30분1~5분
CI 비용매 커밋마다 높은 비용변경 범위에 비례
배포 위험불필요한 배포 포함 가능변경 사항만 정확히 배포
캐시 활용비효율적Turborepo 캐시로 극대화

2. 빌드 타임 의존성 선행 빌드

마이크로앱을 빌드하기 전에, 빌드 시점에 참조하는 공유 패키지를 먼저 빌드해야 한다. 커리어업 프로젝트에서는 ui-kitshell-router가 빌드 타임 의존성에 해당한다.

package.json에 선행 빌드 스크립트 추가

json
// monorepo root package.json
{
  "scripts": {
    "build": "turbo build",
    "build:packages": "turbo build --filter=shell-router --filter=ui-kit"
  }
}

build:packages는 전체 프로젝트가 아닌, 빌드 타임에 공유되는 패키지 두 개만 필터링하여 빌드한다.

각 마이크로앱의 빌드 스크립트 구성

json
// apps/posting/package.json
{
  "scripts": {
    "build": "pnpm --filter monorepo build:packages && webpack --mode production",
    "build:dev": "pnpm --filter monorepo build:packages && webpack --mode development"
  }
}

핵심은 &&로 연결하여 패키지 빌드가 완료된 후에만 마이크로앱 빌드가 시작되도록 하는 것이다.


3. Turborepo --filter 활용

Turborepo의 --filter 플래그는 특정 워크스페이스만 빌드 대상으로 지정하는 핵심 기능이다.

주요 --filter 패턴

bash
# 특정 워크스페이스만 빌드
turbo build --filter=posting

# 여러 워크스페이스 지정
turbo build --filter=shell-router --filter=ui-kit

# glob 패턴 (접두사가 동일한 패키지 일괄 빌드)
turbo build --filter='@career-up/*'

# 특정 워크스페이스와 그 의존성 전부 빌드
turbo build --filter=posting...

# 변경된 파일 기반 (Git 비교)
turbo build --filter='[HEAD^1]'

# 특정 브랜치 기준 변경 감지
turbo build --filter='[origin/main...HEAD]'

--filter 옵션 요약

패턴설명예시
--filter=name이름으로 특정--filter=posting
--filter=name...해당 패키지 + 모든 의존성--filter=posting...
--filter='@scope/*'스코프로 그룹 지정--filter='@career-up/*'
--filter='[ref]'Git ref 이후 변경된 패키지--filter='[origin/main]'
--filter='[ref1...ref2]'두 ref 사이 변경된 패키지--filter='[HEAD^1...HEAD]'

Turborepo 캐싱 메커니즘


4. GitHub Actions CI 워크플로 설정

기본 CI 워크플로

yaml
# .github/workflows/ci.yml
name: CI - Build & Test

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 2  # 변경 감지를 위해 이전 커밋 포함

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 20

      - name: Setup pnpm
        uses: pnpm/action-setup@v2
        with:
          version: 8

      - name: Install dependencies
        run: pnpm install --frozen-lockfile

      - name: Turborepo cache
        uses: actions/cache@v4
        with:
          path: .turbo
          key: turbo-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}
          restore-keys: |
            turbo-${{ runner.os }}-

      - name: Build changed packages
        run: pnpm turbo build --filter='[HEAD^1]'

      - name: Run tests
        run: pnpm turbo test --filter='[HEAD^1]'

마이크로앱별 독립 배포 워크플로

yaml
# .github/workflows/deploy-posting.yml
name: Deploy Posting App

on:
  push:
    branches: [main]
    paths:
      - 'apps/posting/**'
      - 'packages/ui-kit/**'
      - 'packages/shell-router/**'

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: pnpm/action-setup@v2
        with:
          version: 8

      - run: pnpm install --frozen-lockfile

      - name: Build shared packages
        run: pnpm build:packages

      - name: Build posting app
        run: pnpm --filter posting build

      - name: Deploy to CDN
        run: |
          # dist/ 폴더를 S3나 CDN에 업로드
          aws s3 sync apps/posting/dist/ s3://cdn-bucket/posting/ \
            --cache-control "public, max-age=31536000"

paths 트리거 핵심: apps/posting/** 외에도 packages/ui-kit/**packages/shell-router/**를 포함한다. 공유 패키지가 변경되면 해당 패키지에 의존하는 마이크로앱도 재빌드/재배포 해야 하기 때문이다.


5. 환경 변수 기반 엔드포인트 관리

CI/CD에서 빌드할 때 환경별로 다른 엔드포인트를 주입해야 한다. 로컬 개발 시에는 localhost를 사용하지만, QA/스테이징/프로덕션에서는 각각 다른 URL을 사용한다.

.env 파일 구성

bash
# .env (개발)
REACT_APP_MICRO_POSTING=http://localhost:3001
REACT_APP_MICRO_EDU=http://localhost:3002
REACT_APP_MICRO_NETWORK=http://localhost:3003
REACT_APP_MICRO_JOB=http://localhost:3004
REACT_APP_FRAGMENT_RECOMMEND_CONNECTIONS=http://localhost:5001
REACT_APP_SERVER_URL=http://localhost:4000
bash
# .env.production (프로덕션)
REACT_APP_MICRO_POSTING=https://posting.cdn.example.com
REACT_APP_MICRO_EDU=https://edu.cdn.example.com
REACT_APP_MICRO_NETWORK=https://network.cdn.example.com
REACT_APP_MICRO_JOB=https://job.cdn.example.com
REACT_APP_FRAGMENT_RECOMMEND_CONNECTIONS=https://fragment-rc.cdn.example.com
REACT_APP_SERVER_URL=https://api.example.com

Shell에서 환경 변수 사용

typescript
// apps/shell/src/components/AppPosting.tsx
importRemote<{ default: InjectFunctionType }>({
  url: process.env.REACT_APP_MICRO_POSTING!,
  scope: "posting",
  module: "./injector",
  remoteEntryFileName: "remoteEntry.js"
});

이렇게 환경 변수로 관리하면, CI/CD 파이프라인에서 빌드 시점에 환경별 .env 파일만 교체하면 된다.


6. CI/CD 파이프라인 전체 아키텍처


핵심 정리

  1. 모노레포에서 전체 빌드는 비효율적이다. Turborepo의 --filter와 캐싱을 활용하면 변경된 패키지만 빌드하여 CI 시간을 80% 이상 단축할 수 있다
  2. 마이크로앱 빌드 전에 빌드 타임 의존성(ui-kit, shell-router)을 반드시 선행 빌드해야 한다. build:packages 스크립트로 이를 자동화한다
  3. GitHub Actions의 paths 트리거를 사용하면 변경된 파일 경로에 따라 해당 마이크로앱의 배포 워크플로만 실행할 수 있다
  4. 환경 변수로 리모트 엔트리 URL을 관리하면, 개발/QA/프로덕션 환경 전환이 빌드 시점에 .env 파일 교체만으로 완료된다
  5. Turborepo의 해시 기반 캐싱은 입력(소스, 의존성, 환경 변수)이 동일하면 빌드를 완전히 스킵하므로, CI 반복 실행 비용을 획기적으로 줄인다

다음 단계

  • 02-선택적-배포-전략.md: 마이크로앱 추가 개발 후 배포 범위를 결정하는 방법과, UIKit 같은 공유 패키지 변경 시 영향 범위를 분석하여 빌드타임 의존성과 런타임 의존성에 따라 배포 전략을 수립하는 방법을 학습한다