GitHub Actions를 왜 쓰는가
저장소와 CI/CD가 한 플랫폼에 있어 세팅 비용이 거의 없습니다. Pull Request·이슈·릴리즈 같은 이벤트에 바로 훅을 걸 수 있고, 마켓플레이스 덕분에 대부분의 도구가 이미 빌딩 블록으로 존재합니다. 반면 복잡한 배포 오케스트레이션(Approval·Canary·Rollback)이 필요하면 ArgoCD 같은 전용 플랫폼이 더 적합합니다. GitHub Actions는 빌드·테스트·간단한 배포까지가 스위트 스폿입니다
4단계 계층 구조
flowchart LR
E["Event<br/>(push, PR, cron)"]
subgraph wf["Workflow (YAML 파일)"]
J1["Job A"]
J2["Job B"]
J1 --> J2
end
J1 --- S1["Step: checkout"]
J1 --- S2["Step: setup-python"]
J1 --- S3["Step: run pytest"]
S2 -.-> A1["Action:<br/>actions/setup-python"]
E ==> J1
classDef primary fill:#2563eb,stroke:#1e40af,color:#ffffff
classDef info fill:#0891b2,stroke:#0e7490,color:#ffffff
classDef success fill:#059669,stroke:#047857,color:#ffffff
classDef neutral fill:#475569,stroke:#334155,color:#ffffff
class E primary
class J1,J2 info
class S1,S2,S3 success
class A1 neutral
- Workflow:
.github/workflows/하위의 YAML 파일 하나입니다. 이벤트로 트리거됩니다 - Job: 하나의 Runner(VM 또는 컨테이너)에서 실행되는 단위입니다. Job 간 의존은
needs로 표현합니다 - Step: Job 안의 순차 단계입니다. Shell 명령이나 Action을 호출합니다
- Action: 재사용 가능한 작업 단위입니다. 마켓플레이스(
actions/checkout@v4) 또는 커스텀 리포로 배포됩니다
Workflow 기본 문법
name: CI
on:
push:
branches: [main]
pull_request:
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run tests
run: echo "hello"
최소 구성은 이벤트(on) → Job → Step 세 블록입니다. 이 세 가지만 있으면 동작합니다
이벤트 트리거
| 이벤트 | 용도 | 비고 |
|---|---|---|
push |
브랜치에 커밋이 올라갔을 때 | 경로·브랜치 필터 가능 |
pull_request |
PR 오픈·업데이트 | types 로 세분화 |
workflow_dispatch |
수동 실행 | UI 또는 API로 트리거 |
schedule |
cron 스케줄 | UTC 기준 |
release |
릴리즈 생성 시 | 아티팩트 업로드에 자주 사용 |
workflow_call |
다른 워크플로우에서 호출 | Reusable Workflow 용 |
on:
push:
branches: [main]
paths-ignore:
- "docs/**"
workflow_dispatch:
inputs:
environment:
description: "Target environment"
required: true
default: staging
type: choice
options: [staging, production]
schedule:
- cron: "0 18 * * 1-5" # 평일 UTC 18:00 (KST 03:00)
Job과 Runner
Job은 독립된 Runner에서 실행됩니다. Runner는 Job 시작마다 깨끗한 상태로 준비됩니다
| 구분 | GitHub-hosted | Self-hosted |
|---|---|---|
| 관리 부담 | 없음 | 직접 운영 |
| 스펙 | 2코어 기본 | 원하는 대로 |
| 네트워크 | 퍼블릭만 | VPC 접근 가능 |
| 비용 | 무료 한도 + 분당 과금 | 인프라 비용 |
jobs:
test:
runs-on: ubuntu-latest # GitHub-hosted
build:
runs-on: [self-hosted, linux, gpu] # 지정 라벨 Runner
needs: test # test Job이 성공해야 시작
needs 로 의존을 지정하지 않으면 Job들은 기본적으로 병렬 실행됩니다
Step과 Action
Step은 두 가지 형태입니다
steps:
# 1) Action 호출
- uses: actions/setup-python@v5
with:
python-version: "3.12"
# 2) Shell 명령 실행
- name: Install deps
run: |
pip install uv
uv sync --frozen
shell: bash
working-directory: ./backend
Action 버전 지정 방법
@v4 같은 태그는 재할당될 수 있습니다. 보안이 중요한 워크플로우라면 @</code> 로 핀 고정하는 게 안전합니다. Dependabot이 주기적으로 업데이트 PR을 올려줍니다
</div>
## Context와 표현식
`${{ }}` 문법으로 런타임 값을 참조합니다. 자주 쓰는 Context는 다음과 같습니다
| 표현식 | 값 |
|--------|----|
| `github.sha` | 현재 커밋 SHA |
| `github.ref` | `refs/heads/main` 같은 ref |
| `github.event_name` | 트리거 이벤트 이름 |
| `github.actor` | 워크플로우를 시작한 사용자 |
| `runner.os` | `Linux`, `macOS`, `Windows` |
| `secrets.NAME` | Repository·Environment Secret |
| `vars.NAME` | 평문 변수 |
| `job.status` | 직전 Job 상태 |
| `steps..outputs.` | 이전 Step의 출력 |
```yaml
- name: Build
id: build
run: |
TAG=${{ github.sha }}
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
- name: Use tag
run: echo "Built ${{ steps.build.outputs.tag }}"
if: github.event_name == 'push'
```
## 최소 예시 워크플로우
Python 라이브러리 저장소에서 PR마다 테스트를 돌리는 워크플로우입니다
```yaml
name: test
on:
pull_request:
push:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install deps
run: |
pip install uv
uv sync --frozen
- name: Run tests
run: uv run pytest tests/ --junit-xml=report.xml
- name: Upload report
if: always()
uses: actions/upload-artifact@v4
with:
name: junit-report
path: report.xml
```
- `timeout-minutes`: Job별 타임아웃입니다. Runner 무한 점유 방지용으로 항상 설정합니다
- `if: always()`: 앞 Step 실패해도 아티팩트는 업로드합니다
## 정리
| 요소 | 정의 |
|------|------|
| Workflow | `.github/workflows/` 의 YAML 한 개 |
| Job | Runner 한 대에서 실행되는 단위 |
| Step | Job 안의 순차 단계 (Action 호출 또는 Shell) |
| Action | 재사용 가능한 Step 빌딩 블록 |
| Context | `${{ github.*, secrets.*, steps.* }}` 런타임 값 |
다음 글에서는 이 구조를 실전에 써서 Build·Test·Deploy가 하나의 워크플로우에서 돌아가는 파이프라인을 설계합니다