Choorai
Cycle 8

CI/CD - 자동 배포

코드를 푸시하면 자동으로 빌드, 테스트, 배포까지. 수동 배포의 반복 작업에서 벗어나 개발에만 집중하세요.

이 Cycle에서 배우는 것

  • CI/CD 개념과 파이프라인 이해
  • GitHub Actions 워크플로우 작성
  • 프론트엔드/백엔드 자동 배포 구축
  • 환경별 배포 전략 수립

CI/CD란?

CI/CD는 코드 변경부터 배포까지의 과정을 자동화하는 방법론입니다.

CI (Continuous Integration)

코드 변경을 자주 통합하고 자동으로 검증

코드 푸시
자동 빌드
자동 테스트

CD (Continuous Deployment)

검증된 코드를 자동으로 프로덕션에 배포

빌드 성공
자동 배포
릴리스 완료

파이프라인 흐름

📝 Push
🔨 Build
🧪 Test
🚀 Deploy

왜 자동화가 필요한가?

  • 휴먼 에러 방지: 수동 작업의 실수 제거
  • 빠른 피드백: 문제를 즉시 발견
  • 일관성: 매번 동일한 과정으로 배포
  • 시간 절약: 개발에 집중할 시간 확보

GitHub Actions 기본

GitHub Actions는 GitHub에 내장된 무료 CI/CD 도구입니다. 별도 서버 없이 코드 저장소에서 바로 사용할 수 있습니다.

워크플로우 파일 위치

프로젝트 구조
my-project/
├── .github/
│   └── workflows/
│       ├── ci.yml          # CI 워크플로우
│       ├── deploy.yml      # 배포 워크플로우
│       └── test.yml        # 테스트 워크플로우
├── src/
└── ...

YAML 문법 핵심

.github/workflows/hello.yml
name: Hello World CI  # 워크플로우 이름

on:  # 트리거 조건
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:  # 실행할 작업들
  build:
    runs-on: ubuntu-latest  # 실행 환경

    steps:  # 순차 실행할 단계들
      - name: 코드 체크아웃
        uses: actions/checkout@v4

      - name: Hello World 출력
        run: echo "Hello, GitHub Actions!"

트리거 종류

트리거 설명 예시
push 코드 푸시 시 branches: [main]
pull_request PR 생성/업데이트 시 types: [opened, synchronize]
schedule 정해진 시간에 (cron) cron: '0 0 * * *'
workflow_dispatch 수동 실행 GitHub UI에서 버튼 클릭

자주 사용하는 액션

actions/checkout@v4

저장소 코드를 체크아웃

actions/setup-node@v4

Node.js 환경 설정

actions/setup-python@v5

Python 환경 설정

actions/cache@v4

의존성 캐싱

프론트엔드 자동 배포

React/Vue 프로젝트를 Cloudflare Pages에 자동 배포합니다.

Cloudflare Pages 배포 워크플로우

.github/workflows/deploy-frontend.yml
name: Deploy Frontend

on:
  push:
    branches: [main]
    paths:
      - 'frontend/**'  # frontend 폴더 변경 시만 실행

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'
          cache-dependency-path: frontend/package-lock.json

      - name: Install dependencies
        working-directory: frontend
        run: npm ci

      - name: Build
        working-directory: frontend
        run: npm run build
        env:
          VITE_API_URL: ${{ secrets.VITE_API_URL }}

      - name: Deploy to Cloudflare Pages
        uses: cloudflare/pages-action@v1
        with:
          apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
          accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
          projectName: my-frontend
          directory: frontend/dist

Secrets 설정 방법

  1. GitHub 저장소 → SettingsSecrets and variablesActions
  2. New repository secret 클릭
  3. Name: CLOUDFLARE_API_TOKEN
  4. Value: Cloudflare 대시보드에서 생성한 API 토큰

