Ch7. CI/CD 파이프라인 설계
GitHub Actions + Turborepo, 병렬 빌드, 선택적 테스트, 릴리스 전략
핵심 요약
- GitHub Actions에서
turbo run ... --affected로 변경된 패키지만 빌드·테스트해 CI 시간을 줄입니다. --affected와--filter=...[origin/main]변경 감지가 작동하려면actions/checkout에fetch-depth: 0이 필요합니다.- 앱이 많아지면
--dry-run=json출력을 jq로 파싱해 매트릭스 빌드로 CI를 분산합니다. - Changesets는 PR마다 변경 내역을 모아 main 머지 시 버전 범프·CHANGELOG·npm publish를 자동화합니다.
- CI 산출물만 배포하려면 Vercel prebuilt deploy를 쓰되, OIDC가
VERCEL_TOKEN을 대체한다고 가정하지 않습니다.
CI 전체 흐름
GitHub Actions + Turborepo
# .github/workflows/ci.yml
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
jobs:
ci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # 변경 감지에 필요
- uses: actions/setup-node@v4
with:
node-version: 22
cache: 'yarn'
- run: yarn install --immutable
- name: Build, Lint, Typecheck, Test
run: turbo run build lint typecheck test --affectedfetch-depth: 0은 전체 히스토리를 가져와 --affected와 --filter=...[origin/main] 같은 변경 감지가 작동하게 합니다.
변경 감지 기반 선택적 빌드
# main 대비 변경된 패키지만 빌드
turbo run build --filter=...[origin/main]
# 변경된 패키지와 영향을 받는 패키지 실행
turbo run build test --affected
# 직전 커밋 대비 변경분만
turbo run build test --filter=...[HEAD~1]
# 특정 앱과 그 의존성만
turbo run build --filter=web...
# 특정 앱만 (의존성 제외)
turbo run build --filter=web| 필터 | 의미 |
|---|---|
--filter=web | web 앱만 |
--filter=web... | web + web이 의존하는 모든 패키지 |
--filter=...web | web + web에 의존하는 모든 앱/패키지 |
--filter=...[HEAD~1] | 마지막 커밋 이후 변경된 패키지만 |
--filter=web...[origin/main] | main 이후 변경된 web 의존성 체인 |
--affected | 변경된 패키지와 영향을 받는 패키지 |
매트릭스 전략 (대규모)
앱이 많아지면 매트릭스 빌드로 CI를 분산합니다:
jobs:
detect:
runs-on: ubuntu-latest
outputs:
apps: ${{ steps.filter.outputs.apps }}
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 0 }
- id: filter
run: |
APPS=$(turbo run build --filter=...[origin/main] --dry-run=json \
| jq -c '[.tasks[] | select(.directory | startswith("apps/")) | .package]')
echo "apps=$APPS" >> "$GITHUB_OUTPUT"
build:
needs: detect
if: needs.detect.outputs.apps != '[]'
strategy:
matrix:
app: ${{ fromJson(needs.detect.outputs.apps) }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: yarn install --immutable
- run: turbo run build --filter=${{ matrix.app }}...릴리스 자동화 (Changesets)
# 초기 설정
npx @changesets/cli init
# 변경 사항 기록 (PR 시)
npx changeset add
# → .changeset/fuzzy-wolf-dance.md 생성# .github/workflows/release.yml
name: Release
on:
push:
branches: [main]
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: yarn install --immutable
- uses: changesets/action@v1
with:
publish: yarn changeset publish
version: yarn changeset version
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}Changesets는 PR마다 변경 내역을 모았다가 main에 머지될 때 버전 범프 + CHANGELOG 생성 + npm publish를 한 번에 처리합니다.
Vercel prebuilt deploy
빌드와 테스트를 GitHub Actions 쪽에서 직접 통제하려는 엔터프라이즈 팀이라면 Vercel 원격 빌드 대신 prebuilt 배포를 씁니다.
- name: Pull Vercel env
run: vercel pull --yes --environment=production --token=${{ secrets.VERCEL_TOKEN }}
- name: Build on CI
run: vercel build --prod --token=${{ secrets.VERCEL_TOKEN }}
- name: Deploy prebuilt output
run: vercel deploy --prebuilt --prod --token=${{ secrets.VERCEL_TOKEN }}이렇게 하면 "CI에서 검증한 산출물만 배포한다"가 보장됩니다. 단, Vercel CLI 토큰은 여전히 필요하니 OIDC가 VERCEL_TOKEN을 대체한다고 가정하지 마세요.