13. CI/CD 입문 — GitHub Actions

🎯 이 장의 목표
  • CI/CD가 무엇이고 왜 필요한지 이해한다
  • GitHub Actions의 4가지 구성 요소(워크플로우/이벤트/잡/스텝)를 안다
  • 첫 워크플로우 YAML 파일을 직접 작성한다
  • 시크릿, 매트릭스, 배포까지 기본 패턴을 익힌다

13.1 CI/CD란 무엇인가?

지금까지는 코드를 push하면 그걸로 끝이었습니다. 하지만 실무에서는 push할 때마다 다음을 자동으로 하고 싶습니다.

  • 테스트를 돌려 코드가 깨지지 않았는지 확인
  • 코드 스타일(린트)을 검사
  • 빌드가 되는지 확인
  • 문제없으면 서버에 자동 배포
💡 팁
린트(lint)는 코드를 실행하지 않고 훑어보며 문법 오류·스타일 위반·의심스러운 패턴을 자동으로 잡아내는 검사입니다. 그 검사 도구를 린터(linter)라고 합니다. 맞춤법 검사기의 코드 버전이라고 생각하면 됩니다.

이렇게 반복적인 작업을 자동화하는 것이 CI/CD입니다.

약자풀이의미
CIContinuous Integration (지속적 통합)코드 변경마다 자동으로 빌드·테스트
CDContinuous Delivery/Deployment (지속적 배포)검증된 코드를 자동으로 배포
🏭 공장 컨베이어 벨트 비유
부품(코드)을 올려놓으면(push), 벨트가 자동으로 검사 → 조립 → 포장 → 출고(배포)까지 처리합니다. 사람이 매번 손으로 검사하지 않아도 불량품(버그)이 걸러집니다.
💡 팁
왜 중요한가? "내 컴퓨터에선 됐는데요?"라는 말이 사라집니다. 모든 변경이 동일한 깨끗한 환경에서 자동 검증되므로, 버그를 일찍 발견하고 팀 전체의 코드 품질이 안정됩니다.

13.2 GitHub Actions란?

GitHub Actions는 GitHub에 내장된 CI/CD 플랫폼입니다. 별도 서버(예전의 Jenkins 같은)를 둘 필요 없이, 저장소 안에 설정 파일만 두면 GitHub의 클라우드 서버에서 자동으로 작업이 실행됩니다.

  • 공개 저장소는 무료, 비공개도 넉넉한 무료 사용량 제공
  • 설정은 YAML 파일 하나로 끝
  • push, PR, 일정(cron), 수동 트리거 등 다양한 계기로 실행

13.3 4가지 핵심 구성 요소

GitHub Actions를 이해하는 것은 이 4개 단어를 이해하는 것입니다.

flowchart TD
    W["📄 워크플로우 (Workflow)<br/><small>.github/workflows/ci.yml</small>"]
    E["⚡ 이벤트 (Event)<br/><small>언제 실행? on: push</small>"]
    J["⚙️ 잡 (Job)<br/><small>무엇을? test, build, deploy</small>"]
    S["📍 스텝 (Step)<br/><small>한 단계씩의 명령</small>"]
    W --> E --> J --> S
    classDef wf fill:#f3e8ff,stroke:#9333ea,color:#6b21a8
    classDef ev fill:#fef3c7,stroke:#d97706,color:#92400e
    classDef jb fill:#dbeafe,stroke:#2563eb,color:#1e40af
    classDef st fill:#dcfce7,stroke:#16a34a,color:#166534
    class W wf
    class E ev
    class J jb
    class S st
요소설명
Workflow (워크플로우)자동화 전체를 정의한 YAML 파일. .github/workflows/ 폴더에 둠
Event (이벤트)워크플로우를 실행시키는 계기 (push, pull_request 등)
Job (잡)워크플로우 안의 독립된 작업 단위. 가상 서버(러너)에서 실행
Step (스텝)잡 안의 개별 단계. 명령(run)이나 액션(uses)을 실행
Runner (러너)잡이 실제로 돌아가는 가상 서버 (ubuntu-latest 등)
Action (액션)재사용 가능한 작업 묶음 (actions/checkout 등)

