Skip to content
Published on

CI/CD 파이프라인 심화 가이드 2025: 배포 전략, GitOps, 모노레포, 보안 파이프라인

Authors

도입

2024년, DORA(DevOps Research and Assessment) 보고서에 따르면, 엘리트 팀은 하루에 여러 번 배포하고 변경 실패율은 5% 미만입니다. 반면 저성과 팀은 한 달에 한 번 배포하며, 복구에 수 주가 걸립니다. 이 격차의 핵심은 바로 CI/CD 파이프라인의 성숙도에 있습니다.

단순한 빌드-테스트-배포를 넘어서, 현대적 CI/CD는 배포 전략(Blue-Green, Canary), GitOps, 모노레포 최적화, 피처 플래그, 보안 파이프라인까지 아우릅니다. 이 가이드는 2025년 현재 프로덕션 환경에서 검증된 심화 CI/CD 전략을 체계적으로 다룹니다.


1. 배포 전략 (Deployment Strategies)

1.1 Rolling Update

가장 기본적인 배포 방식으로, 인스턴스를 순차적으로 교체합니다.

시간 →

인스턴스 1:  [v1] [v1] [v2] [v2] [v2]
인스턴스 2:  [v1] [v1] [v1] [v2] [v2]
인스턴스 3:  [v1] [v1] [v1] [v1] [v2]

트래픽:      100% v1 → 혼합 → 100% v2
# Kubernetes Rolling Update
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-server
spec:
  replicas: 3
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1        # 최대 1개 추가 Pod
      maxUnavailable: 0  # 다운타임 0
  template:
    spec:
      containers:
        - name: api
          image: myapp:v2
          readinessProbe:
            httpGet:
              path: /health
              port: 8080
            initialDelaySeconds: 10
            periodSeconds: 5

장점: 간단, 추가 리소스 최소, 점진적 교체 단점: 배포 중 v1/v2 혼합 트래픽, 즉시 롤백 어려움

1.2 Blue-Green Deployment

두 개의 동일한 환경(Blue/Green)을 유지하고, 트래픽을 한 번에 전환합니다.

┌─────────────────────────────────────────────┐
│                                             │
Load Balancer│       │                                     │
 (전환 전: Blue로 트래픽)│   ┌───────────┐     ┌───────────┐           │
│   │   Blue    │     │   Green   │           │
   (v1)   (v2)    │           │
│   │  Active   │     │  Standby  │           │
│   └───────────┘     └───────────┘           │
│                                             │
│   전환 후: Green으로 100% 트래픽 전환         │
│                                             │
│   ┌───────────┐     ┌───────────┐           │
│   │   Blue    │     │   Green   │           │
   (v1)   (v2)    │           │
│   │  Standby  │     │  Active   │           │
│   └───────────┘     └───────────┘           │
└─────────────────────────────────────────────┘
# AWS ALB를 이용한 Blue-Green (GitHub Actions)
name: Blue-Green Deploy

on:
  push:
    branches: [main]

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

      - name: Build and Push Docker Image
        run: |
          docker build -t myapp:${{ github.sha }} .
          docker push myregistry/myapp:${{ github.sha }}

      - name: Deploy to Green Environment
        run: |
          aws ecs update-service \
            --cluster production \
            --service green-service \
            --task-definition myapp:${{ github.sha }}

      - name: Run Smoke Tests on Green
        run: |
          ./scripts/smoke-test.sh https://green.myapp.com

      - name: Switch Traffic to Green
        run: |
          aws elbv2 modify-listener \
            --listener-arn ${{ secrets.ALB_LISTENER_ARN }} \
            --default-actions \
              Type=forward,TargetGroupArn=${{ secrets.GREEN_TG_ARN }}

장점: 즉시 롤백 가능 (Blue로 트래픽 전환), 다운타임 없음 단점: 2배의 인프라 비용, 데이터베이스 마이그레이션 복잡

1.3 Canary Deployment

소수의 트래픽에만 새 버전을 먼저 배포하여 검증합니다.

Phase 1: 5% 트래픽을 Canary(v2)┌──────────────────────────────┐
│ v1 v1 v1 v1 v1 v1 v1 v1 v1  │ 95%
│ v2                           │  5%
└──────────────────────────────┘

