여러분이 작성한 모든 GitHub Actions 워크플로는 YAML 파일입니다. 전부 다요 — 트리거, 작업, 단계, 환경 변수, 매트릭스 전략 — 모두 YAML로 표현됩니다. 즉, YAML을 깊이 이해하는 것은 단순히 학문적인 것이 아닙니다: 안정적으로 작동하는 CI와 금요일 오후 4시에 릴리스를 출시하려는 순간 신비롭게 중단되는 CI의 차이입니다.

GitHub Actions는 훌륭한 워크플로 구문 문서를 제공하지만, Actions 기능에 집중하여 내부의 YAML 메커니즘을 다루지 않습니다. 이 글은 두 가지 모두를 다룹니다 — 워크플로 구조와 CI 설정을 성공하거나 실패하게 만드는 YAML 특정 패턴(및 함정).

워크플로 구조: 최상위 키

GitHub Actions 워크플로 파일은 .github/workflows/에 있으며 세 개의 필수 최상위 키를 가집니다. 공식 워크플로 개요는 파일이 저장소의 어디에 있는지, GitHub가 파일을 어떻게 발견하는지 설명합니다:

yaml
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 test
YAML 불리언으로서 on에 주의하세요: YAML 1.1에서 따옴표 없는 단어 on은 불리언 true로 해석됩니다. GitHub의 파서는 이것을 올바르게 처리하지만, 일반 YAML 린터가 워크플로 파일에 플래그를 달면 이 때문입니다. 안전한 형태는 따옴표로 감싸는 것입니다: "on": — 하지만 GitHub Actions 자체는 따옴표를 요구하지 않습니다.

트리거: on: 키

on: 키는 워크플로가 언제 실행되는지 제어합니다. 트리거 이벤트의 전체 목록은 풀 리퀘스트 리뷰부터 저장소 디스패치까지 모든 것을 다룹니다. 가장 일반적인 패턴들입니다:

yaml
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 참조를 확인하세요. 없다면 &&로 명령어를 한 줄에 연결해야 하는데, 이것은 읽기 어렵습니다:

yaml
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을 사용합니다. 이들이 어떻게 함께 작동하는지 보여줍니다:

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로 표현됩니다. 각 매트릭스 값 조합에 대해 작업을 한 번씩 실행합니다:

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의 표현식 언어를 사용합니다:

yaml
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:를 사용합니다:

yaml
# .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 }}
yaml
# .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 포매터를 사용하세요.