13.4 YAML 문법 기초 (잠깐!)

워크플로우는 YAML로 씁니다. 처음 보면 낯서니 규칙 3가지만 알아둡시다.

📌 핵심
YAML(YAML Ain't Markup Language)은 설정을 사람이 읽기 쉽게 적는 데이터 형식입니다. JSON과 목적은 같지만, 괄호 대신 들여쓰기로 구조를 표현해 더 간결합니다. 각종 도구의 설정 파일에 널리 쓰입니다.
  1. 들여쓰기로 계층을 표현 (스페이스만, 탭 금지). 들여쓰기가 곧 구조입니다.
  2. key: value 형태의 짝
  3. -로 시작하면 목록(리스트)의 항목
YAML
person:               # 객체
  name: Gildong       # 문자열
  age: 20             # 숫자
  hobbies:            # 리스트
    - coding
    - reading
⚠️ 흔한 실수
흔한 실수: YAML은 들여쓰기에 매우 민감합니다. 칸이 안 맞으면 워크플로우가 통째로 실패합니다. 탭 대신 스페이스 2칸을 일관되게 쓰세요. GitHub의 Actions 탭이 문법 오류를 알려주니 당황하지 마세요.

13.5 첫 워크플로우 만들기

저장소 루트에 .github/workflows/ 폴더를 만들고 그 안에 ci.yml을 만듭니다. 이 경로와 .yml 확장자는 약속이라 정확히 지켜야 GitHub가 인식합니다.

CODE
my-project/
└── .github/
    └── workflows/
        └── ci.yml      ← 여기!

가장 단순한 예제:

YAML
name: CI                          # 워크플로우 이름 (Actions 탭에 표시)

on:                               # 이벤트: 언제 실행?
  push:
    branches: [main]              # main에 push될 때
  pull_request:
    branches: [main]              # main으로 PR이 열릴 때

jobs:                             # 잡 정의
  test:                           # 'test'라는 이름의 잡
    runs-on: ubuntu-latest        # 우분투 가상 서버에서 실행
    steps:                        # 스텝들
      - name: Checkout code       # ① 저장소 코드 받아오기
        uses: actions/checkout@v4

      - name: Set up Node.js      # ② Node.js 설치
        uses: actions/setup-node@v4
        with:
          node-version: "20"
          cache: "npm"

      - name: Install dependencies # ③ 의존성 설치
        run: npm ci

      - name: Run tests            # ④ 테스트 실행
        run: npm test

이 파일을 커밋해서 push하면, GitHub의 Actions 탭에서 워크플로우가 자동 실행되는 것을 볼 수 있습니다. 초록 체크(✓)면 통과, 빨간 X면 실패입니다.

📌 핵심
잠깐, 이 예제에 나온 도구들
  • Node.js: 브라우저 밖에서도 JavaScript를 실행할 수 있게 해주는 런타임(실행 환경). 서버·도구 개발에 널리 쓰입니다.
  • npm(Node Package Manager): Node.js의 패키지(라이브러리) 설치·관리 도구. npm ci는 설정 파일에 적힌 의존성을 그대로 설치하는 명령입니다.
  • 위 예제는 Node.js 환경을 가정하지만, Python·Java 등 다른 언어도 같은 방식으로 각자의 도구를 써서 워크플로우를 구성합니다.

스텝의 두 종류: uses vs run

YAML
- uses: actions/checkout@v4    # 남이 만든 '액션'을 가져다 씀
- run: npm test                # 직접 셸 명령을 실행
  • uses: 재사용 가능한 액션을 호출 (마켓플레이스에 수천 개)
  • run: 평범한 터미널 명령을 그대로 실행
📌 핵심
actions/checkout은 거의 모든 워크플로우의 첫 스텝입니다. 러너(빈 가상 서버)에 내 저장소 코드를 복제해 오는 역할이라, 이게 없으면 코드가 없어 아무것도 못 합니다.

13.6 이벤트(트리거) 더 알아보기

on:에 들어갈 수 있는 대표적인 계기들입니다.

YAML
on:
  push:                          # 푸시할 때
    branches: [main, develop]
    paths-ignore: ['**.md']      # 문서만 바뀌면 실행 안 함

  pull_request:                  # PR 이벤트
    branches: [main]

  workflow_dispatch:             # 수동 실행 (Actions 탭의 버튼)

  schedule:                      # 정기 실행 (cron, UTC 기준)
    - cron: '0 2 * * 1-5'        # 평일 새벽 2시
이벤트언제
push브랜치에 커밋이 올라올 때
pull_requestPR이 열리거나 갱신될 때
workflow_dispatch사람이 버튼으로 수동 실행
schedule정해진 시각에 자동 (cron)
💡 팁
cron(크론)은 "언제 반복 실행할지"를 다섯 칸의 숫자로 표현하는 오래된 표준 표기법입니다. 분 시 일 월 요일 순서이며, *는 "매번"을 뜻합니다. 예: 0 2 * * 1-5 = "월~금요일 새벽 2시 0분마다". 외우기 어려우니 crontab.guru 같은 도구로 만들면 편합니다. (UTC는 세계 표준시로, 한국 시간보다 9시간 느립니다.)

13.7 시크릿(Secrets) — 비밀번호 안전하게 다루기

배포에는 API 키, 비밀번호 같은 민감 정보가 필요합니다. 이런 값을 절대 YAML 파일에 직접 적으면 안 됩니다. 코드에 적으면 저장소를 보는 모두에게 노출됩니다.

대신 GitHub의 Secrets 기능을 씁니다.

  1. 저장소 → Settings → Secrets and variables → Actions
  2. New repository secret으로 키 등록 (예: DEPLOY_API_KEY)
  3. 워크플로우에서 ${{ secrets.이름 }}으로 참조
YAML
- name: Deploy
  env:
    API_KEY: ${{ secrets.DEPLOY_API_KEY }}
  run: |
    curl -X POST https://your-api.com/deploy \
      -H "Authorization: Bearer $API_KEY"
💡 팁
GitHub Actions는 로그에서 시크릿 값을 자동으로 가립니다(masking). 그래도 시크릿을 직접 echo로 출력하는 일은 피하세요.
⚠️ 흔한 실수
3장 복습: .env 같은 비밀 파일은 반드시 .gitignore에 넣어 애초에 커밋되지 않게 해야 합니다. 한 번 push된 비밀번호는 이력에 영원히 남습니다.

13.8 컨텍스트 변수와 표현식

GitHub은 실행 중 유용한 정보를 자동으로 제공합니다.

YAML
- run: echo "브랜치는 ${{ github.ref }}"
- run: echo "커밋한 사람은 ${{ github.actor }}"

# 조건부 실행: main 브랜치일 때만
- name: Deploy to production
  if: github.ref == 'refs/heads/main'
  run: ./deploy.sh
변수의미
github.ref트리거된 브랜치/태그
github.actor작업을 실행시킨 사용자
github.sha커밋 해시
runner.os러너 운영체제

13.9 매트릭스 빌드 — 여러 환경에서 한 번에

같은 테스트를 여러 버전/OS에서 동시에 돌리고 싶을 때 matrix를 씁니다. 라이브러리 개발자에게 특히 유용합니다.

YAML
jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: [18, 20, 22]    # 세 버전 병렬 테스트
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}
      - run: npm ci
      - run: npm test

