2025-07-25 00:47
Status:
Tags:데브옵스
깃허브 액션
기존 문제점
- 수동 배포 번거로움
- 반복적 테스트 작업
- 기존 CI/CD 도구 복잡함과 진입 장벽 → 데브옵스 엔지니어 반필수 솔직히 깃허브 쓰면 그냥 깃허브 액션 쓰는게 훨씬 쉽고 정신건강에 좋다.
핵심 구조
이벤트 → 워크 플로우 → 작업 → 액션 → 러너
Workflow (워크플로우) → Job (작업) → Step (단계) → Action (액션)
0. 특정 이벤트 발생
이벤트와 트리거 발생한 이벤트가 워크플로우
- push
- pull_request
- schedule
- workflow_dispatch
1. Workflow (워크플로우)
자동화된 프로세스의 정의, 가장 상위 개념
.github/workflows
디렉토리에 yaml 파일로 저장- 특정 이벤트에 의해 트리거
- 하나의 저장소에 여러 워크플로우 가능
- 각각 다른 작업 수행(빌드, 테스트, 배포 등)
name: 워크플로우 이름
on: [이벤트]
jobs:
작업명:
runs-on: 실행환경
steps:
- 단계들...
2. Job (작업)
동일한 러너에서 실행되는 단계들의 집합
- 병렬 실행
needs
키워드 쓰면 순차 실행 가능- 각각 독립된 가상머신에서 실행
runs-on
으로 실행 환경 지정
3. Step (단계)
작업 내에서 순차적으로 실행되는 개별 명령
run
: 셀 명령어 실행uses
: 액션 사용
4. Action(액션)
재사용 가능한 코드 단위, 복잡한 작업 캡슐화 직접 만들기도 하고 이미 만들어진걸 가져다가 쓸수도 있음 깃허브 마켓 플레이스엔 2만개 이상의 액션 등록되어 있음 CD 파이프라인
steps:
- name: 체크아웃
uses: actions/checkout@v4
- name: Node.js 설정
uses: actions/setup-node@v4
5. 러너 환경
깃허브에서 제공하는 가상 머신으로 유지보수 자동으로 처리 각 작업마다 가상머신 제공하고, 보통은 우분투로 쓴다. 월 2000분 무료, 퍼블릭은 무제한 원하면 직접 self-hosted 러너스 생성해서 만들수도 있음
개념 | 설명 |
---|---|
이벤트 | 특정 상황 발생 시 트리거, 예: 푸시, PR |
워크플로우 | 이벤트에 따른 작업 집합 정의 |
잡(Job) | 병렬 또는 순차적 한 작업 단위 |
스텝(Step) | 하나의 명령 또는 액션 수행 |
액션(Action) | 재사용 가능한 자동화 모듈 |
러너(Runner) | 작업 실행 환경 (VM/컨테이너) |
yaml 통합 예시 코드
# ==============================================================================
# GitHub Actions 완전 문법 참조 스크립트 (Next.js 프로젝트용)
# 모든 주요 문법과 고급 기능을 포함한 학습용 종합 예제
# ==============================================================================
# ------------------------------------------------------------------------------
# 1. 워크플로우 메타데이터 (Workflow Metadata)
# ------------------------------------------------------------------------------
# 워크플로우 이름 - Actions 탭에서 표시되는 이름
# 생략하면 파일 경로가 표시됨
name: "Complete GitHub Actions Syntax Reference (Next.js)"
# 워크플로우 실행 시 표시되는 동적 이름
# 표현식과 컨텍스트 -name: "${{ github.actor }}이(가) ${{ github.ref_name }} 브랜치에서 배포 실행 🚀"
# ------------------------------------------------------------------------------
# 2. 이벤트 트리거 (Event Triggers)
# ------------------------------------------------------------------------------
# 워크플로우를 실행시키는 이벤트들 정의
on:
# 2.1 Push 이벤트 - 코드가 푸시될 때 실행
push:
# 특정 브랜치에서만 실행
branches:
- main
- develop
- "release/*" # 와일드카드 패턴 사용 가능
# 특정 브랜치 제외
branches-ignore:
- "feature/*"
# 특정 파일 경로에서만 실행
paths:
- "src/**"
- "pages/**"
- "components/**"
- "package.json"
- "package-lock.json"
# 특정 파일 경로 제외
paths-ignore:
- "docs/**"
- "README.md"
- "*.md"
# 특정 태그에서 실행
tags:
- "v*.*.*"
# 2.2 Pull Request 이벤트
pull_request:
# PR 이벤트 타입 지정
types:
- opened # PR 생성
- synchronize # PR 업데이트
- reopened # PR 재오픈
- closed # PR 닫힘
branches: [main]
# 2.3 스케줄 이벤트 - cron 표현식 사용
schedule:
# 매일 오전 2시 (UTC) 실행
- cron: '0 2 * * *'
# 매주 월요일 오전 9시 실행
- cron: '0 9 * * MON'
# 2.4 수동 트리거 - Actions 탭에서 수동 실행 가능
workflow_dispatch:
# 입력 매개변수 정의
inputs:
environment:
description: '배포 환경 선택'
required: true
type: choice
options:
- development
- staging
- production
default: 'staging'
skip_tests:
description: '테스트 건너뛸지 여부'
required: false
type: boolean
default: false
custom_message:
description: '사용자 정의 메시지'
required: false
type: string
default: 'Manual deployment'
# 2.5 다른 워크플로우에서 호출 가능하게 설정
workflow_call:
inputs:
node_version:
description: 'Node.js 버전'
required: false
type: string
default: '20'
secrets:
NPM_TOKEN:
description: 'NPM 레지스트리 토큰'
required: true
outputs:
build_version:
description: '빌드된 버전'
value: ${{ jobs.build.outputs.version }}
# ------------------------------------------------------------------------------
# 3. 전역 환경 변수 (Global Environment Variables)
# ------------------------------------------------------------------------------
# 모든 작업에서 사용 가능한 환경 변수
env:
# 정적 환경 변수
NODE_ENV: production
NEXT_TELEMETRY_DISABLED: 1
CI: true
# 표현식을 사용한 동적 환경 변수
BRANCH_NAME: ${{ github.ref_name }}
COMMIT_SHA: ${{ github.sha }}
BUILD_TIME: ${{ github.run_number }}
# 조건부 환경 변수 (삼항 연산자 사용)
DEPLOY_URL: ${{ github.ref == 'refs/heads/main' && 'https://prod.example.com' || 'https://staging.example.com' }}
# ------------------------------------------------------------------------------
# 4. 권한 설정 (Permissions)
# ------------------------------------------------------------------------------
# GITHUB_TOKEN의 권한 설정
permissions:
contents: read # 저장소 내용 읽기
pages: write # GitHub Pages 쓰기
id-token: write # OIDC 토큰 쓰기
actions: read # Actions 읽기
checks: write # 체크 쓰기
deployments: write # 배포 쓰기
issues: write # 이슈 쓰기
pull-requests: write # PR 쓰기
security-events: write # 보안 이벤트 쓰기
# ------------------------------------------------------------------------------
# 5. 동시성 제어 (Concurrency)
# ------------------------------------------------------------------------------
# 동시 실행 제어 - 같은 그룹의 워크플로우는 하나씩만 실행
concurrency:
# 동시성 그룹 이름 (브랜치별로 구분)
group: ${{ github.workflow }}-${{ github.ref }}
# 진행 중인 작업을 취소할지 여부
cancel-in-progress: true
# ------------------------------------------------------------------------------
# 6. 기본값 설정 (Defaults)
# ------------------------------------------------------------------------------
# 모든 작업의 기본 설정
defaults:
run:
# 기본 셸 설정
shell: bash
# 기본 작업 디렉토리
working-directory: ./
# ------------------------------------------------------------------------------
# 7. 작업 정의 (Jobs)
# ------------------------------------------------------------------------------
jobs:
# ===========================================================================
# 7.1 코드 품질 검사 작업
# ===========================================================================
code-quality:
# 작업 이름 (선택사항)
name: "코드 품질 및 린트 검사"
# 실행 환경 지정
runs-on: ubuntu-latest # ubuntu-latest, windows-latest, macos-latest
# 작업 실행 조건 (표현식 사용)
if: ${{ !contains(github.event.head_commit.message, '[skip ci]') }}
# 작업 타임아웃 (분 단위)
timeout-minutes: 15
# 작업 레벨 환경 변수
env:
NODE_VERSION: "20"
# 작업 단계들
steps:
# 7.1.1 저장소 체크아웃
- name: "📥 소스코드 체크아웃"
uses: actions/checkout@v4
with:
# 전체 히스토리 가져오기 (기본값: 1)
fetch-depth: 0
# 특정 토큰 사용 (기본값: GITHUB_TOKEN)
token: ${{ secrets.GITHUB_TOKEN }}
# LFS 파일 체크아웃
lfs: false
# 7.1.2 Node.js 환경 설정
- name: "⚙️ Node.js 환경 설정"
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
# 패키지 매니저 캐시 활성화
cache: 'npm'
# 레지스트리 URL 설정
registry-url: 'https://registry.npmjs.org'
# .nvmrc 파일에서 Node 버전 읽기
node-version-file: '.nvmrc'
# 7.1.3 의존성 캐싱
- name: "📦 의존성 캐시 설정"
uses: actions/cache@v3
with:
path: |
~/.npm
node_modules
# 캐시 키 생성 (package-lock.json 해시 기반)
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
# 캐시 복원 키 (부분 매치 허용)
restore-keys: |
${{ runner.os }}-node-
# 7.1.4 의존성 설치
- name: "📦 의존성 설치"
run: |
# npm ci는 package-lock.json을 기반으로 정확한 버전 설치
npm ci --prefer-offline --no-audit
env:
# 단계별 환경 변수
NPM_CONFIG_LOGLEVEL: warn
# 7.1.5 TypeScript 타입 검사
- name: "🔍 TypeScript 타입 검사"
run: npx tsc --noEmit
# 계속 실행 (오류가 있어도 다음 단계 실행)
continue-on-error: false
# 7.1.6 ESLint 검사
- name: "🧹 ESLint 코드 린트 검사"
run: |
# ESLint 실행 및 결과를 파일로 저장
npm run lint -- --format=json --output-file=eslint-report.json
# 경고 포함 개수 확인
npm run lint -- --max-warnings 0
# 실패해도 계속 진행
continue-on-error: true
# 7.1.7 Prettier 포맷 검사
- name: "💅 Prettier 포맷 검사"
run: npm run format:check
# 7.1.8 보안 취약점 검사
- name: "🔒 보안 취약점 검사"
run: |
# npm audit 실행
npm audit --audit-level=high
# 결과를 JSON 파일로 저장
npm audit --json > audit-results.json
continue-on-error: true
# 7.1.9 아티팩트 업로드 (린트 결과)
- name: "📄 린트 결과 업로드"
uses: actions/upload-artifact@v3
if: always() # 항상 실행 (성공/실패 관계없이)
with:
name: lint-results
path: |
eslint-report.json
audit-results.json
# 보존 기간 (일 단위)
retention-days: 7
# ===========================================================================
# 7.2 매트릭스 전략을 사용한 테스트 작업
# ===========================================================================
test:
name: "테스트 실행 (Node ${{ matrix.node }}, ${{ matrix.os }})"
# 이전 작업에 의존
needs: [code-quality]
# 매트릭스 전략 설정
strategy:
# 하나의 매트릭스가 실패해도 다른 매트릭스는 계속 실행
fail-fast: false
# 최대 병렬 실행 수
max-parallel: 4
# 매트릭스 변수 정의
matrix:
# 운영체제 매트릭스
os: [ubuntu-latest, windows-latest, macos-latest]
# Node.js 버전 매트릭스
node: ['18', '20', '21']
# 매트릭스 조합 추가
include:
# 실험적 설정 추가
- os: ubuntu-latest
node: '22'
experimental: true
# 매트릭스 조합 제외
exclude:
# Windows에서 Node 21 제외
- os: windows-latest
node: '21'
# 매트릭스 OS에서 실행
runs-on: ${{ matrix.os }}
# 실험적 빌드는 실패해도 전체 실패로 처리하지 않음
continue-on-error: ${{ matrix.experimental == true }}
steps:
- name: "📥 체크아웃"
uses: actions/checkout@v4
- name: "⚙️ Node.js ${{ matrix.node }} 설정"
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
cache: 'npm'
- name: "📦 의존성 설치"
run: npm ci
# 조건부 단계 실행
- name: "🧪 단위 테스트 실행"
if: ${{ !inputs.skip_tests }}
run: |
# 테스트 실행 및 커버리지 생성
npm test -- --coverage --watchAll=false
env:
# 테스트 환경 변수
NODE_ENV: test
CI: true
- name: "📊 테스트 커버리지 업로드"
uses: codecov/codecov-action@v3
if: matrix.os == 'ubuntu-latest' && matrix.node == '20'
with:
files: ./coverage/lcov.info
flags: unittests
name: codecov-umbrella
# ===========================================================================
# 7.3 빌드 작업 (여러 출력과 아티팩트 처리)
# ===========================================================================
build:
name: "빌드 및 아티팩트 생성"
needs: [test]
runs-on: ubuntu-latest
# 작업 출력 정의 (다른 작업에서 사용 가능)
outputs:
# 출력 변수 정의
build-version: ${{ steps.version.outputs.version }}
build-time: ${{ steps.build-info.outputs.time }}
artifact-url: ${{ steps.upload.outputs.artifact-url }}
steps:
- uses: actions/checkout@v4
- name: "⚙️ Node.js 설정"
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
# 버전 정보 생성
- name: "📋 버전 정보 생성"
id: version
run: |
# 버전 문자열 생성
VERSION="1.0.${{ github.run_number }}"
echo "version=${VERSION}" >> $GITHUB_OUTPUT
echo "Generated version: ${VERSION}"
# 빌드 정보 설정
- name: "ℹ️ 빌드 정보 설정"
id: build-info
run: |
# 빌드 시간 설정
BUILD_TIME=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
echo "time=${BUILD_TIME}" >> $GITHUB_OUTPUT
# 환경변수 파일에 추가
echo "BUILD_VERSION=${{ steps.version.outputs.version }}" >> $GITHUB_ENV
echo "BUILD_TIME=${BUILD_TIME}" >> $GITHUB_ENV
# Next.js 빌드
- name: "🏗️ Next.js 애플리케이션 빌드"
run: |
# 빌드 환경 설정
echo "Building Next.js application..."
echo "Version: $BUILD_VERSION"
echo "Time: $BUILD_TIME"
# Next.js 빌드 실행
npm run build
env:
# 빌드 환경 변수
NODE_ENV: production
NEXT_TELEMETRY_DISABLED: 1
# Secrets 사용
NEXT_PUBLIC_API_URL: ${{ secrets.API_URL }}
DATABASE_URL: ${{ secrets.DATABASE_URL }}
# 빌드 결과 검증
- name: "✅ 빌드 결과 검증"
run: |
# 빌드 폴더 존재 확인
if [ ! -d ".next" ]; then
echo "❌ Build failed: .next directory not found"
exit 1
fi
# 빌드 파일 크기 확인
BUILD_SIZE=$(du -sh .next | cut -f1)
echo "📊 Build size: $BUILD_SIZE"
# 빌드 정보를 파일로 저장
cat > build-info.json << EOF
{
"version": "$BUILD_VERSION",
"buildTime": "$BUILD_TIME",
"size": "$BUILD_SIZE",
"commit": "${{ github.sha }}",
"branch": "${{ github.ref_name }}"
}
EOF
# 빌드 아티팩트 업로드
- name: "📦 빌드 아티팩트 업로드"
id: upload
uses: actions/upload-artifact@v3
with:
name: next-build-${{ steps.version.outputs.version }}
path: |
.next/
public/
build-info.json
package.json
retention-days: 30
# GitHub Pages용 정적 파일 생성 (조건부)
- name: "📄 GitHub Pages 아티팩트 생성"
if: github.ref == 'refs/heads/main'
uses: actions/upload-pages-artifact@v2
with:
path: ./out
# ===========================================================================
# 7.4 보안 검사 작업
# ===========================================================================
security:
name: "보안 검사"
runs-on: ubuntu-latest
# 병렬 실행 (다른 작업에 의존하지 않음)
# 권한 설정 (작업별)
permissions:
security-events: write
actions: read
contents: read
steps:
- uses: actions/checkout@v4
# CodeQL 분석 초기화
- name: "🔍 CodeQL 분석 초기화"
uses: github/codeql-action/init@v2
with:
languages: javascript
# 추가 쿼리 사용
queries: security-extended
# 종속성 보안 검사
- name: "🔒 종속성 보안 검사"
uses: snyk/actions/node@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
with:
args: --severity-threshold=high --json-file-output=snyk-results.json
continue-on-error: true
# CodeQL 분석 수행
- name: "📊 CodeQL 분석 수행"
uses: github/codeql-action/analyze@v2
# ===========================================================================
# 7.5 조건부 배포 작업 (환경별)
# ===========================================================================
deploy:
name: "배포 (${{ github.event.inputs.environment || 'staging' }})"
needs: [build, security]
runs-on: ubuntu-latest
# 배포 환경 설정
environment:
name: ${{ github.event.inputs.environment || 'staging' }}
# 배포 URL (동적 설정)
url: ${{ steps.deploy.outputs.url }}
# 메인 브랜치 또는 수동 실행에서만 배포
if: ${{ github.ref == 'refs/heads/main' || github.event_name == 'workflow_dispatch' }}
steps:
- uses: actions/checkout@v4
# 환경별 설정 로드
- name: "⚙️ 환경 설정"
id: config
run: |
ENV="${{ github.event.inputs.environment || 'staging' }}"
case $ENV in
"production")
echo "domain=app.example.com" >> $GITHUB_OUTPUT
echo "api_url=https://api.example.com" >> $GITHUB_OUTPUT
echo "deploy_path=/var/www/production" >> $GITHUB_OUTPUT
;;
"staging")
echo "domain=staging.example.com" >> $GITHUB_OUTPUT
echo "api_url=https://staging-api.example.com" >> $GITHUB_OUTPUT
echo "deploy_path=/var/www/staging" >> $GITHUB_OUTPUT
;;
*)
echo "domain=dev.example.com" >> $GITHUB_OUTPUT
echo "api_url=https://dev-api.example.com" >> $GITHUB_OUTPUT
echo "deploy_path=/var/www/development" >> $GITHUB_OUTPUT
;;
esac
# 빌드 아티팩트 다운로드
- name: "📦 빌드 아티팩트 다운로드"
uses: actions/download-artifact@v3
with:
name: next-build-${{ needs.build.outputs.build-version }}
path: ./build
# 배포 실행
- name: "🚀 애플리케이션 배포"
id: deploy
run: |
ENV="${{ github.event.inputs.environment || 'staging' }}"
DOMAIN="${{ steps.config.outputs.domain }}"
echo "🚀 Deploying to $ENV environment..."
echo "🌐 Domain: $DOMAIN"
# 배포 로직 (예: rsync, scp, API 호출 등)
# 실제 배포 명령어들...
# 배포 URL 출력
echo "url=https://$DOMAIN" >> $GITHUB_OUTPUT
env:
# 배포에 필요한 시크릿들
DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }}
SERVER_HOST: ${{ secrets.SERVER_HOST }}
SERVER_USER: ${{ secrets.SERVER_USER }}
# 배포 상태 알림
- name: "📢 배포 완료 알림"
if: always()
uses: 8398a7/action-slack@v3
with:
status: ${{ job.status }}
fields: repo,message,commit,author,action,eventName,ref,workflow
text: |
배포 ${{ job.status }}!
🌍 환경: ${{ github.event.inputs.environment || 'staging' }}
🔗 URL: ${{ steps.deploy.outputs.url }}
💬 메시지: ${{ github.event.inputs.custom_message || github.event.head_commit.message }}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
# ===========================================================================
# 7.6 정리 작업 (Cleanup Job)
# ===========================================================================
cleanup:
name: "정리 작업"
runs-on: ubuntu-latest
# 모든 작업이 완료된 후 실행 (성공/실패 무관)
needs: [deploy]
if: always()
steps:
# 이전 아티팩트 정리
- name: "🧹 이전 아티팩트 정리"
uses: actions/github-script@v6
with:
script: |
// 30일 이상된 아티팩트 삭제
const artifacts = await github.rest.actions.listArtifactsForRepo({
owner: context.repo.owner,
repo: context.repo.repo,
});
const thirtyDaysAgo = new Date();
thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);
for (const artifact of artifacts.data.artifacts) {
if (new Date(artifact.created_at) < thirtyDaysAgo) {
await github.rest.actions.deleteArtifact({
owner: context.repo.owner,
repo: context.repo.repo,
artifact_id: artifact.id,
});
console.log(`Deleted artifact: ${artifact.name}`);
}
}
# ==============================================================================
# 추가 고급 문법 예제들
# ==============================================================================
# 다른 워크플로우 파일에서 이 워크플로우를 재사용하는 방법:
#
# jobs:
# call-reusable-workflow:
# uses: ./.github/workflows/this-file.yml
# with:
# node_version: '18'
# secrets:
# NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
# 표현식에서 사용 가능한 함수들:
# - contains(search, item): 포함 여부 확인
# - startsWith(searchString, searchValue): 시작 문자열 확인
# - endsWith(searchString, searchValue): 끝 문자열 확인
# - format(string, replaceValue0, replaceValue1, ...): 문자열 포맷팅
# - join(array, separator): 배열 조인
# - toJSON(value): JSON 문자열로 변환
# - fromJSON(value): JSON 파싱
# - hashFiles(path): 파일 해시 생성
# - success(): 이전 단계들이 모두 성공
# - always(): 항상 실행
# - cancelled(): 취소됨
# - failure(): 실패함
# 컨텍스트 변수들:
# - github.*: GitHub 이벤트 및 저장소 정보
# - env.*: 환경 변수
# - job.*: 현재 작업 정보
# - steps.*: 이전 단계 출력
# - runner.*: 러너 정보
# - secrets.*: 저장소 시크릿
# - vars.*: 저장소 변수
# - inputs.*: 워크플로우 입력
# - matrix.*: 매트릭스 값
References
GitHub Actions 완전 핸드북_ 초보자부터 중급자까지 GitHub Actions 문법과 Next.js 실제 사용 사례 핸드북 CI CD 깃 젠킨스