포스트

[이제와서 시작하는 GitHub 마스터하기 - 고급편 #1-3] GitHub Actions: 고급 기능과 최적화

[이제와서 시작하는 GitHub 마스터하기 - 고급편 #1-3] GitHub Actions: 고급 기능과 최적화

들어가며

이전 편에서 CI/CD 파이프라인을 구축하는 방법을 배웠습니다. 이번 편에서는 워크플로우를 더 효율적으로 만드는 고급 기능들을 알아보겠습니다.

💡 이 글은 누구를 위한 글인가요?

  • 기본 CI/CD 파이프라인을 구축해본 분
  • 워크플로우를 재사용하고 싶은 분
  • 실행 시간과 비용을 최적화하고 싶은 분

1. 재사용 가능한 워크플로우 (⭐ 중급자 필수)

왜 재사용이 필요한가요?

문제: 여러 프로젝트에서 비슷한 워크플로우 반복

1
2
3
project-a/.github/workflows/test.yml  (100줄)
project-b/.github/workflows/test.yml  (100줄, 거의 같음)
project-c/.github/workflows/test.yml  (100줄, 거의 같음)

해결: 하나의 템플릿을 만들어서 재사용!

1
2
3
4
5
.github/workflows/reusable-test.yml (템플릿)
  ↓ (사용)
project-a/.github/workflows/test.yml (10줄)
project-b/.github/workflows/test.yml (10줄)
project-c/.github/workflows/test.yml (10줄)

재사용 가능한 워크플로우 만들기

Step 1: 템플릿 파일 생성

.github/workflows/reusable-test.yml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
name: Reusable Test Workflow

on:
  workflow_call:  # 다른 워크플로우에서 호출 가능
    inputs:
      node-version:
        required: true
        type: string
        description: 'Node.js version to use'
      run-lint:
        required: false
        type: boolean
        default: true
        description: 'Run lint'
    secrets:
      npm-token:
        required: false
        description: 'NPM registry token'

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: ${{ inputs.node-version }}

      - name: Install dependencies
        run: npm ci
        env:
          NODE_AUTH_TOKEN: ${{ secrets.npm-token }}

      - name: Lint
        if: ${{ inputs.run-lint }}
        run: npm run lint

      - name: Test
        run: npm test

Step 2: 다른 워크플로우에서 사용

.github/workflows/ci.yml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
name: CI

on: [push, pull_request]

jobs:
  test-node-18:
    uses: ./.github/workflows/reusable-test.yml
    with:
      node-version: '18.x'
      run-lint: true
    secrets:
      npm-token: ${{ secrets.NPM_TOKEN }}

  test-node-20:
    uses: ./.github/workflows/reusable-test.yml
    with:
      node-version: '20.x'
      run-lint: false

장점

이점 설명 예시
재사용성 한 번 작성, 여러 곳에서 사용 모든 프로젝트에 동일한 테스트
유지보수 한 곳만 수정하면 모든 곳에 적용 버그 수정 한 번에 배포
일관성 모든 프로젝트가 같은 방식 사용 표준화된 CI/CD

2. 매트릭스 전략: 여러 환경에서 동시 테스트

기본 매트릭스

목표: Node.js 16, 18, 20 버전에서 모두 테스트하기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: [16, 18, 20]  # 3개 버전

    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}

      - run: npm ci
      - run: npm test

이 워크플로우는 3개의 병렬 작업을 생성합니다!

graph LR
    A[Git Push] --> B[Matrix Strategy]
    B --> C[Node 16 Test]
    B --> D[Node 18 Test]
    B --> E[Node 20 Test]
    C --> F[결과]
    D --> F
    E --> F

    style B fill:#bbf,stroke:#333,stroke-width:2px
    style F fill:#9f9,stroke:#333,stroke-width:2px

다차원 매트릭스

목표: 여러 OS + 여러 Node 버전 조합 테스트

1
2
3
4
5
6
strategy:
  matrix:
    os: [ubuntu-latest, windows-latest, macos-latest]
    node-version: [18, 20]

runs-on: ${{ matrix.os }}

이것은 6개의 조합을 만듭니다!

OS Node 18 Node 20
Ubuntu
Windows
macOS

특정 조합 제외하기

1
2
3
4
5
6
7
8
strategy:
  matrix:
    os: [ubuntu-latest, windows-latest, macos-latest]
    node-version: [16, 18, 20]
    exclude:
      # Windows + Node 16은 불안정하여 제외
      - os: windows-latest
        node-version: 16
🚀 고급 옵션 (클릭하여 펼치기)

특정 조합 추가하기

1
2
3
4
5
6
7
8
9
strategy:
  matrix:
    os: [ubuntu-latest]
    node-version: [18, 20]
    include:
      # macOS에서 최신 버전만 추가 테스트
      - os: macos-latest
        node-version: 20
        experimental: true

하나 실패해도 나머지 계속 실행

1
2
3
4
strategy:
  fail-fast: false  # 기본값: true
  matrix:
    node-version: [16, 18, 20]
  • fail-fast: true - 하나 실패하면 나머지 중단
  • fail-fast: false - 모두 실행

3. 조건부 실행

브랜치별 다른 동작

1
2
3
4
5
6
7
8
9
10
11
12
13
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Deploy to Production
        if: github.ref == 'refs/heads/main'
        run: echo "Deploying to production"

      - name: Deploy to Staging
        if: github.ref == 'refs/heads/develop'
        run: echo "Deploying to staging"

PR에서만 실행

1
2
3
4
5
- name: Comment on PR
  if: github.event_name == 'pull_request'
  run: |
    echo "This is a PR!"
    echo "PR number: $"

이전 단계 결과에 따라 실행

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - name: Run tests
        id: tests
        run: npm test
        continue-on-error: true  # 실패해도 계속

      - name: Notify on failure
        if: failure()
        run: echo "Tests failed!"

      - name: Notify on success
        if: success()
        run: echo "All tests passed!"

      - name: Always cleanup
        if: always()
        run: echo "Cleanup resources"

조건 함수

함수 언제 true? 예시
success() 이전 단계 모두 성공 배포
failure() 이전 단계 실패 에러 알림
always() 항상 실행 정리 작업
cancelled() 워크플로우 취소됨 리소스 정리

4. 모니터링과 알림

Slack 알림

목표: 배포 성공/실패 시 Slack으로 알림

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Deploy
        id: deploy
        run: |
          # 배포 로직
          echo "deploy-url=https://app.example.com" >> $GITHUB_OUTPUT

      - name: Notify Slack (Success)
        if: success()
        uses: slackapi/slack-github-action@v1.24.0
        with:
          webhook-url: ${{ secrets.SLACK_WEBHOOK }}
          payload: |
            {
              "text": "✅ 배포 성공!",
              "blocks": [
                {
                  "type": "section",
                  "text": {
                    "type": "mrkdwn",
                    "text": "*배포 성공!* 🎉\n*URL:* ${{ steps.deploy.outputs.deploy-url }}"
                  }
                }
              ]
            }

      - name: Notify Slack (Failure)
        if: failure()
        uses: slackapi/slack-github-action@v1.24.0
        with:
          webhook-url: ${{ secrets.SLACK_WEBHOOK }}
          payload: |
            {
              "text": "❌ 배포 실패!",
              "blocks": [
                {
                  "type": "section",
                  "text": {
                    "type": "mrkdwn",
                    "text": "*배포 실패* ❌\n*로그:* ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
                  }
                }
              ]
            }

Slack Webhook 설정 방법

  1. Slack 워크스페이스에서 AppsIncoming Webhooks 추가
  2. Webhook URL 복사
  3. GitHub → SettingsSecretsSLACK_WEBHOOK 추가

Discord 알림

1
2
3
4
5
6
- name: Discord notification
  uses: Ilshidur/action-discord@master
  env:
    DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
  with:
    args: '배포 완료! https://app.example.com'

Email 알림

1
2
3
4
5
6
7
8
9
10
11
12
- name: Send email
  if: failure()
  uses: dawidd6/action-send-mail@v3
  with:
    server_address: smtp.gmail.com
    server_port: 465
    username: ${{ secrets.EMAIL_USERNAME }}
    password: ${{ secrets.EMAIL_PASSWORD }}
    subject: GitHub Actions 실패 알림
    to: team@example.com
    from: GitHub Actions
    body: 워크플로우가 실패했습니다!

상태 배지 추가

README.md에 추가:

1
2
3
4
# My Project

![CI Status](https://github.com/username/repo/workflows/CI/badge.svg)
![Deploy Status](https://github.com/username/repo/workflows/Deploy/badge.svg)

5. 비용 최적화 (⭐ 중급자 필수)

GitHub Actions 요금제

플랜 Public 저장소 Private 저장소 (월 무료 시간) 추가 비용
Free 무제한 2,000분 $0.008/분
Pro 무제한 3,000분 $0.008/분
Team 무제한 10,000분 $0.008/분

⚠️ 주의: 무료 시간을 초과하면 요금이 부과됩니다!

비용 절감 전략

1. 중복 실행 방지

1
2
3
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true  # 이전 실행 취소

효과: 같은 브랜치에 연속으로 푸시 시 이전 워크플로우 자동 취소!

2. 변경된 파일만 검사

1
2
3
4
5
6
7
8
on:
  push:
    paths:
      - 'src/**'        # src 폴더만
      - 'package.json'
  pull_request:
    paths:
      - 'src/**'

효과: 문서 수정 시에는 워크플로우 실행 안 함!

3. 조건부 Job 실행

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
jobs:
  check-changes:
    runs-on: ubuntu-latest
    outputs:
      backend-changed: ${{ steps.filter.outputs.backend }}
      frontend-changed: ${{ steps.filter.outputs.frontend }}
    steps:
      - uses: actions/checkout@v4
      - uses: dorny/paths-filter@v2
        id: filter
        with:
          filters: |
            backend:
              - 'backend/**'
            frontend:
              - 'frontend/**'

  test-backend:
    needs: check-changes
    if: needs.check-changes.outputs.backend-changed == 'true'
    runs-on: ubuntu-latest
    steps:
      - run: echo "Testing backend"

  test-frontend:
    needs: check-changes
    if: needs.check-changes.outputs.frontend-changed == 'true'
    runs-on: ubuntu-latest
    steps:
      - run: echo "Testing frontend"

효과: 백엔드만 변경하면 프론트엔드 테스트 건너뜀!

4. 캐싱 활용

1
2
3
4
5
- name: Cache dependencies
  uses: actions/cache@v3
  with:
    path: ~/.npm
    key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}

효과: 의존성 설치 시간 2분 → 10초!

비용 모니터링

SettingsBilling and plansActions 에서 사용량 확인:

  • 이번 달 사용 시간
  • 남은 무료 시간
  • 예상 비용

6. 2025년 최신 기능

GPU Runners (ML/AI 작업)

1
2
3
4
5
6
jobs:
  train-model:
    runs-on: [gpu, nvidia-t4]  # GPU 러너
    steps:
      - name: Train ML model
        run: python train.py

언제 사용?:

  • 머신러닝 모델 학습
  • 이미지/비디오 처리
  • 3D 렌더링

💡 참고: GPU 러너는 유료입니다!

Copilot 통합 (AI 기반 워크플로우 생성)

1
2
3
4
5
- name: AI-powered workflow optimization
  uses: github/copilot-actions@v1
  with:
    task: optimize-workflow
    context: ${{ toJson(github) }}

기능:

  • 자연어로 워크플로우 생성
  • 자동 최적화 제안
  • 에러 자동 수정

Merge Queue (안전한 병합)

1
2
on:
  merge_group:  # Merge Queue 이벤트

기능:

  • PR을 순차적으로 테스트 후 병합
  • 동시 병합으로 인한 충돌 방지

7. 트러블슈팅 가이드

일반적인 문제와 해결법

문제 원인 해결 방법
워크플로우 실행 안 됨 파일 위치 오류 .github/workflows/ 확인
권한 오류 토큰 권한 부족 permissions 추가
캐시 미스 키 변경됨 hashFiles() 확인
시크릿 안 보임 이름 오타 대소문자 구분 확인
느린 빌드 캐싱 없음 cache 옵션 추가

디버깅 팁

1. Context 정보 출력

1
2
3
4
5
- name: Dump GitHub context
  run: echo '${{ toJson(github) }}'

- name: Dump Job context
  run: echo '${{ toJson(job) }}'

2. SSH로 러너 접속 (고급)

1
2
3
4
- name: Debug with tmate
  if: failure()
  uses: mxschmitt/action-tmate@v3
  timeout-minutes: 15

실패 시 SSH로 접속하여 직접 디버깅 가능!

3. 로컬에서 테스트

1
2
3
4
5
# act 설치
brew install act

# 워크플로우 로컬 실행
act push

8. 베스트 프랙티스 체크리스트

보안

  • 시크릿을 코드에 쓰지 않기
  • permissions 최소 권한으로 설정
  • Dependabot으로 의존성 업데이트
  • 써드파티 Action은 특정 버전(@v4)사용

성능

  • 캐싱 활용하기
  • 병렬 Job 최대한 활용
  • 변경된 파일만 검사하기
  • 불필요한 Step 제거

유지보수

  • 재사용 가능한 워크플로우 작성
  • 명확한 이름 사용
  • 주석으로 복잡한 로직 설명
  • README에 워크플로우 문서화

9. 실습 과제

📝 초보자 과제 (필수)

  • 재사용 가능한 워크플로우 만들어보기
  • 매트릭스 전략으로 여러 버전 테스트하기
  • Slack 알림 추가하기
  • README에 상태 배지 추가하기

🚀 중급자 과제 (도전)

  • 조건부 배포 (main → production, develop → staging)
  • 비용 최적화 (concurrency, paths 필터)
  • 커스텀 Action 만들기
  • 멀티 스테이지 파이프라인 구축

10. 정리

GitHub Actions는 무궁무진합니다! 기본만 알아도 90%는 해결됩니다.

이 시리즈에서 배운 것:

  1. [#1] 입문편: Workflow, Job, Step 기본 개념
  2. [#1-2] CI/CD편: 언어별 CI, 자동 배포
  3. [#1-3] 고급편 (현재): 재사용, 매트릭스, 알림, 최적화

다음 단계:

  • 본인 프로젝트에 전체 파이프라인 구축
  • 비용 최적화로 무료 플랜 내에서 활용
  • [고급편 #2]에서 더 복잡한 패턴 배우기

📚 GitHub 마스터하기 시리즈

🚀 고급편 (전문가)

  1. GitHub Actions 입문
  2. GitHub Actions: CI/CD 파이프라인 ⬅️ 이전 편
  3. [GitHub Actions: 고급 기능] (현재 글)
  4. Actions 고급 활용 ⬅️ 다음 편
  5. Webhooks와 API
  6. GitHub Apps 개발
  7. 보안 기능
  8. GitHub Packages
  9. Codespaces
  10. GitHub CLI
  11. 통계와 인사이트

“늦었다고 생각할 때가 가장 빠른 때입니다. 지금 시작하세요!” 🚀

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.