이 한 워크플로우가 Node 18, 20, 22에서 동시에 병렬로 실행됩니다.

13.10 잡 연결하기 — needs (파이프라인)

여러 잡을 순서대로 연결해 "검사 → 테스트 → 배포" 파이프라인을 만들 수 있습니다. needs는 "앞 잡이 성공해야 실행"을 뜻합니다.

YAML
jobs:
  quality:                    # 1단계: 코드 품질
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci
      - run: npm run lint

  test:                       # 2단계: 테스트 (quality 성공 후)
    needs: quality
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci
      - run: npm test

  deploy:                     # 3단계: 배포 (test 성공 후, main에서만)
    needs: test
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: ./deploy.sh
flowchart LR
    Q["🔍 quality<br/>품질검사<br/><small>lint</small>"]
    T["🧪 test<br/>테스트"]
    D["🚀 deploy<br/>배포<br/><small>main만</small>"]
    Q -- "성공" --> T
    T -- "성공" --> D
    classDef q fill:#fef3c7,stroke:#d97706,color:#92400e
    classDef t fill:#dbeafe,stroke:#2563eb,color:#1e40af
    classDef d fill:#dcfce7,stroke:#16a34a,color:#166534
    class Q q
    class T t
    class D d

각 단계가 실패하면 다음 단계는 실행되지 않습니다. 불량품이 배포까지 가지 않도록 막아주는 것입니다.

