CI/CD란?
CI/CD는 코드 변경부터 배포까지의 과정을 자동화하는 방법론입니다.
CI (Continuous Integration)
코드 변경을 자주 통합하고 자동으로 검증
CD (Continuous Deployment)
검증된 코드를 자동으로 프로덕션에 배포
파이프라인 흐름
왜 자동화가 필요한가?
- 휴먼 에러 방지: 수동 작업의 실수 제거
- 빠른 피드백: 문제를 즉시 발견
- 일관성: 매번 동일한 과정으로 배포
- 시간 절약: 개발에 집중할 시간 확보
GitHub Actions 기본
GitHub Actions는 GitHub에 내장된 무료 CI/CD 도구입니다. 별도 서버 없이 코드 저장소에서 바로 사용할 수 있습니다.
워크플로우 파일 위치
my-project/
├── .github/
│ └── workflows/
│ ├── ci.yml # CI 워크플로우
│ ├── deploy.yml # 배포 워크플로우
│ └── test.yml # 테스트 워크플로우
├── src/
└── ...YAML 문법 핵심
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 배포 워크플로우
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/distSecrets 설정 방법
- GitHub 저장소 → Settings → Secrets and variables → Actions
- New repository secret 클릭
- Name:
CLOUDFLARE_API_TOKEN - Value: Cloudflare 대시보드에서 생성한 API 토큰
Cloudflare API 토큰 생성
- Cloudflare 대시보드 → My Profile → API Tokens
- Create Token → Edit Cloudflare Workers 템플릿 사용
- Account Resources: 본인 계정 선택
- Zone Resources: All zones (또는 특정 도메인)
백엔드 자동 배포
FastAPI/Node.js 백엔드를 Cloud Run에 자동 배포합니다.
FastAPI → Cloud Run 배포
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 배포
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-unauthenticatedGCP 서비스 계정 키 생성
# 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 빌드
여러 버전/환경에서 동시에 테스트합니다.
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를 활용해서 실제 프로젝트를 시작해보세요!