GitHub Actions CI/CD: 처음부터 시작하는 빌드, 테스트, 배포 워크플로우
지속적 통합 및 배포를 위한 GitHub Actions 설정 방법을 알아보세요. 트리거, 잡, 매트릭스, 시크릿, 캐싱, Docker, 실제 워크플로우 예시를 다룹니다.
GitHub Actions는 모든 푸시, 풀 리퀘스트, 머지를 자동화된 파이프라인으로 전환합니다. 테스트 실행, 아티팩트 빌드, 취약점 스캔, 프로덕션 배포까지 — 관리할 인프라 없이 저장소 안에서 바로 동작하는 CI/CD 플랫폼입니다.
GitHub Actions 동작 원리
모든 워크플로우는 .github/workflows/ 안의 YAML 파일입니다. 트리거 이벤트가 발생하면 GitHub가 새로운 가상 머신을 실행하고, 잡을 처리한 뒤 결과를 보고합니다 — 모두 수 초 안에 이루어집니다.
main에 푸시
↓
워크플로우 트리거됨
↓
잡: 빌드 & 테스트 (ubuntu-latest)
├── 1단계: 코드 체크아웃
├── 2단계: Node.js 설치
├── 3단계: npm ci
├── 4단계: npm test
└── 5단계: npm run build
↓
✅ 모든 검사 통과
첫 번째 워크플로우
.github/workflows/ci.yml 파일을 생성하세요:
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
build:
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 test
- run: npm run build
이게 전부입니다. 이제 main에 대한 모든 푸시와 PR에서 테스트 스위트가 자동으로 실행됩니다.
트리거: 워크플로우 실행 시점
코드 이벤트
on:
push:
branches: [main, develop]
paths: ['src/**', 'package.json'] # 해당 파일이 변경될 때만 실행
pull_request:
types: [opened, synchronize]
스케줄 (cron)
on:
schedule:
- cron: '0 6 * * 1' # 매주 월요일 UTC 06:00
수동 실행
on:
workflow_dispatch:
inputs:
environment:
description: '배포 대상'
required: true
type: choice
options: [staging, production]
이렇게 하면 GitHub UI에 "Run workflow" 버튼이 추가되어 파라미터를 선택할 수 있습니다.
재사용 가능한 워크플로우
on:
workflow_call:
inputs:
node-version:
type: string
default: '20'
다른 워크플로우에서 uses: ./.github/workflows/reusable.yml로 호출할 수 있습니다.
잡과 스텝
병렬 잡
잡은 기본적으로 병렬로 실행됩니다:
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm run lint
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm test
build:
runs-on: ubuntu-latest
needs: [lint, test] # 두 잡이 모두 통과할 때까지 대기
steps:
- uses: actions/checkout@v4
- run: npm run build
매트릭스 전략 — 여러 버전에서 테스트
동일한 잡을 여러 환경 설정에서 실행합니다:
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18, 20, 22]
os: [ubuntu-latest, windows-latest]
fail-fast: false
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: npm ci
- run: npm test
이렇게 하면 6개의 병렬 잡이 생성됩니다 (버전 3개 × 운영체제 2개).
시크릿과 환경 변수
시크릿 사용
Settings → Secrets and variables → Actions에 민감한 값을 저장하세요:
steps:
- name: Deploy
env:
API_KEY: ${{ secrets.API_KEY }}
DATABASE_URL: ${{ secrets.DATABASE_URL }}
run: ./deploy.sh
시크릿은 로그에서 마스킹 처리되며, 포크된 저장소에는 노출되지 않습니다.
환경 변수
# 워크플로우 레벨
env:
NODE_ENV: production
jobs:
build:
# 잡 레벨
env:
CI: true
steps:
- name: Build
# 스텝 레벨
env:
VITE_API_URL: https://api.example.com
run: npm run build
더 빠른 빌드를 위한 캐싱
캐싱 없이는 매 실행마다 모든 의존성을 새로 다운로드합니다. 캐싱을 적용하면 빌드 시간이 50~80% 단축됩니다:
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm # npm 캐싱 내장
# 또는 다른 도구를 위한 수동 캐싱
- uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-
아티팩트: 잡 간 데이터 공유
한 잡에서 빌드 결과물을 업로드하고 다른 잡에서 다운로드합니다:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm run build
- uses: actions/upload-artifact@v4
with:
name: dist
path: dist/
deploy:
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/download-artifact@v4
with:
name: dist
- run: ./deploy.sh
실제 워크플로우 예시
Docker 빌드 및 푸시
name: Docker
on:
push:
tags: ['v*']
permissions:
packages: write
jobs:
docker:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/build-push-action@v5
with:
push: true
tags: ghcr.io/${{ github.repository }}:${{ github.ref_name }}
cache-from: type=gha
cache-to: type=gha,mode=max
Cloudflare Pages에 배포
name: Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- run: npm ci && npm run build
- uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
command: pages deploy dist --project-name=my-site
체인지로그와 함께 릴리스
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
run: |
git log $(git describe --tags --abbrev=0 HEAD^)..HEAD \
--pretty=format:"- %s" > CHANGELOG.md
- uses: softprops/action-gh-release@v2
with:
body_path: CHANGELOG.md
generate_release_notes: true
조건부 실행
스텝과 잡이 실행되는 조건을 제어합니다:
steps:
- name: Deploy to production
if: github.ref == 'refs/heads/main'
run: ./deploy-prod.sh
- name: Deploy to staging
if: github.event_name == 'pull_request'
run: ./deploy-staging.sh
- name: Notify on failure
if: failure()
run: curl -X POST ${{ secrets.SLACK_WEBHOOK }} -d '{"text":"Build failed!"}'
보안 모범 사례
1. 액션 버전을 SHA에 고정
# ❌ 변경 가능한 태그 — 보안 위협에 노출될 수 있음
- uses: actions/checkout@v4
# ✅ 정확한 커밋 SHA에 고정
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
2. 최소 권한 원칙 적용
permissions:
contents: read # 필요한 것만
packages: write
3. 시크릿을 절대 출력하지 않기
# ❌ 절대 이렇게 하지 마세요
- run: echo ${{ secrets.API_KEY }}
# ✅ 환경 변수 사용
- env:
API_KEY: ${{ secrets.API_KEY }}
run: ./script-that-uses-api-key.sh
4. 포크에서 오는 PR 권한 제한
on:
pull_request_target: # 베이스 브랜치 컨텍스트에서 실행
types: [opened, synchronize]
실패한 워크플로우 디버깅
- 로그 확인 — 실패한 스텝을 클릭하면 전체 출력을 볼 수 있습니다
- 디버그 로깅 추가 — 시크릿
ACTIONS_STEP_DEBUG를true로 설정하세요 act로 로컬 실행 — nektos/act를 사용해 내 머신에서 워크플로우를 실행하세요- 러너에 SSH 접속 — 인터랙티브 디버깅을 위해
mxschmitt/action-tmate를 사용하세요
워크플로우를 시각적으로 빌드하기
YAML을 직접 작성하고 싶지 않으신가요? **GitHub Actions Generator**를 사용해 Node.js, Python, Docker, 배포 등 다양한 템플릿으로 워크플로우를 시각적으로 구성하고, 프로덕션 바로 적용 가능한 YAML을 다운로드하세요.
마무리
GitHub Actions는 저장소 안에서 바로 동작하는 CI/CD를 제공합니다:
- 단순하게 시작 — 빌드 + 테스트가 포함된 워크플로우 파일 하나로 시작하세요
- 캐싱 추가 —
actions/cache나 언어별 내장 캐싱으로 빌드 시간을 단축하세요 - 매트릭스 활용 — 다양한 버전과 운영체제에서 테스트하세요
- 시크릿 보호 — 하드코딩하지 말고 항상 암호화된 시크릿을 사용하세요
- 모든 것을 자동화 — 릴리스, 배포, 보안 스캔, 스케줄 작업까지
최고의 CI/CD 파이프라인은 여러분이 신경 쓰지 않아도 모든 커밋에서 자동으로 실행되는 것입니다. 한 번만 설정해 두면, GitHub가 나머지를 알아서 처리합니다.