13.11 속도 올리기 — 캐싱

매번 의존성을 새로 내려받으면 느립니다. 캐시로 재사용해 속도를 높입니다.

YAML
- name: Cache dependencies
  uses: actions/cache@v4
  with:
    path: ~/.npm
    key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
    restore-keys: |
      ${{ runner.os }}-node-
💡 팁
hashFiles()가 lockfile로 캐시 키를 만들어, 의존성이 바뀔 때만 캐시를 새로 만들고 그 외엔 재사용합니다. (참고로 actions/setup-nodecache: "npm" 옵션이 이를 더 간단히 처리해주기도 합니다.) lockfile(잠금 파일, 예: package-lock.json)은 의존성의 정확한 버전을 못박아 기록한 파일로, 누가 설치하든 똑같은 버전이 깔리도록 보장합니다.

13.12 실무 팁과 베스트 프랙티스

  • 권한 최소화: 워크플로우에 permissions:를 명시해 GITHUB_TOKEN의 권한을 꼭 필요한 만큼만 부여.
  • 파일명은 명확하게: build-and-test.yml, deploy-prod.yml처럼 역할이 드러나게.
  • 시크릿은 반드시 Secrets로, 코드에 직접 쓰지 않기.
  • 빠른 피드백: PR마다 CI가 돌게 해서, 병합 전에 문제를 발견.
  • gh와 연계: 12장의 gh run list / gh run watch로 결과를 터미널에서 모니터링.
💡 팁
시작 팁: 직접 YAML을 처음부터 쓰기 부담되면, 저장소 Actions 탭 → New workflow에서 언어별 템플릿을 골라 시작하세요. GitHub이 actions/starter-workflows로 검증된 예제를 제공합니다.

13.13 이 장에서 배운 것 (요약)

  • CI/CD = 빌드·테스트·배포 자동화. GitHub Actions는 GitHub 내장 CI/CD
  • 구성: 워크플로우(YAML) → 이벤트(on) → 잡(jobs) → 스텝(steps), 러너에서 실행
  • 파일 위치는 .github/workflows/*.yml (약속)
  • 스텝은 uses(액션 호출) 또는 run(셸 명령), 첫 스텝은 보통 actions/checkout
  • 민감 정보는 Secrets로, ${{ secrets.이름 }} 참조 (코드에 직접 금지)
  • matrix로 다중 환경 병렬 테스트, needs로 잡 연결(파이프라인)
  • 캐싱으로 속도 향상, gh run으로 결과 모니터링

✍️ 확인 문제

  1. CI와 CD는 각각 무엇의 약자이며 무슨 일을 하나요?
  2. 워크플로우 파일은 어느 폴더에 어떤 확장자로 둬야 하나요?
  3. API 키를 워크플로우에서 안전하게 쓰려면 어떻게 해야 하나요?
축하합니다! 이제 Git 기초부터 GitHub 협업·자동화까지 익혔습니다. 부록의 치트시트로 정리하세요. → 99_부록-치트시트.md