GitHub Actions 입문: YAML 워크플로 기초와 트리거 설정

화면을 가득 채운 빨간 오류 메시지를 보면 키보드를 내리치고 싶어집니다.
로컬에서는 분명 잘 실행되던 코드가 GitHub에 푸시하자마자 실패합니다. YAML 파일은 벌써 여섯 번이나 고쳤는데 매번 들여쓰기 문제입니다. 코드를 작성하는 것보다 이게 왜 더 어려운 걸까요?
사실 GitHub Actions 자체는 복잡하지 않습니다. 문제는 수백 페이지에 달하는 설정 설명부터 들이미는 문서입니다. 읽다 보면 금세 머리가 아파집니다. 이 글에서는 가장 간단한 방식으로 YAML 워크플로의 핵심 구조를 이해해 봅니다.
이 글에서 배울 내용은 다음과 같습니다.
- YAML 파일의 네 가지 핵심 필드와 각 필드의 실제 역할
- 자주 쓰는 트리거 8가지의 설정 방법과 활용 사례
- 바로 복사해 사용할 수 있는 완전한 워크플로 템플릿
- 제가 직접 겪은 문제와 이를 피하는 방법
준비되셨나요? 시작해 보겠습니다.
YAML 워크플로 파일: 네 가지 핵심 필드
솔직히 처음 GitHub Actions를 접했을 때 .github/workflows 디렉터리의 YAML 파일은 암호문처럼 보였습니다. 들여쓰기와 콜론이 가득하고, 공백 하나만 잘못 바꿔도 전부 실패했습니다.
하지만 나중에 알고 보니 핵심은 네 부분뿐이었습니다. 이 네 가지를 이해하면 나머지는 부가 기능에 가깝습니다.
name: 워크플로에 이름 붙이기
가장 간단한 필드지만 처음에는 저를 포함해 많은 사람이 무시하곤 합니다.
name: CI for Node.js App
name은 GitHub Actions 탭에 표시되는 워크플로 이름입니다. 코드를 푸시한 뒤 저장소의 Actions 페이지에 들어갔을 때 보이는 이름이 바로 이것입니다.
이름을 지을 때는 프로젝트 이름 + 기능 설명 형식을 사용하면 좋습니다. 예를 들어 MyApp CI, Backend Deploy처럼 작성하면 워크플로가 많아져도 원하는 항목을 한눈에 찾을 수 있습니다.
이 필드는 생략할 수도 있습니다. 작성하지 않으면 GitHub가 파일 이름을 대신 사용합니다. 하지만 파일 이름은 보통 영문 약어라 직관성이 떨어지므로 생략하지 않는 편이 좋습니다.
on: 언제 실행할지 정하기
on은 전체 워크플로의 ‘스위치’입니다. 어떤 상황에서 워크플로를 실행할지 GitHub에 알려 줍니다.
가장 간단한 작성 방법은 다음과 같습니다.
on: push
코드가 푸시되기만 하면 실행된다는 뜻입니다.
그러나 실제 프로젝트에서는 보통 더 세밀한 제어가 필요합니다. 예를 들어 main 브랜치에 푸시가 발생했을 때만 실행하려면 다음과 같이 작성합니다.
on:
push:
branches: [main]
Pull Request가 생성될 때도 실행하고 싶다면 다음과 같이 설정합니다.
on:
push:
branches: [main]
pull_request:
branches: [main]
트리거는 GitHub Actions의 핵심입니다. 뒤에서 자주 쓰는 8가지 사례를 별도 섹션으로 자세히 살펴보겠습니다. 여기서는 on이 워크플로의 ‘실행 시점’을 결정한다는 점만 기억하면 됩니다.
jobs: 수행할 작업 정의하기
jobs는 워크플로의 본체로, ‘구체적으로 어떤 작업을 수행할지’ 정의합니다.
하나의 워크플로에는 여러 job을 포함할 수 있으며 기본적으로 병렬 실행됩니다. 각 job은 runs-on 필드로 실행 환경을 지정해야 합니다.
jobs:
build:
runs-on: ubuntu-latest
steps:
# ...단계 목록
위 코드는 build라는 job을 정의하며, 이 job은 GitHub가 제공하는 최신 Ubuntu 버전에서 실행됩니다.
워크플로에 여러 job이 있다면 needs 필드로 의존 관계를 정의할 수 있습니다.
jobs:
test:
runs-on: ubuntu-latest
# ... 테스트 단계
deploy:
needs: test # test가 끝난 뒤에 실행
runs-on: ubuntu-latest
# ... 배포 단계
이렇게 하면 deploy는 test가 끝날 때까지 기다렸다가 시작합니다. test가 실패하면 deploy는 실행되지 않습니다.
steps: 구체적인 실행 단계
steps는 job 안에서 사용하는 가장 작은 실행 단위입니다. 명령이나 Action을 순서대로 실행합니다.
각 step은 두 가지 방식으로 작성할 수 있습니다.
1. run으로 명령 실행:
steps:
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
run 뒤에는 터미널에서 실행할 명령을 작성합니다. 로컬에서 입력하는 명령과 같습니다.
2. uses로 Action 호출:
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
uses는 GitHub Actions의 강력한 기능입니다. 다른 사람이 만들어 둔 Action을 그대로 가져와 사용할 수 있습니다. 예를 들어 actions/checkout@v4는 코드를 가져오고, actions/setup-node@v4는 Node.js 환경을 설정해 줍니다.
with는 Action에 매개변수를 전달합니다. 위 예의 node-version: '20'은 setup-node에 Node.js 20 버전을 사용하라고 지정한 것입니다.
네 가지 필드를 모두 살펴봤습니다. 생각보다 간단하지 않나요?
트리거 완전 분석: 자주 쓰는 8가지 사례
앞에서 on 필드가 실행 시점을 결정한다고 설명했습니다. GitHub Actions는 수십 가지 트리거를 지원하지만 솔직히 자주 쓰는 것은 몇 가지뿐입니다.
각 트리거의 활용 사례를 빠르게 파악할 수 있도록 표로 정리했습니다.
| 트리거 | 대표적인 활용 사례 | 설정 예시 |
|---|---|---|
push | 브랜치에 코드 푸시 | on: push: branches: [main] |
pull_request | PR 생성 또는 업데이트 | on: pull_request: types: [opened, synchronize] |
schedule | 예약 작업(Cron) | on: schedule: - cron: '0 0 * * *' |
workflow_dispatch | 수동 실행 | on: workflow_dispatch: inputs: env: ... |
workflow_call | 재사용 가능한 워크플로 | on: workflow_call: inputs: ... |
release | 릴리스 이벤트 | on: release: types: [published] |
issues | Issue 이벤트 | on: issues: types: [opened, labeled] |
repository_dispatch | 외부 이벤트 | on: repository_dispatch: types: [deploy] |
이제 가장 자주 쓰는 몇 가지를 자세히 살펴보겠습니다.
push: 가장 기본적인 트리거
push는 처음 접하게 될 트리거입니다. 코드가 브랜치에 푸시될 때 실행됩니다.
간단한 설정은 다음과 같습니다.
on: push
하지만 이 설정에는 문제가 있습니다. 어느 브랜치든 푸시가 발생하면 실행됩니다. 저장소에 브랜치가 20개 있고 모든 사람이 코드를 푸시할 때마다 워크플로가 실행된다면 무료 사용량은 금세 소진됩니다.
브랜치를 제한하는 편이 더 합리적입니다.
on:
push:
branches: [main, develop]
와일드카드를 사용할 수도 있습니다.
on:
push:
branches:
- 'main'
- 'release/**' # release/v1.0, release/v2.0 등과 일치
pull_request: 코드 병합 전의 수문장
pull_request는 PR이 생성되거나 업데이트될 때 실행되며, 보통 테스트와 코드 스타일 검사에 사용합니다.
on:
pull_request:
branches: [main]
types 필드로 실행 시점을 더 세밀하게 제어할 수 있습니다.
on:
pull_request:
types: [opened, synchronize, reopened]
opened: PR이 생성됨synchronize: PR에 새 커밋이 추가됨reopened: PR이 다시 열림
이렇게 설정하면 이 세 가지 상황에서만 워크플로가 실행되므로 리소스를 낭비하지 않습니다.
schedule: 예약 작업
schedule은 Cron 표현식으로 예약 실행을 정의합니다. 예를 들어 매일 새벽에 테스트를 한 번 실행하려면 다음과 같이 작성합니다.
on:
schedule:
- cron: '0 0 * * *' # 매일 UTC 0시
Cron 표현식에는 분, 시, 일, 월, 요일의 5개 필드가 있습니다.
자주 쓰는 시간 설정은 다음과 같습니다.
0 0 * * *: 매일 UTC 0시(베이징 시간 오전 8시)0 */6 * * *: 6시간마다 한 번30 2 * * 1: 매주 월요일 UTC 2:30
주의할 점이 있습니다. GitHub는 UTC 시간을 사용합니다. 베이징 시간 오전 9시에 작업을 실행하려면 UTC 1시(0 1 * * *)로 설정해야 합니다.
workflow_dispatch: 수동 실행
자동으로 실행하지 않고 버튼을 눌러 직접 실행하고 싶을 때가 있습니다. 이때 workflow_dispatch를 사용합니다.
on:
workflow_dispatch:
inputs:
environment:
description: '배포 환경'
required: true
default: 'staging'
type: choice
options:
- staging
- production
설정을 마치면 Actions 페이지에 ‘Run workflow’ 버튼이 나타납니다. 버튼을 클릭해 환경 매개변수를 선택한 뒤 실행할 수 있습니다.
이 트리거는 배포 상황에서 특히 유용합니다. 테스트는 자동으로 실행하고 배포는 수동으로 실행할 수 있습니다.
workflow_call: 워크플로 재사용
워크플로 로직이 복잡하거나 여러 저장소에서 같은 프로세스를 사용해야 한다면 workflow_call로 워크플로를 재사용 가능한 구성 요소로 만들 수 있습니다.
먼저 재사용 가능한 워크플로를 정의합니다.
# .github/workflows/ci.yml
on:
workflow_call:
inputs:
node-version:
required: true
type: string
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
다른 워크플로에서 다음과 같이 호출합니다.
# .github/workflows/main.yml
on: push
jobs:
call-ci:
uses: ./.github/workflows/ci.yml
with:
node-version: '20'
이제 여러 저장소에서 같은 CI 로직을 재사용할 수 있습니다. 한 곳만 수정하면 사용하는 모든 곳에 적용됩니다.
나머지 트리거(release, issues, repository_dispatch)는 상대적으로 사용 빈도가 낮아 여기서는 자세히 설명하지 않겠습니다. 관심이 있다면 GitHub 공식 문서를 참고하세요.
실전: 첫 번째 워크플로 템플릿
개념을 충분히 살펴봤으니 직접 사용해 보겠습니다.
아래는 완전한 Node.js 프로젝트 CI 워크플로입니다. 자신의 프로젝트에 그대로 복사해 사용할 수 있습니다.
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
# 1. 코드 가져오기
- name: Checkout code
uses: actions/checkout@v4
# 2. Node.js 환경 설정
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
# 3. 의존성 설치
- name: Install dependencies
run: npm ci
# 4. 테스트 실행
- name: Run tests
run: npm test
어떻게 사용하나요?
1단계: 프로젝트 루트 디렉터리에 .github/workflows 폴더를 만듭니다. 이미 있다면 이 단계는 건너뜁니다.
2단계: 이 폴더에 ci.yml 파일을 만들고 위 코드를 붙여 넣습니다.
3단계: 변경 사항을 커밋하고 GitHub에 푸시합니다.
푸시한 뒤 저장소의 Actions 탭을 열면 워크플로가 실행되는 모습을 볼 수 있습니다.
한 줄씩 살펴보기
name: CI: Actions 페이지에 표시되는 워크플로 이름on: push: branches: [main]: main 브랜치에 코드를 푸시할 때 실행on: pull_request: branches: [main]: main을 대상으로 PR을 만들 때도 실행jobs: build:: build라는 job 정의runs-on: ubuntu-latest: GitHub가 제공하는 최신 Ubuntu에서 실행actions/checkout@v4: 코드를 가상 머신으로 가져오는 공식 Actionactions/setup-node@v4: Node.js 환경을 설정하는 공식 Actionnpm ci: 의존성 설치(npm install보다 빠르고 깔끔함)npm test: 테스트 실행
Node.js 프로젝트가 아니라면 중간 단계를 바꾸기만 하면 됩니다. 예를 들어 Python 프로젝트는 다음과 같습니다.
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- run: pip install -r requirements.txt
- run: pytest
기본 흐름은 ‘코드 가져오기 → 환경 설정 → 의존성 설치 → 테스트 실행’입니다.
자주 발생하는 설정 오류 점검
제가 겪은 문제를 여러분은 다시 겪지 않기를 바랍니다.
문제가 발생했을 때 확인할 수 있도록 점검 목록을 정리했습니다.
| 오류 현상 | 가능한 원인 | 해결 방법 |
|---|---|---|
| 워크플로가 실행되지 않음 | 브랜치 필터 조건 오류 | branches 설정을 확인하고 브랜치 이름의 대소문자가 정확한지 검사 |
| YAML 파싱 실패 | 들여쓰기에 Tab 사용 | 모든 들여쓰기를 공백으로 변경. YAML은 Tab을 지원하지 않음 |
| Secrets가 적용되지 않음 | 범위 오류 | secrets가 올바른 계층(job 또는 step)에 있는지 확인 |
| Job 의존 관계 실패 | needs 참조 오류 | job-id 철자와 대소문자 확인 |
| 워크플로 실행이 너무 느림 | 캐시를 사용하지 않음 | actions/cache를 추가해 의존성 캐시 |
| 권한 부족 오류 | GITHUB_TOKEN 권한 부족 | job에 permissions 설정 추가 |
오류 1: 잘못된 들여쓰기
가장 자주 발생하는 오류입니다.
YAML은 들여쓰기에 매우 민감합니다. 반드시 공백을 사용해야 하며 Tab은 사용할 수 없습니다. 각 단계의 들여쓰기는 공백 2개여야 합니다. 다른 개수를 사용하더라도 전체 파일에서 일관되어야 하지만 GitHub Actions에서는 기본적으로 2개를 사용합니다.
# 잘못된 예: 들여쓰기가 어긋남
jobs:
build:
runs-on: ubuntu-latest # 이 줄은 공백 4개만큼 들여써야 함
# 올바른 작성법
jobs:
build:
runs-on: ubuntu-latest # 공백 4개만큼 들여쓰기
대부분의 편집기(VS Code, WebStorm)는 ‘Tab 키를 누르면 자동으로 공백 삽입’을 설정할 수 있습니다. 매번 공백을 직접 입력하지 않도록 이 설정을 켜는 것이 좋습니다.
오류 2: 잘못된 브랜치 이름
GitHub 브랜치 이름은 대소문자를 구분합니다. main과 Main은 서로 다른 브랜치입니다.
# 브랜치 이름이 main인 경우
on:
push:
branches: [Main] # 잘못됨. 실행되지 않음
# 올바른 작성법
on:
push:
branches: [main] # 소문자
브랜치 이름이 확실하지 않다면 GitHub 저장소 페이지에서 확인하거나 로컬에서 git branch를 실행하세요.
오류 3: job-id 철자 오류
워크플로에 여러 job이 있고 서로 의존 관계가 있다면 needs 필드는 다른 job의 id를 정확히 참조해야 합니다.
jobs:
test:
runs-on: ubuntu-latest
# ...
deploy:
needs: Test # 잘못됨. 대소문자가 일치하지 않음
runs-on: ubuntu-latest
# 올바른 작성법
jobs:
test:
runs-on: ubuntu-latest
deploy:
needs: test # 소문자이며 위의 job id와 일치
runs-on: ubuntu-latest
오류 4: 잘못된 계층에서 Secrets 사용
GitHub Secrets에는 저장소 수준과 환경 수준이라는 두 가지 범위가 있습니다. 참조할 때는 ${{ secrets.XXX }}를 사용합니다.
# 잘못된 예: secrets 작성 위치 오류
jobs:
build:
runs-on: ubuntu-latest
env:
API_KEY: secrets.MY_KEY # 잘못됨. ${{ }}가 없음
# 올바른 작성법
jobs:
build:
runs-on: ubuntu-latest
env:
API_KEY: ${{ secrets.MY_KEY }} # ${{ }}로 감싸기
또한 Secrets는 암호화되어 있으므로 로그에서 실제 값을 볼 수 없습니다. 로그에서 제대로 전달되었는지 확인하려면 대체 값을 출력할 수 있습니다.
- name: Debug
run: echo "API_KEY is set: ${{ secrets.MY_KEY != '' }}"
이렇게 하면 실제 Key를 노출하지 않고 값이 비어 있는지 확인할 수 있습니다.
GitHub Actions와 다른 CI/CD 도구 비교
Jenkins, GitLab CI, CircleCI를 사용해 본 적이 있을 수 있습니다. 그렇다면 이들과 비교했을 때 GitHub Actions의 장단점은 무엇일까요?
표로 비교해 보겠습니다.
| 비교 항목 | GitHub Actions | GitLab CI | Jenkins | CircleCI |
|---|---|---|---|---|
| 설정 언어 | YAML | YAML | Groovy | YAML |
| 호스팅 방식 | 클라우드 네이티브 | 클라우드/자체 호스팅 | 자체 호스팅 | 클라우드 네이티브 |
| 통합 수준 | GitHub 네이티브 | GitLab 네이티브 | 설정 필요 | 설정 필요 |
| 학습 곡선 | 낮음 | 낮음 | 높음 | 보통 |
| 무료 사용량 | 월 2000분(비공개 저장소) | 월 400분 | 제한 없음(자체 호스팅) | 월 6000분 |
| 공개 저장소 | 제한 없음 | 제한 없음 | 제한 없음 | 제한 없음 |
GitHub Actions의 장점
1. 별도 설정 없는 통합
코드가 이미 GitHub에 호스팅되어 있다면 GitHub Actions가 가장 자연스러운 선택입니다. webhook을 따로 설정하거나 서버를 유지하거나 플러그인을 설치할 필요가 없습니다. YAML 파일을 만들고 푸시하면 바로 실행됩니다.
2. Marketplace 생태계
GitHub Marketplace에는 수천 개의 Actions가 있으며 AWS, Azure, Google Cloud도 공식 Actions를 제공합니다. 필요한 기능은 누군가 이미 만들어 두었을 가능성이 큽니다. uses로 바로 호출하면 됩니다.
3. 개인 개발자에게 유리한 무료 사용량
공개 저장소는 제한 없이 사용할 수 있고 비공개 저장소에는 매월 2000분이 제공됩니다. 개인 프로젝트나 소규모 팀에는 대체로 충분합니다.
GitHub Actions를 선택하지 말아야 할 때
1. 코드가 GitHub에 없는 경우
GitLab이나 Bitbucket을 사용한다면 각 플랫폼에 내장된 CI/CD를 사용하는 편이 좋습니다. GitHub Actions도 webhook으로 실행할 수 있지만 복잡하게 연결하는 것보다 기본 제공 도구를 사용하는 편이 낫습니다.
2. 실행 환경을 완전히 제어해야 하는 경우
GitHub Actions의 runner 환경은 정해져 있습니다(Ubuntu/Windows/macOS). 원하는 소프트웨어를 설치하거나 특수한 환경을 설정할 수 없습니다. 이런 경우에는 Jenkins와 자체 호스팅 runner를 함께 사용하는 편이 더 유연합니다.
3. 보안 요구 사항이 매우 높은 경우
GitHub Actions의 runner는 GitHub가 호스팅하는 가상 머신입니다. 코드에 매우 민감한 정보가 포함되어 있다면 자체 CI 시스템이 필요할 수 있습니다. 다만 GitHub도 self-hosted runner를 지원하므로 절충할 수 있습니다.
제안
대부분의 개인 개발자와 소규모 팀에는 다음과 같이 권합니다.
- 코드가 GitHub에 있음 → GitHub Actions 선택
- 코드가 GitLab에 있음 → GitLab CI 선택
- 고도의 사용자 지정이 필요함 → Jenkins 또는 self-hosted runner 선택
절대적으로 가장 좋은 선택은 없습니다. 자신의 상황에 맞는 도구가 가장 좋은 도구입니다.
마무리
여기까지 읽었다면 이제 GitHub Actions의 YAML 워크플로가 어떻게 작동하는지 대략 이해했을 것입니다.
핵심 내용은 다음과 같습니다.
- 네 가지 핵심 필드:
name으로 이름 지정,on으로 트리거 설정,jobs로 작업 정의,steps로 단계 실행 - 자주 쓰는 트리거 8가지: 그중
push,pull_request,schedule을 가장 많이 사용 - 복사해 쓸 수 있는 템플릿 하나: 코드 가져오기 → 환경 설정 → 의존성 설치 → 테스트 실행
- 자주 발생하는 문제 네 가지: 들여쓰기, 브랜치 이름, job-id, Secrets
이제 다음 단계로 넘어가 보세요.
- 자신의 프로젝트에 첫 워크플로를 만듭니다. 이 글의 템플릿을 복사해 프로젝트에 맞게 수정하면 됩니다.
- 시리즈의 심화 글 《GitHub Actions 캐시 전략: CI/CD 파이프라인 속도 5배 높이기》를 읽고 워크플로를 더 빠르게 만드는 방법을 알아봅니다.
- GitHub Marketplace를 둘러보며 바로 활용할 만한 Actions를 찾아봅니다.
자동화의 편리함을 한번 맛보면 이전 방식으로 돌아가기 어렵습니다.
FAQ
GitHub Actions 워크플로에 반드시 포함해야 하는 필드는 무엇인가요?
push와 pull_request 트리거는 무엇이 다른가요?
YAML 들여쓰기에는 Tab과 공백 중 무엇을 사용해야 하나요?
무료 사용량은 얼마이며 초과하면 어떻게 해야 하나요?
3분 읽기 · 게시일: 2026년 4월 10일 · 수정일: 2026년 9월 8일
GitHub Actions 완전 가이드
이 시리즈의 첫 글을 읽고 있습니다. 다음 글로 이어가거나 시리즈 허브에서 전체 경로를 확인하세요.



댓글
GitHub로 로그인하여 댓글을 남기세요