Phase 2: 검증 후 25%로 확대
┌──────────────────────────────┐
│ v1 v1 v1 v1 v1 v1 v1        │ 75%
│ v2 v2 v2                     │ 25%
└──────────────────────────────┘

Phase 3: 안정 확인 후 100%
┌──────────────────────────────┐
│ v2 v2 v2 v2 v2 v2 v2 v2 v2  │ 100%
└──────────────────────────────┘
# Istio를 이용한 Canary Deployment
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: myapp
spec:
  hosts:
    - myapp.example.com
  http:
    - route:
        - destination:
            host: myapp
            subset: stable
          weight: 95
        - destination:
            host: myapp
            subset: canary
          weight: 5
---
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
  name: myapp
spec:
  host: myapp
  subsets:
    - name: stable
      labels:
        version: v1
    - name: canary
      labels:
        version: v2

장점: 리스크 최소화, 실제 트래픽으로 검증, 점진적 롤아웃 단점: 구현 복잡성, 메트릭 기반 자동화 필요

1.4 배포 전략 비교

전략다운타임롤백 속도비용복잡도적합 시나리오
Rolling없음느림낮음낮음일반 서비스
Blue-Green없음즉시높음 (2배)중간중요 서비스
Canary없음빠름중간높음대규모 트래픽
A/B Testing없음빠름중간매우 높음기능 실험

2. GitOps

2.1 GitOps 원칙

┌──────────────────────────────────────────────────────┐
GitOps 원칙                                          │
│                                                      │
1. 선언적 (Declarative)- 시스템 상태를 코드로 선언                         │
│                                                      │
2. 버전 관리 (Versioned)- Git이 단일 진실의 원천(SSOT)│                                                      │
3. 자동 적용 (Automated)- 승인된 변경은 자동으로 시스템에 반영               │
│                                                      │
4. 자가 치유 (Self-healing)- 실제 상태와 원하는 상태의 차이를 자동 수정         │
└──────────────────────────────────────────────────────┘

2.2 ArgoCD 설정

# argocd-application.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: myapp-production
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/myorg/k8s-manifests.git
    targetRevision: main
    path: apps/myapp/overlays/production
  destination:
    server: https://kubernetes.default.svc
    namespace: production
  syncPolicy:
    automated:
      prune: true         # 불필요한 리소스 정리
      selfHeal: true      # 수동 변경 자동 복구
      allowEmpty: false
    syncOptions:
      - CreateNamespace=true
      - PrunePropagationPolicy=foreground
    retry:
      limit: 5
      backoff:
        duration: 5s
        factor: 2
        maxDuration: 3m

2.3 ArgoCD App-of-Apps 패턴

# root-application.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: root-app
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/myorg/k8s-manifests.git
    targetRevision: main
    path: apps
  destination:
    server: https://kubernetes.default.svc

# apps/ 디렉토리 구조
# apps/
#   myapp/
#     application.yaml
#   api-gateway/
#     application.yaml
#   monitoring/
#     application.yaml
#   cert-manager/
#     application.yaml

2.4 ArgoCD vs Flux 비교

기능ArgoCDFlux v2
UI 대시보드내장 웹 UIWeave GitOps (별도)
멀티 클러스터지원지원
Helm 지원네이티브HelmRelease CRD
Kustomize 지원네이티브네이티브
이미지 자동 업데이트ArgoCD Image Updater내장
알림ArgoCD NotificationsNotification Controller
RBAC내장Kubernetes RBAC 사용
접근 방식Pull 기반Pull 기반
학습 곡선중간중간-높음

2.5 ArgoCD 알림 설정

# argocd-notifications-cm.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: argocd-notifications-cm
  namespace: argocd
data:
  service.slack: |
    token: $slack-token
  trigger.on-sync-succeeded: |
    - when: app.status.sync.status == 'Synced'
      send: [app-sync-succeeded]
  trigger.on-sync-failed: |
    - when: app.status.sync.status == 'Unknown'
      send: [app-sync-failed]
  template.app-sync-succeeded: |
    slack:
      attachments: |
        [{
          "color": "#18be52",
          "title": "{{.app.metadata.name}} 동기화 성공",
          "text": "배포 완료: {{.app.spec.source.targetRevision}}"
        }]
  template.app-sync-failed: |
    slack:
      attachments: |
        [{
          "color": "#E96D76",
          "title": "{{.app.metadata.name}} 동기화 실패",
          "text": "오류: {{.app.status.conditions}}"
        }]

