DevOps·2분 읽기

DevOps CI/CD 파이프라인 구축 가이드

GitHub Actions를 활용한 CI/CD 파이프라인 구축 방법을 알아봅니다. 자동 테스트, 빌드, 배포까지 DevOps 워크플로우 설계 핵심을 다룹니다.

공유:

DevOps CI/CD 파이프라인 구축 가이드

소프트웨어 개발에서 CI/CD(지속적 통합/지속적 배포)는 코드 변경사항을 자동으로 테스트하고 배포하는 프로세스입니다. 수동 배포의 위험성과 비효율성을 제거하고, 개발팀이 코드 품질에 집중할 수 있도록 도와줍니다.

CI/CD의 기본 개념

CI (Continuous Integration) - 지속적 통합

CI는 개발자들이 코드 변경사항을 자주 메인 브랜치에 병합하는 관행입니다. 각 병합 시 자동으로 빌드와 테스트가 실행되어 문제를 조기에 발견할 수 있습니다.

CI의 핵심 원칙은 다음과 같습니다.

  • 코드를 자주 커밋하고 병합합니다 (하루에 최소 1회 이상)
  • 모든 변경사항에 대해 자동 테스트를 실행합니다
  • 빌드가 깨지면 즉시 수정합니다
  • 테스트 커버리지를 지속적으로 관리합니다

CD (Continuous Delivery/Deployment) - 지속적 전달/배포

CD는 CI를 통과한 코드를 자동으로 스테이징 또는 프로덕션 환경에 배포하는 과정입니다.

  • Continuous Delivery: 프로덕션 배포 전 수동 승인 필요
  • Continuous Deployment: 모든 변경사항이 자동으로 프로덕션 배포
개발자 커밋 → 자동 빌드 → 자동 테스트 → 코드 리뷰 → 스테이징 배포 → 프로덕션 배포
    [CI 영역]                              [CD 영역]

GitHub Actions로 CI/CD 구축하기

GitHub Actions는 GitHub에 내장된 CI/CD 플랫폼으로, YAML 파일로 워크플로우를 정의합니다. 무료 티어에서도 월 2,000분의 실행 시간을 제공하므로 개인 프로젝트에 적합합니다.

기본 워크플로우 구조

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

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

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: [18, 20]
    steps:
      - uses: actions/checkout@v4
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}
          cache: 'npm'
      - run: npm ci
      - run: npm run lint
      - run: npm test
      - run: npm run build

주요 구성 요소 설명

트리거(on): 워크플로우가 실행되는 조건을 정의합니다. push, pull_request, schedule 등 다양한 이벤트를 지원합니다.

잡(jobs): 독립적으로 실행되는 작업 단위입니다. 각 잡은 별도의 가상 머신에서 실행되며 병렬 처리가 가능합니다.

스텝(steps): 잡 내에서 순차적으로 실행되는 단계입니다. 커맨드 실행이나 액션(Action) 사용이 가능합니다.

실전 파이프라인 설계

Node.js 프로젝트 전체 파이프라인

name: Full Pipeline

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

env:
  NODE_VERSION: '20'

jobs:
  # 1단계: 코드 품질 검사
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: 'npm'
      - run: npm ci
      - run: npm run lint
      - run: npx tsc --noEmit

  # 2단계: 단위 테스트
  unit-test:
    runs-on: ubuntu-latest
    needs: lint
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: 'npm'
      - run: npm ci
      - run: npm test -- --coverage
      - name: Upload coverage
        uses: actions/upload-artifact@v4
        with:
          name: coverage
          path: coverage/

  # 3단계: 빌드
  build:
    runs-on: ubuntu-latest
    needs: unit-test
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: 'npm'
      - run: npm ci
      - run: npm run build
      - name: Upload build
        uses: actions/upload-artifact@v4
        with:
          name: build
          path: .next/

  # 4단계: 배포 (main 브랜치만)
  deploy:
    runs-on: ubuntu-latest
    needs: build
    if: github.ref == 'refs/heads/main'
    steps:
      - uses: actions/checkout@v4
      - name: Deploy to Vercel
        uses: amondnet/vercel-action@v25
        with:
          vercel-token: ${{ secrets.VERCEL_TOKEN }}
          vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
          vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
          vercel-args: '--prod'