Cloudflare API 토큰 생성

  1. Cloudflare 대시보드 → My Profile → API Tokens
  2. Create Token → Edit Cloudflare Workers 템플릿 사용
  3. Account Resources: 본인 계정 선택
  4. Zone Resources: All zones (또는 특정 도메인)

백엔드 자동 배포

FastAPI/Node.js 백엔드를 Cloud Run에 자동 배포합니다.

FastAPI → Cloud Run 배포

.github/workflows/deploy-backend.yml (FastAPI)
name: Deploy Backend (FastAPI)

on:
  push:
    branches: [main]
    paths:
      - 'backend/**'

env:
  PROJECT_ID: my-gcp-project
  REGION: asia-northeast3
  SERVICE_NAME: my-api

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Google Auth
        uses: google-github-actions/auth@v2
        with:
          credentials_json: ${{ secrets.GCP_SA_KEY }}

      - name: Setup Cloud SDK
        uses: google-github-actions/setup-gcloud@v2

      - name: Configure Docker
        run: gcloud auth configure-docker ${{ env.REGION }}-docker.pkg.dev

      - name: Build and Push Docker Image
        working-directory: backend
        run: |
          docker build -t ${{ env.REGION }}-docker.pkg.dev/${{ env.PROJECT_ID }}/my-repo/${{ env.SERVICE_NAME }}:${{ github.sha }} .
          docker push ${{ env.REGION }}-docker.pkg.dev/${{ env.PROJECT_ID }}/my-repo/${{ env.SERVICE_NAME }}:${{ github.sha }}

      - name: Deploy to Cloud Run
        run: |
          gcloud run deploy ${{ env.SERVICE_NAME }} \
            --image=${{ env.REGION }}-docker.pkg.dev/${{ env.PROJECT_ID }}/my-repo/${{ env.SERVICE_NAME }}:${{ github.sha }} \
            --region=${{ env.REGION }} \
            --platform=managed \
            --allow-unauthenticated \
            --set-env-vars="DATABASE_URL=${{ secrets.DATABASE_URL }}"

Node.js (Hono) → Cloud Run 배포

.github/workflows/deploy-backend.yml (Hono)
name: Deploy Backend (Hono)

on:
  push:
    branches: [main]
    paths:
      - 'backend/**'

env:
  PROJECT_ID: my-gcp-project
  REGION: asia-northeast3
  SERVICE_NAME: my-api

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Google Auth
        uses: google-github-actions/auth@v2
        with:
          credentials_json: ${{ secrets.GCP_SA_KEY }}

      - name: Setup Cloud SDK
        uses: google-github-actions/setup-gcloud@v2

      - name: Configure Docker
        run: gcloud auth configure-docker ${{ env.REGION }}-docker.pkg.dev

      - name: Build and Push Docker Image
        working-directory: backend
        run: |
          docker build -t ${{ env.REGION }}-docker.pkg.dev/${{ env.PROJECT_ID }}/my-repo/${{ env.SERVICE_NAME }}:${{ github.sha }} .
          docker push ${{ env.REGION }}-docker.pkg.dev/${{ env.PROJECT_ID }}/my-repo/${{ env.SERVICE_NAME }}:${{ github.sha }}

      - name: Deploy to Cloud Run
        run: |
          gcloud run deploy ${{ env.SERVICE_NAME }} \
            --image=${{ env.REGION }}-docker.pkg.dev/${{ env.PROJECT_ID }}/my-repo/${{ env.SERVICE_NAME }}:${{ github.sha }} \
            --region=${{ env.REGION }} \
            --platform=managed \
            --allow-unauthenticated

GCP 서비스 계정 키 생성

터미널
# 1. 서비스 계정 생성
gcloud iam service-accounts create github-actions \
  --display-name="GitHub Actions"

# 2. 필요한 역할 부여
gcloud projects add-iam-policy-binding PROJECT_ID \
  --member="serviceAccount:github-actions@PROJECT_ID.iam.gserviceaccount.com" \
  --role="roles/run.admin"

