여러분이 작성한 모든 GitHub Actions 워크플로는 YAML 파일입니다. 전부 다요 — 트리거, 작업, 단계, 환경 변수, 매트릭스 전략 — 모두 YAML로 표현됩니다. 즉, YAML을 깊이 이해하는 것은 단순히 학문적인 것이 아닙니다: 안정적으로 작동하는 CI와 금요일 오후 4시에 릴리스를 출시하려는 순간 신비롭게 중단되는 CI의 차이입니다.
GitHub Actions는 훌륭한 워크플로 구문 문서를 제공하지만, Actions 기능에 집중하여 내부의 YAML 메커니즘을 다루지 않습니다. 이 글은 두 가지 모두를 다룹니다 — 워크플로 구조와 CI 설정을 성공하거나 실패하게 만드는 YAML 특정 패턴(및 함정).
워크플로 구조: 최상위 키
GitHub Actions 워크플로 파일은 .github/workflows/에 있으며 세 개의 필수 최상위 키를 가집니다.
공식 워크플로 개요는
파일이 저장소의 어디에 있는지, GitHub가 파일을 어떻게 발견하는지 설명합니다:
name: CI Pipeline # optional but shown in the Actions UI
on: # triggers
push:
branches: [main]
pull_request:
jobs: # the actual work
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci && npm teston에 주의하세요: YAML 1.1에서 따옴표 없는 단어 on은
불리언 true로 해석됩니다. GitHub의 파서는 이것을 올바르게 처리하지만, 일반 YAML 린터가
워크플로 파일에 플래그를 달면 이 때문입니다. 안전한 형태는 따옴표로 감싸는 것입니다:
"on": — 하지만 GitHub Actions 자체는 따옴표를 요구하지 않습니다.트리거: on: 키
on: 키는 워크플로가 언제 실행되는지 제어합니다.
트리거 이벤트의 전체 목록은
풀 리퀘스트 리뷰부터 저장소 디스패치까지 모든 것을 다룹니다. 가장 일반적인 패턴들입니다:
on:
# Push to specific branches
push:
branches:
- main
- "release/**" # glob pattern — quotes needed for /**
paths-ignore:
- "docs/**"
- "*.md"
# PRs targeting main
pull_request:
branches: [main]
types: [opened, synchronize, reopened]
# Scheduled (cron syntax)
schedule:
- cron: "0 2 * * 1" # every Monday at 2am UTC
# Manual trigger with input
workflow_dispatch:
inputs:
environment:
description: "Target environment"
required: true
default: staging
type: choice
options: [staging, production]여러 줄 run: 명령어와 YAML 블록 스칼라
run: 키는 YAML의 리터럴 블록 스칼라(|)가 실력을 발휘하는 곳입니다 —
블록 및 접힌 스칼라의 모든 변형은 yaml-multiline.info 참조를 확인하세요.
없다면 &&로 명령어를 한 줄에 연결해야 하는데, 이것은 읽기 어렵습니다:
steps:
# Single line — works but hard to read
- name: Build
run: npm ci && npm run lint && npm run test && npm run build
# Multi-line with literal block scalar — much better
- name: Build
run: |
npm ci
npm run lint
npm run test -- --coverage
npm run build
# Folded scalar (>) joins lines with spaces — NOT what you want for shell
# This would run as a single command with spaces where the newlines are:
- name: Wrong for shell
run: >
npm ci
npm test|를 사용하고, >는 절대 사용하지 마세요.
접힌 스칼라는 줄 바꿈을 공백으로 접어버려, 셸 명령어들이 하나의 긴 문자열로 합쳐집니다.
이는 모호한 오류를 유발합니다. 리터럴 블록 스칼라 |는 줄 바꿈을 정확히 보존하므로
각 줄이 별도의 셸 명령어로 실행됩니다.환경 변수와 시크릿
GitHub Actions의 환경 변수는 Actions 컨텍스트와 표현식 문법과 함께 YAML을 사용합니다. 이들이 어떻게 함께 작동하는지 보여줍니다:
env:
NODE_ENV: production # workflow-level env var
API_VERSION: "2.1" # quoted to force string type
jobs:
deploy:
runs-on: ubuntu-latest
env:
DEPLOY_ENV: staging # job-level env var (overrides workflow-level)
steps:
- name: Deploy to staging
env:
API_KEY: ${{ secrets.STAGING_API_KEY }} # step-level, from secret
DATABASE_URL: ${{ vars.STAGING_DB_URL }} # step-level, from variable
run: |
echo "Deploying to $DEPLOY_ENV"
./scripts/deploy.sh표현식 문법 ${{ }}은 YAML 위에 레이어로 올려진 GitHub Actions 자체 템플릿 언어입니다.
일부 컨텍스트에서는 YAML 파싱 전에 평가되며, 이는 YAML 자체의 따옴표 규칙과 예기치 않게 상호작용할 수 있습니다.
확실하지 않을 때는 ${{로 시작하는 값을 큰따옴표로 감싸세요.
매트릭스 전략: 여러 설정에서 빌드
매트릭스 전략은 GitHub Actions의 가장 강력한 기능 중 하나입니다 — 완전히 YAML로 표현됩니다. 각 매트릭스 값 조합에 대해 작업을 한 번씩 실행합니다:
jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false # don't cancel other matrix jobs on failure
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
node: ["18", "20", "22"]
exclude:
- os: windows-latest
node: "18" # skip this combination
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
- run: npm ci && npm test이 단일 작업 정의는 최대 9개의 병렬 실행을 생성합니다 (3 OS × 3 Node 버전, 1개 제외).
YAML 매트릭스 값은 일반 배열입니다 — 주의할 점은 18 같은 버전 번호를
"18"로 따옴표로 감싸 문자열로 유지해야 한다는 것입니다. 따옴표 없이는 YAML이 정수로 파싱하여
일부 Actions가 불평할 수 있습니다.
if:를 사용한 조건부 단계
if: 키를 사용하면 조건에 따라 단계를 건너뛸 수 있습니다. 일반 YAML 값이 아닌
GitHub의 표현식 언어를 사용합니다:
steps:
- name: Run tests
run: npm test
- name: Upload coverage
if: success() && github.ref == 'refs/heads/main'
uses: codecov/codecov-action@v4
- name: Notify on failure
if: failure()
uses: slackapi/slack-github-action@v1
with:
slack-message: "Build failed on ${{ github.ref }}"
channel-id: "C12345ABC"
env:
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
- name: Deploy (only on tag push)
if: startsWith(github.ref, 'refs/tags/v')
run: ./scripts/deploy.sh production재사용 가능한 워크플로
파일 내뿐만 아니라 저장소 간 중복을 제거하기 위해, GitHub Actions는
재사용 가능한 워크플로를 지원합니다.
호출되는 워크플로는 on: workflow_call:을 사용하고, 호출자는
단계 수준이 아닌 작업 수준에서 uses:를 사용합니다:
# .github/workflows/reusable-test.yml (the reusable workflow)
on:
workflow_call:
inputs:
node-version:
required: false
type: string
default: "20"
secrets:
NPM_TOKEN:
required: true
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node-version }}
- run: npm ci && npm test
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}# .github/workflows/ci.yml (the caller)
on: [push, pull_request]
jobs:
run-tests:
uses: my-org/shared-workflows/.github/workflows/reusable-test.yml@main
with:
node-version: "22"
secrets:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}GitHub Actions에서 가장 흔한 YAML 실수
- 단계의 잘못된 들여쓰기. 단계는 일관된 공백으로
steps:아래에 들여쓰기되어야 합니다. 단 하나의 여분 또는 누락된 공백이 단계를 해당 작업 외부로 이동시킵니다. - 따옴표 없는 버전 번호.
node-version: 20은 정수를 전달합니다; 일부 액션은 문자열을 기대합니다. 안전을 위해node-version: "20"을 사용하세요. - 셸 스크립트에
|대신>사용. 접힌 스칼라는 줄 바꿈을 압축합니다. 여러 줄 스크립트가 하나의 긴 문자열이 되어 실패합니다. - 글로브 패턴에 따옴표 누락.
release/**같은 패턴에는 YAML이 별칭으로 해석할 수 있는*가 포함됩니다. 항상 글로브 패턴을 따옴표로 감싸세요. - if: 조건에서 시크릿 사용. GitHub는 로그에서 시크릿을 마스킹하지만
if:표현식에서는 허용하지 않습니다. 대신 환경 변수를 사용하세요. - 매트릭스 작업에서
fail-fast: false잊기. 기본적으로 하나의 매트릭스 작업이 실패하면 다른 모든 작업이 취소됩니다. 디버깅 중에는 보통 원하지 않는 동작입니다.
마무리
GitHub Actions 워크플로는 근본적으로 YAML 파일입니다 — YAML의 리터럴 블록 스칼라, 따옴표 규칙, 타입 강제를 이해하는 것은 더 신뢰할 수 있는 CI를 작성하는 것으로 직결됩니다. 가장 흔한 실패는 들여쓰기 실수, 잘못된 타입으로 강제되는 따옴표 없는 값, 셸 스크립트에 잘못된 여러 줄 문자열 연산자 사용에서 옵니다. 이 세 가지를 올바르게 하면 대부분의 워크플로 디버깅이 간단해집니다. 푸시 전에 구문 오류를 잡으려면 YAML 유효성 검사기를, 워크플로 파일 전체에 일관된 들여쓰기를 적용하려면 YAML 포매터를 사용하세요.