Ray Book
프론트엔드 인프라

CI/CD 파이프라인

커밋부터 배포까지, lint, type-check, test, build, deploy 파이프라인의 각 단계를 시각화합니다

infraci-cdgithub-actionsdeploymentpipeline

CI/CD가 해결하는 문제

"내 로컬에서는 되는데요", 개발자라면 누구나 한 번쯤 들어봤을 말입니다. 코드가 개인 머신에서 동작하는 것과 실제 프로덕션에 배포되는 것 사이에는 많은 단계가 있습니다.

수동으로 이 과정을 반복하면 문제가 발생합니다.

  • 빌드 누락 , "빌드 깨지는 거 몰랐어요" → 배포 후 500 에러
  • 테스트 스킵 , "급한 핫픽스라서..." → 기존 기능이 깨짐
  • 린트 무시 , "나중에 고칠게요" → 코드 품질 하락
  • 배포 실수 , 스테이징 대신 프로덕션에 배포

CI (Continuous Integration)는 코드 변경을 자동으로 검증하고, CD (Continuous Deployment)는 검증된 코드를 자동으로 배포합니다. 사람의 실수를 시스템이 방지합니다.

파이프라인의 단계

프론트엔드 CI/CD 파이프라인은 보통 다음 단계로 구성됩니다.

Push → Install → Lint + Type-check → Test → Build → Deploy → Post-deploy
                                                                     
                   (병렬 실행)        (순차)   (순차)  (조건부)   (병렬)
                                                                   
     캐시 복원                                          Lighthouse + Bundle check

각 단계는 이전 단계의 성공을 전제로 합니다. 하나라도 실패하면 파이프라인이 중단됩니다, 빠른 실패 (fail fast) 원칙입니다.

GitHub Actions 기본 구조

가장 널리 쓰이는 CI/CD 플랫폼인 GitHub Actions의 구조를 살펴봅니다.

# .github/workflows/ci.yml
name: CI

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

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: 'pnpm'
      - run: pnpm install --frozen-lockfile
      - run: pnpm lint

  type-check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: 'pnpm'
      - run: pnpm install --frozen-lockfile
      - run: pnpm tsc --noEmit

  test:
    needs: [lint, type-check]
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: 'pnpm'
      - run: pnpm install --frozen-lockfile
      - run: pnpm test

  build:
    needs: [test]
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: 'pnpm'
      - run: pnpm install --frozen-lockfile
      - run: pnpm build
      - uses: actions/upload-artifact@v4
        with:
          name: build-output
          path: dist/

핵심 포인트:

  • linttype-check는 서로 독립적이므로 병렬 실행
  • testneeds: [lint, type-check]로 둘 다 통과한 후 실행
  • buildneeds: [test]로 테스트 통과 후 실행
  • --frozen-lockfile로 lockfile과 다른 의존성 설치를 방지

캐싱 전략

CI에서 가장 많은 시간을 잡아먹는 것은 의존성 설치 입니다. 캐싱으로 이 시간을 줄입니다.

node_modules 캐싱

- uses: actions/setup-node@v4
  with:
    node-version: 22
    cache: 'pnpm'  # pnpm store를 자동 캐싱

actions/setup-node는 패키지 매니저의 캐시를 자동으로 관리합니다. lockfile의 해시를 키로 사용하므로, 의존성이 변경되지 않으면 캐시에서 복원합니다.

빌드 캐싱

Next.js의 경우 .next/cache를 캐싱하면 재빌드 시간이 크게 줄어듭니다.

- uses: actions/cache@v4
  with:
    path: .next/cache
    key: nextjs-${{ hashFiles('pnpm-lock.yaml') }}-${{ hashFiles('**/*.ts', '**/*.tsx') }}
    restore-keys: |
      nextjs-${{ hashFiles('pnpm-lock.yaml') }}-

Turborepo 리모트 캐시

모노레포에서는 Turborepo의 리모트 캐시가 가장 효과적입니다.

- run: pnpm turbo run build --token=${{ secrets.TURBO_TOKEN }} --team=${{ vars.TURBO_TEAM }}

한 PR의 빌드 결과를 다른 PR과 공유할 수 있습니다.

Preview 배포

PR마다 고유한 URL로 배포하는 것이 Preview 배포입니다. Vercel, Netlify, Cloudflare Pages 같은 플랫폼은 이를 자동으로 지원합니다.

PR #42: feat/new-header
  → https://my-app-pr-42.vercel.app

PR #57: fix/login-bug
  → https://my-app-pr-57.vercel.app

Preview 배포의 장점:

  • 코드 리뷰와 시각적 확인을 동시에 , PR 코멘트에 Preview URL이 자동으로 달림
  • QA 팀이 머지 전에 테스트 , 별도 환경 세팅 없이 URL만 공유
  • 디자이너와 빠른 피드백 루프 , "이 URL 확인해주세요"
# Vercel의 경우 별도 설정 없이 자동
# 수동 구성이 필요한 경우:
deploy-preview:
  if: github.event_name == 'pull_request'
  needs: [build]
  runs-on: ubuntu-latest
  steps:
    - uses: actions/download-artifact@v4
      with:
        name: build-output
        path: dist/
    - run: npx vercel deploy --prebuilt --token=${{ secrets.VERCEL_TOKEN }}