gcloud projects add-iam-policy-binding PROJECT_ID \
  --member="serviceAccount:github-actions@PROJECT_ID.iam.gserviceaccount.com" \
  --role="roles/storage.admin"

gcloud projects add-iam-policy-binding PROJECT_ID \
  --member="serviceAccount:github-actions@PROJECT_ID.iam.gserviceaccount.com" \
  --role="roles/iam.serviceAccountUser"

# 3. 키 생성 (JSON 파일)
gcloud iam service-accounts keys create key.json \
  --iam-account=github-actions@PROJECT_ID.iam.gserviceaccount.com

# 4. key.json 내용을 GCP_SA_KEY secret에 등록

환경별 배포 (dev/staging/prod)

환경별 배포 예시
name: Deploy by Branch

on:
  push:
    branches:
      - main        # 프로덕션
      - staging     # 스테이징
      - develop     # 개발

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:
      - name: Set environment
        run: |
          if [ "${{ github.ref }}" = "refs/heads/main" ]; then
            echo "ENV=prod" >> $GITHUB_ENV
            echo "SERVICE_NAME=my-api" >> $GITHUB_ENV
          elif [ "${{ github.ref }}" = "refs/heads/staging" ]; then
            echo "ENV=staging" >> $GITHUB_ENV
            echo "SERVICE_NAME=my-api-staging" >> $GITHUB_ENV
          else
            echo "ENV=dev" >> $GITHUB_ENV
            echo "SERVICE_NAME=my-api-dev" >> $GITHUB_ENV
          fi

      # ... 이후 배포 단계에서 ${{ env.SERVICE_NAME }} 사용

고급 패턴

Matrix 빌드

여러 버전/환경에서 동시에 테스트합니다.

Matrix 빌드
jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: [18, 20, 22]
        os: [ubuntu-latest, macos-latest]

    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}
      - run: npm ci
      - run: npm test

캐싱으로 빌드 속도 개선

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

# Python 의존성 캐싱
- uses: actions/setup-python@v5
  with:
    python-version: '3.11'
    cache: 'pip'  # 자동 캐싱!

# Docker 레이어 캐싱
- uses: docker/build-push-action@v5
  with:
    cache-from: type=gha
    cache-to: type=gha,mode=max

조건부 실행

조건부 실행
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Build
        run: npm run build

  deploy:
    needs: build  # build 완료 후 실행
    if: github.ref == 'refs/heads/main'  # main 브랜치만
    runs-on: ubuntu-latest
    steps:
      - name: Deploy
        run: echo "Deploying..."

무료 티어 & 팁

GitHub Actions 무료 한도

플랜 무료 제공량
Public 저장소 무제한
Private (Free 플랜) 2,000분/월
Private (Pro 플랜) 3,000분/월

비용 최적화 팁

  • 캐싱 활용: 의존성 캐싱으로 빌드 시간 단축
  • 경로 필터: 변경된 폴더만 빌드 (paths 옵션)
  • 조건부 실행: 불필요한 워크플로우 스킵
  • Self-hosted Runner: 대량 빌드 시 자체 러너 사용

자주 발생하는 오류

❌ Permission denied

서비스 계정에 필요한 역할이 없음

→ IAM에서 roles/run.admin 등 역할 추가

❌ Secret not found

Secrets에 등록되지 않은 변수 참조

→ Settings → Secrets에서 변수 추가

❌ Docker build failed

로컬에서는 되는데 CI에서 실패

→ .dockerignore 확인, 환경변수 확인

🎉 학습 사이클 완주!

축하합니다! 모든 학습 사이클을 완료했습니다. 이제 도메인 설정부터 자동 배포까지 전체 웹 서비스 배포 파이프라인을 구축할 수 있습니다. Agent Recipes를 활용해서 실제 프로젝트를 시작해보세요!

마지막 업데이트: 2026년 2월 22일 · 버전: v0.0.1

피드백 보내기

입력한 내용으로 새 이슈 페이지를 엽니다.

GitHub 이슈로 보내기