의존성 캐싱 전략

빌드 시간을 단축하려면 의존성 캐싱이 중요합니다. actions/cachesetup-node의 cache 옵션을 활용하면 npm 패키지를 재다운로드하지 않아도 됩니다.

- uses: actions/setup-node@v4
  with:
    node-version: '20'
    cache: 'npm'  # package-lock.json 기반 자동 캐싱

환경 분리와 시크릿 관리

환경별 설정

프로덕션, 스테이징, 개발 환경을 분리하는 것은 안전한 배포의 기본입니다.

deploy-staging:
  environment: staging
  runs-on: ubuntu-latest
  steps:
    - name: Deploy
      env:
        DATABASE_URL: ${{ secrets.STAGING_DATABASE_URL }}
        API_KEY: ${{ secrets.STAGING_API_KEY }}
      run: npm run deploy:staging

deploy-production:
  environment: production
  runs-on: ubuntu-latest
  needs: deploy-staging
  steps:
    - name: Deploy
      env:
        DATABASE_URL: ${{ secrets.PROD_DATABASE_URL }}
        API_KEY: ${{ secrets.PROD_API_KEY }}
      run: npm run deploy:prod

시크릿 관리 모범 사례

민감한 정보는 GitHub Secrets에 저장하고, 워크플로우에서 참조합니다. 시크릿은 로그에 마스킹되어 출력되므로 안전합니다.

  • API 키, 데이터베이스 URL 등은 반드시 시크릿으로 관리합니다
  • 환경별로 시크릿을 분리합니다 (PROD_, STAGING_ 접두사 활용)
  • 정기적으로 시크릿을 로테이션합니다
  • 최소 권한 원칙을 적용합니다

테스트 자동화 전략

테스트 피라미드

효과적인 테스트 전략은 테스트 피라미드를 따릅니다.

        /\
       /  \      E2E 테스트 (적음, 느림, 비쌈)
      /    \     Cypress, Playwright
     /------\
    /        \   통합 테스트 (중간)
   /          \  Supertest, Testing Library
  /------------\
 /              \ 단위 테스트 (많음, 빠름, 저렴)
/________________\ Jest, Vitest

PR 자동 검사

Pull Request가 생성되면 자동으로 코드 품질을 검사하는 워크플로우를 설정할 수 있습니다.

name: PR Check

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

jobs:
  check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'
      - run: npm ci
      - run: npm run lint
      - run: npm test
      - run: npm run build
      - name: Comment PR
        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: '모든 검사를 통과했습니다!'
            })

Docker를 활용한 배포

컨테이너 기반 배포는 환경 일관성을 보장합니다. Dockerfile을 작성하고 GitHub Actions에서 이미지를 빌드하여 레지스트리에 푸시할 수 있습니다.

멀티 스테이지 Dockerfile

# 빌드 스테이지
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# 프로덕션 스테이지
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
COPY --from=builder /app/.next ./.next
COPY --from=builder /app/public ./public
COPY --from=builder /app/package*.json ./
RUN npm ci --omit=dev
EXPOSE 3000
CMD ["npm", "start"]

모니터링과 알림

배포 후 모니터링은 서비스 안정성의 핵심입니다.

Slack 알림 설정

- name: Slack Notification
  uses: 8398a7/action-slack@v3
  with:
    status: ${{ job.status }}
    text: |
      배포 결과: ${{ job.status }}
      커밋: ${{ github.sha }}
      브랜치: ${{ github.ref_name }}
  env:
    SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}
  if: always()

마무리

CI/CD 파이프라인은 현대 소프트웨어 개발의 필수 인프라입니다. GitHub Actions를 활용하면 별도의 CI 서버 없이도 강력한 자동화 파이프라인을 구축할 수 있습니다. 처음에는 간단한 파이프라인으로 시작하고, 프로젝트가 성장하면서 테스트, 보안 스캔, 성능 테스트 등을 점진적으로 추가하는 것을 권장합니다. 자동화에 투자하는 시간은 반드시 개발 생산성으로 돌아옵니다.

#DevOps#CI/CD#GitHub Actions#자동화#배포