배포 전략

프로덕션 배포 시 다운타임을 최소화하는 전략이 필요합니다.

롤링 배포 (Rolling)

기존 인스턴스를 하나씩 새 버전으로 교체 합니다. 교체 중에는 구 버전과 신 버전이 공존합니다.

[v1] [v1] [v1] [v1]    ← 배포 시작
[v2] [v1] [v1] [v1]    ← 1개 교체
[v2] [v2] [v1] [v1]    ← 2개 교체
[v2] [v2] [v2] [v2]    ← 완료
  • 장점 : 리소스 추가 없이 점진적 배포
  • 단점 : 배포 중 두 버전이 공존 (API 호환성 주의)

블루-그린 배포 (Blue-Green)

동일한 환경 두 개를 운영합니다. 새 버전을 Green에 배포하고, 트래픽을 한 번에 전환합니다.

Blue (v1) ← 트래픽    Green (v2) ← 대기
Blue (v1) ← 대기      Green (v2) ← 트래픽 전환
  • 장점 : 즉시 롤백 가능 (트래픽을 다시 Blue로 전환)
  • 단점 : 인프라 비용 2배

카나리 배포 (Canary)

트래픽의 일부 (예: 5%)만 새 버전으로 보내고, 문제가 없으면 점진적으로 늘립니다.

v1: 100% → 95% → 80% → 50% → 0%
v2: 0%   → 5%  → 20% → 50% → 100%
  • 장점 : 위험 최소화, 실제 사용자로 검증
  • 단점 : 라우팅 설정의 복잡도, 모니터링 필수

프론트엔드에서는 CDN 레벨에서 카나리를 구현하거나, Vercel의 Skew Protection 같은 기능을 활용합니다.

프론트엔드 특화, Post-deploy 검증

프론트엔드에는 백엔드와 다른 품질 지표가 있습니다.

Lighthouse CI

배포 후 자동으로 Lighthouse를 실행하여 Core Web Vitals를 측정합니다.

lighthouse:
  needs: [deploy]
  runs-on: ubuntu-latest
  steps:
    - uses: actions/checkout@v4
    - uses: treosh/lighthouse-ci-action@v12
      with:
        urls: |
          https://my-app.com/
          https://my-app.com/dashboard
        budgetPath: ./lighthouse-budget.json
        uploadArtifacts: true
// lighthouse-budget.json
[{
  "path": "/*",
  "timings": [
    { "metric": "largest-contentful-paint", "budget": 2500 },
    { "metric": "interactive", "budget": 3500 }
  ],
  "resourceSizes": [
    { "resourceType": "script", "budget": 300 },
    { "resourceType": "total", "budget": 500 }
  ]
}]

LCP가 2.5초를 초과하면 CI가 실패합니다. 성능 저하를 배포 전에 잡습니다.

번들 사이즈 체크

- uses: andresz1/size-limit-action@v1
  with:
    github_token: ${{ secrets.GITHUB_TOKEN }}

PR에 번들 사이즈 변화를 자동으로 코멘트합니다.

📦 Size limit report

  Package     Size       Change
  main.js     142 kB     +3.2 kB (+2.3%)
  vendor.js   89 kB      0 B

시리즈 마무리, 프론트엔드 인프라 전체 지도

7편에 걸쳐 프론트엔드 인프라의 핵심 주제를 다뤘습니다.

주제핵심 키워드
1트랜스파일러Babel, SWC, TypeScript
2린터와 포매터ESLint, Prettier, AST
3번들러Webpack, Vite, esbuild
4Webpack vs ViteHMR, Dev Server, 빌드 속도
5패키지 매니저npm, pnpm, Yarn PnP
6모노레포Turborepo, Nx, Workspaces
7CI/CD 파이프라인GitHub Actions, 배포 전략

이 인프라들은 독립적이 아니라 하나의 파이프라인으로 연결 됩니다.

코드 작성 → 린터/포매터 → 트랜스파일 → 번들링 → 테스트 → 빌드 → 배포
                                                                 
  패키지 매니저  ESLint        SWC/Babel   Vite/Webpack          CI/CD
  (pnpm)       Prettier                   Tree-shaking         GitHub Actions
               (모노레포: Turborepo/Nx가 이 전체를 오케스트레이션)

각 도구의 역할을 이해하면, 새로운 도구가 등장해도 어떤 문제를 해결하는 도구인지 즉시 파악할 수 있습니다.

시각화

커밋부터 배포 후 검증까지, CI/CD 파이프라인의 각 단계를 순서대로 봅니다.

Step 1/5Commit & Push

개발자가 코드를 커밋하고 원격 저장소에 push합니다. GitHub는 이벤트를 감지하여 워크플로우를 트리거합니다.

git commit
git push
GitHub Event
push / pull_request

체크리스트

  • PR마다 lint, type-check, test가 자동으로 실행되는가?
  • 의존성 설치에 캐싱을 적용했는가?
  • PR에 Preview 배포가 자동으로 생성되는가?
  • 프로덕션 배포 시 롤백 전략이 있는가?
  • Lighthouse CI로 성능 예산을 설정했는가?
  • 번들 사이즈 변화를 PR에서 확인할 수 있는가?
  • --frozen-lockfile 플래그로 CI에서 lockfile 검증을 하는가?