3. 모노레포 CI (Monorepo CI)

3.1 모노레포 구조

monorepo/
  apps/
    web/           # Next.js 프론트엔드
    api/           # Express 백엔드
    mobile/        # React Native  packages/
    ui/            # 공유 UI 컴포넌트
    utils/         # 공유 유틸리티
    config/        # 공유 설정
  turbo.json       # Turborepo 설정
  nx.json          # 또는 Nx 설정
  package.json

3.2 Turborepo 설정

{
  "globalDependencies": ["**/.env.*local"],
  "pipeline": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": ["dist/**", ".next/**"],
      "cache": true
    },
    "test": {
      "dependsOn": ["build"],
      "outputs": [],
      "cache": true
    },
    "lint": {
      "outputs": [],
      "cache": true
    },
    "deploy": {
      "dependsOn": ["build", "test", "lint"],
      "cache": false
    }
  }
}

3.3 Nx 변경 감지 기반 빌드

# .github/workflows/monorepo-ci.yml
name: Monorepo CI

on:
  pull_request:
    branches: [main]

jobs:
  affected:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0  # 전체 히스토리 필요

      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'npm'

      - run: npm ci

      # 변경된 프로젝트만 빌드/테스트
      - name: Lint affected
        run: npx nx affected --target=lint --base=origin/main

      - name: Test affected
        run: npx nx affected --target=test --base=origin/main

      - name: Build affected
        run: npx nx affected --target=build --base=origin/main

  # Remote Caching (Nx Cloud 또는 Turborepo Remote Cache)
  remote-cache:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci
      - name: Build with remote cache
        run: npx turbo build --team=myteam --token=${{ secrets.TURBO_TOKEN }}
        env:
          TURBO_TEAM: myteam
          TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}

3.4 Turborepo vs Nx vs Bazel 비교

기능TurborepoNxBazel
학습 곡선낮음중간높음
언어 지원JS/TSJS/TS, Go, Rust 등다중 언어
변경 감지파일 해시프로젝트 그래프의존성 그래프
원격 캐싱Vercel RCNx CloudRemote Execution
설정 복잡도매우 간단중간복잡
대규모 적합성중규모대규모초대규모
코드 생성없음내장 GeneratorRules

4. Feature Flags (피처 플래그)

4.1 피처 플래그의 유형

┌──────────────────────────────────────────────────┐
Feature Flag 유형                                │
├──────────────────────────────────────────────────┤
│                                                  │
1. Release Flag (릴리스 플래그)- 미완성 기능을 숨기고 배포                    │
- 수명: 짧음 (며칠 ~ 몇 주)│                                                  │
2. Experiment Flag (실험 플래그)- A/B 테스트, 기능 비교                       │
- 수명: 중간 (몇 주 ~ 몇 달)│                                                  │
3. Ops Flag (운영 플래그)- 시스템 동작 제어 (킬 스위치)- 수명: 영구적                                │
│                                                  │
4. Permission Flag (권한 플래그)- 사용자/조직별 기능 접근 제어                  │
- 수명: 영구적                                │
└──────────────────────────────────────────────────┘

4.2 구현 예제

// featureFlags.ts
import { UnleashClient } from 'unleash-proxy-client';

const unleash = new UnleashClient({
  url: 'https://unleash.myapp.com/api/frontend',
  clientKey: process.env.UNLEASH_CLIENT_KEY!,
  appName: 'myapp-frontend',
  refreshInterval: 15,
});

unleash.start();

// React Hook
export function useFeatureFlag(flagName: string): boolean {
  const [enabled, setEnabled] = useState(false);

  useEffect(() => {
    setEnabled(unleash.isEnabled(flagName));

    const handler = () => {
      setEnabled(unleash.isEnabled(flagName));
    };
    unleash.on('update', handler);
    return () => unleash.off('update', handler);
  }, [flagName]);

  return enabled;
}

// 컴포넌트에서 사용
function CheckoutPage() {
  const newCheckoutEnabled = useFeatureFlag('new-checkout-flow');

  if (newCheckoutEnabled) {
    return <NewCheckoutFlow />;
  }
  return <LegacyCheckoutFlow />;
}

4.3 Progressive Rollout (점진적 롤아웃)

// 서버 사이드 피처 플래그 (LaunchDarkly 스타일)
import LaunchDarkly from 'launchdarkly-node-server-sdk';

const client = LaunchDarkly.init(process.env.LD_SDK_KEY!);

async function getFeatureValue(
  flagKey: string,
  user: LDUser,
  defaultValue: boolean
): Promise<boolean> {
  await client.waitForInitialization();
  return client.variation(flagKey, user, defaultValue);
}

// 점진적 롤아웃 설정 (LaunchDarkly 대시보드 설정과 동일)
// Phase 1: 내부 직원만 (beta-testers 그룹)
// Phase 2: 5% 사용자
// Phase 3: 25% 사용자
// Phase 4: 50% 사용자
// Phase 5: 100% 사용자

// 킬 스위치 패턴
async function processPayment(order: Order) {
  const newPaymentEnabled = await getFeatureValue(
    'new-payment-gateway',
    { key: order.userId },
    false  // 기본값: 비활성화
  );

  if (newPaymentEnabled) {
    return newPaymentGateway.process(order);
  }
  return legacyPaymentGateway.process(order);
}

4.4 LaunchDarkly vs Unleash vs Flagsmith

기능LaunchDarklyUnleashFlagsmith
가격유료 (비쌈)오픈소스 + 엔터프라이즈오픈소스 + 클라우드
타겟팅매우 정교기본정교
A/B 테스트내장별도내장
SDK 지원25+ 언어15+ 언어15+ 언어
셀프호스팅불가가능가능
감사 로그지원지원지원

5. 환경 관리 (Environment Management)

5.1 환경 계층 구조

┌─────────────────────────────────────────┐
Production (프로덕션)- 실제 사용자 트래픽                    │
- 최고 수준의 모니터링                  │
- 변경 시 승인 프로세스                 │
├─────────────────────────────────────────┤
Staging (스테이징)- 프로덕션과 동일한 구성                │
- QA 팀 테스트                         │
- 통합 테스트 실행                      │
├─────────────────────────────────────────┤
Development (개발)- 최신 기능 통합                        │
- 자동 배포 (main 브랜치)├─────────────────────────────────────────┤
PR Preview (PR별 미리보기)- PR마다 독립 환경 생성                 │
- PR 닫히면 자동 삭제                   │
└─────────────────────────────────────────┘

5.2 Ephemeral Environments (임시 환경)

# .github/workflows/preview.yml
name: PR Preview Environment

on:
  pull_request:
    types: [opened, synchronize, reopened]

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

      - name: Deploy Preview
        run: |
          PREVIEW_URL="pr-${{ github.event.number }}.preview.myapp.com"
          helm upgrade --install \
            pr-${{ github.event.number }} \
            ./charts/myapp \
            --namespace previews \
            --set image.tag=${{ github.sha }} \
            --set ingress.host=$PREVIEW_URL \
            --set resources.requests.memory=256Mi \
            --set resources.requests.cpu=100m

      - name: Comment PR with URL
        uses: actions/github-script@v7
        with:
          script: |
            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: `Preview deployed: https://pr-${context.issue.number}.preview.myapp.com`
            })

  cleanup-preview:
    if: github.event.action == 'closed'
    runs-on: ubuntu-latest
    steps:
      - name: Delete Preview
        run: |
          helm uninstall pr-${{ github.event.number }} \
            --namespace previews

6. 파이프라인 보안 (Pipeline Security)

6.1 Secrets 관리

# GitHub Actions - OIDC를 이용한 AWS 인증 (비밀키 없이)
name: Secure Deploy

permissions:
  id-token: write
  contents: read

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

      # OIDC 기반 AWS 인증 (장기 비밀키 불필요)
      - name: Configure AWS Credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::123456789012:role/github-actions-role
          aws-region: ap-northeast-2

      # 시크릿 스캔
      - name: Scan for secrets
        uses: trufflesecurity/trufflehog@v3
        with:
          path: ./
          extra_args: --only-verified

      # 컨테이너 이미지 스캔
      - name: Scan container image
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: myapp:${{ github.sha }}
          severity: 'CRITICAL,HIGH'
          exit-code: '1'

6.2 SLSA (Supply-chain Levels for Software Artifacts)

# SLSA Level 3 - 서명된 빌드 + 출처 증명
name: SLSA Build

jobs:
  build:
    runs-on: ubuntu-latest
    permissions:
      id-token: write
      contents: read
      attestations: write
    steps:
      - uses: actions/checkout@v4

      - name: Build
        run: npm run build

      # Sigstore를 이용한 아티팩트 서명
      - name: Sign artifact
        uses: sigstore/cosign-installer@v3

      - name: Sign container image
        run: |
          cosign sign \
            --yes \
            myregistry/myapp:${{ github.sha }}

      # SLSA 출처 증명 생성
      - name: Generate SLSA provenance
        uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v2.0.0
        with:
          image: myregistry/myapp
          digest: ${{ steps.build.outputs.digest }}

6.3 보안 스캔 파이프라인

# .github/workflows/security.yml
name: Security Pipeline

on:
  pull_request:
    branches: [main]
  schedule:
    - cron: '0 6 * * 1'  # 매주 월요일 06:00

jobs:
  sast:
    name: Static Analysis
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Run Semgrep
        uses: semgrep/semgrep-action@v1
        with:
          config: p/owasp-top-ten

  dependency-check:
    name: Dependency Audit
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm audit --audit-level=high
      - name: Snyk Test
        uses: snyk/actions/node@master
        env:
          SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}

  container-scan:
    name: Container Security
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Build image
        run: docker build -t myapp:scan .
      - name: Trivy scan
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: myapp:scan
          severity: 'CRITICAL,HIGH'
          format: 'sarif'
          output: 'trivy-results.sarif'
      - name: Upload SARIF
        uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: 'trivy-results.sarif'

  iac-scan:
    name: Infrastructure as Code Scan
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Checkov scan
        uses: bridgecrewio/checkov-action@master
        with:
          directory: ./terraform
          framework: terraform

7. 롤백 전략 (Rollback Strategies)

7.1 즉시 롤백

# Kubernetes 롤백
# 마지막 성공 버전으로 즉시 롤백
# kubectl rollout undo deployment/myapp

# GitHub Actions 자동 롤백
name: Deploy with Auto-Rollback

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Deploy new version
        id: deploy
        run: |
          kubectl set image deployment/myapp \
            myapp=myregistry/myapp:${{ github.sha }}
          kubectl rollout status deployment/myapp --timeout=300s

      - name: Run post-deploy checks
        id: healthcheck
        run: |
          for i in $(seq 1 10); do
            STATUS=$(curl -s -o /dev/null -w "%{http_code}" https://myapp.com/health)
            if [ "$STATUS" != "200" ]; then
              echo "Health check failed: $STATUS"
              exit 1
            fi
            sleep 5
          done

      - name: Rollback on failure
        if: failure()
        run: |
          echo "Rolling back to previous version..."
          kubectl rollout undo deployment/myapp
          kubectl rollout status deployment/myapp --timeout=300s

7.2 데이터베이스 인식 롤백

배포 과정에서 DB 스키마 변경이 있는 경우:

1. Forward-Compatible 마이그레이션 (안전)
   ┌─────────────────────────────────────┐
Step 1: DB에 새 컬럼 추가 (nullable)Step 2: 앱 v2 배포 (두 컬럼 모두 사용)Step 3: 데이터 마이그레이션          │
Step 4: 이전 컬럼 제거              │
   └─────────────────────────────────────┘

2. Expand-Contract 패턴
   Phase 1 (Expand): 새 스키마 추가, 기존 유지
   Phase 2 (Migrate): 데이터 이전
   Phase 3 (Contract): 이전 스키마 제거

7.3 Forward-Fix vs Rollback 결정 기준

상황권장 전략이유
단순 버그 수정Forward-Fix롤백보다 수정이 빠름
심각한 장애즉시 롤백서비스 복구가 최우선
DB 마이그레이션 포함Forward-Fix롤백 시 데이터 손실 위험
성능 저하카나리 롤백영향 범위 파악 후 결정

8. 파이프라인 성능 최적화

8.1 캐싱 전략

# .github/workflows/optimized-ci.yml
name: Optimized CI

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

      # Node.js 의존성 캐싱
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'npm'

      # Docker 레이어 캐싱
      - uses: docker/setup-buildx-action@v3
      - uses: docker/build-push-action@v6
        with:
          context: .
          push: true
          tags: myapp:${{ github.sha }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

      # Turborepo 원격 캐싱
      - name: Build with cache
        run: npx turbo build --cache-dir=.turbo
        env:
          TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
          TURBO_TEAM: myteam

8.2 병렬화 전략

# 병렬 잡 실행
jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci
      - run: npm run lint

  unit-test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci
      - run: npm run test:unit

  type-check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci
      - run: npm run type-check

  # 모든 검증 통과 후 빌드
  build:
    needs: [lint, unit-test, type-check]
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci
      - run: npm run build

8.3 Self-Hosted Runners

# 자체 호스팅 러너 (ARM64 Mac 등)
jobs:
  build-ios:
    runs-on: [self-hosted, macOS, ARM64]
    steps:
      - uses: actions/checkout@v4
      - name: Build iOS
        run: xcodebuild -workspace MyApp.xcworkspace ...

  build-android:
    runs-on: [self-hosted, Linux, X64]
    steps:
      - uses: actions/checkout@v4
      - name: Build Android
        run: ./gradlew assembleRelease

9. 릴리스 관리 (Release Management)

9.1 Semantic Versioning

MAJOR.MINOR.PATCH (: 2.1.3)

MAJOR: 호환되지 않는 API 변경
MINOR: 하위 호환 기능 추가
PATCH: 하위 호환 버그 수정

예시:
  1.0.01.0.1  (버그 수정)
  1.0.11.1.0  (새 기능 추가)
  1.1.02.0.0  (Breaking Change)

9.2 자동 릴리스 노트

# .github/workflows/release.yml
name: Release

on:
  push:
    tags: ['v*']

permissions:
  contents: write

jobs:
  release:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Generate Changelog
        id: changelog
        uses: orhun/git-cliff-action@v3
        with:
          config: cliff.toml

      - name: Create Release
        uses: softprops/action-gh-release@v2
        with:
          body: ${{ steps.changelog.outputs.content }}
          draft: false
          prerelease: false

9.3 Conventional Commits

# 커밋 메시지 형식
# type(scope): description

feat(auth): add OAuth2.0 login support
fix(cart): resolve quantity update race condition
docs(api): update REST API documentation
chore(deps): upgrade express to v5.0
perf(db): optimize query for order listing
refactor(payment): extract payment strategy pattern
test(checkout): add E2E tests for checkout flow

# Breaking Change 표기
feat(api)!: change response format for /orders endpoint

BREAKING CHANGE: The orders endpoint now returns
paginated responses instead of a flat array.

9.4 Release Train 모델

┌─────────────────────────────────────────────┐
Release Train (격주 릴리스)│                                             │
Week 1: 기능 개발 + 코드 동결(Code Freeze)Week 2: QA + 버그 수정 + 릴리스              │
│                                             │
│ ──┬──────┬──────┬──────┬──────┬──           │
│   v2.1   v2.2   v2.3   v2.4   v2.5│                                             │
│ 핫픽스는 별도 브랜치에서 즉시 배포             │
└─────────────────────────────────────────────┘

10. 멀티 클라우드 배포

10.1 Terraform을 이용한 멀티 클라우드

# main.tf - 멀티 클라우드 프로바이더
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
    google = {
      source  = "hashicorp/google"
      version = "~> 5.0"
    }
  }
}

# AWS 배포
resource "aws_ecs_service" "primary" {
  name            = "myapp-primary"
  cluster         = aws_ecs_cluster.main.id
  task_definition = aws_ecs_task_definition.app.arn
  desired_count   = 3

  load_balancer {
    target_group_arn = aws_lb_target_group.app.arn
    container_name   = "myapp"
    container_port   = 8080
  }
}

# GCP 배포 (DR)
resource "google_cloud_run_service" "failover" {
  name     = "myapp-failover"
  location = "asia-northeast3"

  template {
    spec {
      containers {
        image = "gcr.io/myproject/myapp:latest"
      }
    }
  }
}

11. 실전 퀴즈

Q1: Blue-Green 배포의 가장 큰 장점은?

A) 인프라 비용이 저렴하다 B) 즉시 롤백이 가능하다 C) 구현이 간단하다 D) 트래픽 분할이 가능하다

정답: B

Blue-Green 배포는 두 개의 동일한 환경을 유지하므로, 문제 발생 시 로드밸런서의 트래픽을 이전 환경(Blue)으로 즉시 전환하여 롤백할 수 있습니다. 다만 2배의 인프라 비용이 단점입니다.

Q2: GitOps에서 Git 저장소의 역할은?

A) 코드 백업 저장소 B) CI/CD 파이프라인 실행기 C) 인프라의 단일 진실의 원천(SSOT) D) 모니터링 대시보드

정답: C

GitOps에서 Git 저장소는 인프라와 애플리케이션 상태의 단일 진실의 원천(Single Source of Truth)입니다. 모든 변경은 Git을 통해 이루어지며, ArgoCD/Flux 같은 도구가 Git 상태와 실제 클러스터 상태를 동기화합니다.

Q3: 모노레포 CI에서 "affected" 빌드의 장점은?

A) 모든 프로젝트를 항상 빌드한다 B) 변경된 프로젝트와 영향받는 프로젝트만 빌드하여 시간을 절약한다 C) 빌드 캐시를 무시한다 D) 테스트를 건너뛴다

정답: B

"affected" 빌드는 Git diff를 분석하여 변경된 프로젝트와 그에 의존하는 프로젝트만 선택적으로 빌드/테스트합니다. 이를 통해 대규모 모노레포에서 CI 시간을 크게 단축할 수 있습니다.

Q4: Feature Flag의 "킬 스위치" 용도는?

A) 서버를 종료한다 B) 문제가 있는 기능을 코드 배포 없이 즉시 비활성화한다 C) 데이터베이스를 삭제한다 D) CI 파이프라인을 중단한다

정답: B

킬 스위치는 프로덕션에서 문제가 발생했을 때, 새로운 코드 배포 없이 피처 플래그를 OFF로 전환하여 해당 기능을 즉시 비활성화하는 패턴입니다. 롤백보다 훨씬 빠르게 대응할 수 있습니다.

Q5: SLSA (Supply-chain Levels for Software Artifacts)의 목적은?

A) 코드 성능 최적화 B) 소프트웨어 공급망의 무결성을 보장한다 C) 데이터베이스 보안 D) UI 디자인 검증

정답: B

SLSA는 소프트웨어 공급망 공격을 방지하기 위한 보안 프레임워크입니다. 빌드 프로세스의 출처 증명, 아티팩트 서명, 빌드 환경의 격리 등을 통해 소프트웨어가 변조되지 않았음을 보장합니다.


12. CI/CD 성숙도 체크리스트

CI/CD 성숙도 체크리스트:

Level 1 - 기본
[  ] 자동 빌드 파이프라인
[  ] 자동 테스트 실행
[  ] 코드 리뷰 프로세스

Level 2 - 표준
[  ] 배포 전략 (Rolling/Blue-Green)
[  ] 환경 분리 (dev/staging/prod)
[  ] 시크릿 관리 (Vault/OIDC)

Level 3 - 고급
[  ] GitOps (ArgoCD/Flux)
[  ] Feature Flags
[  ] Canary 배포
[  ] PR Preview 환경

Level 4 - 엘리트
[  ] 파이프라인 보안 (SLSA)
[  ] 자동 롤백
[  ] 모노레포 최적화
[  ] 릴리스 자동화

13. 참고 자료

  1. DORA Metrics: dora.dev
  2. ArgoCD Documentation: argo-cd.readthedocs.io
  3. Flux Documentation: fluxcd.io
  4. Turborepo Documentation: turbo.build
  5. Nx Documentation: nx.dev
  6. LaunchDarkly Docs: docs.launchdarkly.com
  7. Unleash Documentation: docs.getunleash.io
  8. SLSA Framework: slsa.dev
  9. Sigstore / Cosign: sigstore.dev
  10. Semgrep: semgrep.dev
  11. Conventional Commits: conventionalcommits.org
  12. Git Cliff (Changelog): git-cliff.org
  13. GitHub Actions Docs: docs.github.com/actions
  14. Kubernetes Deployment Strategies: kubernetes.io/docs