13. CI/CD 입문 — GitHub Actions
- CI/CD가 무엇이고 왜 필요한지 이해한다
- GitHub Actions의 4가지 구성 요소(워크플로우/이벤트/잡/스텝)를 안다
- 첫 워크플로우 YAML 파일을 직접 작성한다
- 시크릿, 매트릭스, 배포까지 기본 패턴을 익힌다
13.1 CI/CD란 무엇인가?
지금까지는 코드를 push하면 그걸로 끝이었습니다. 하지만 실무에서는 push할 때마다 다음을 자동으로 하고 싶습니다.
- 테스트를 돌려 코드가 깨지지 않았는지 확인
- 코드 스타일(린트)을 검사
- 빌드가 되는지 확인
- 문제없으면 서버에 자동 배포
이렇게 반복적인 작업을 자동화하는 것이 CI/CD입니다.
| 약자 | 풀이 | 의미 |
|---|---|---|
| CI | Continuous Integration (지속적 통합) | 코드 변경마다 자동으로 빌드·테스트 |
| CD | Continuous 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가지만 알아둡시다.
- 들여쓰기로 계층을 표현 (스페이스만, 탭 금지). 들여쓰기가 곧 구조입니다.
key: value형태의 짝-로 시작하면 목록(리스트)의 항목
person: # 객체 name: Gildong # 문자열 age: 20 # 숫자 hobbies: # 리스트 - coding - reading
13.5 첫 워크플로우 만들기
저장소 루트에 .github/workflows/ 폴더를 만들고 그 안에 ci.yml을 만듭니다. 이 경로와 .yml 확장자는 약속이라 정확히 지켜야 GitHub가 인식합니다.
my-project/
└── .github/
└── workflows/
└── ci.yml ← 여기!
가장 단순한 예제:
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
- uses: actions/checkout@v4 # 남이 만든 '액션'을 가져다 씀 - run: npm test # 직접 셸 명령을 실행
uses: 재사용 가능한 액션을 호출 (마켓플레이스에 수천 개)run: 평범한 터미널 명령을 그대로 실행
actions/checkout은 거의 모든 워크플로우의 첫 스텝입니다. 러너(빈 가상 서버)에 내 저장소 코드를 복제해 오는 역할이라, 이게 없으면 코드가 없어 아무것도 못 합니다.13.6 이벤트(트리거) 더 알아보기
on:에 들어갈 수 있는 대표적인 계기들입니다.
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_request | PR이 열리거나 갱신될 때 |
workflow_dispatch | 사람이 버튼으로 수동 실행 |
schedule | 정해진 시각에 자동 (cron) |
분 시 일 월 요일 순서이며, *는 "매번"을 뜻합니다. 예: 0 2 * * 1-5 = "월~금요일 새벽 2시 0분마다". 외우기 어려우니 crontab.guru 같은 도구로 만들면 편합니다. (UTC는 세계 표준시로, 한국 시간보다 9시간 느립니다.)13.7 시크릿(Secrets) — 비밀번호 안전하게 다루기
배포에는 API 키, 비밀번호 같은 민감 정보가 필요합니다. 이런 값을 절대 YAML 파일에 직접 적으면 안 됩니다. 코드에 적으면 저장소를 보는 모두에게 노출됩니다.
대신 GitHub의 Secrets 기능을 씁니다.
- 저장소 → Settings → Secrets and variables → Actions
- New repository secret으로 키 등록 (예:
DEPLOY_API_KEY) - 워크플로우에서
${{ secrets.이름 }}으로 참조
- name: Deploy env: API_KEY: ${{ secrets.DEPLOY_API_KEY }} run: | curl -X POST https://your-api.com/deploy \ -H "Authorization: Bearer $API_KEY"
echo로 출력하는 일은 피하세요..env 같은 비밀 파일은 반드시 .gitignore에 넣어 애초에 커밋되지 않게 해야 합니다. 한 번 push된 비밀번호는 이력에 영원히 남습니다.13.8 컨텍스트 변수와 표현식
GitHub은 실행 중 유용한 정보를 자동으로 제공합니다.
- 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를 씁니다. 라이브러리 개발자에게 특히 유용합니다.
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는 "앞 잡이 성공해야 실행"을 뜻합니다.
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 속도 올리기 — 캐싱
매번 의존성을 새로 내려받으면 느립니다. 캐시로 재사용해 속도를 높입니다.
- 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-node의 cache: "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로 결과를 터미널에서 모니터링.
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으로 결과 모니터링
✍️ 확인 문제
- CI와 CD는 각각 무엇의 약자이며 무슨 일을 하나요?
- 워크플로우 파일은 어느 폴더에 어떤 확장자로 둬야 하나요?
- API 키를 워크플로우에서 안전하게 쓰려면 어떻게 해야 하나요?
축하합니다! 이제 Git 기초부터 GitHub 협업·자동화까지 익혔습니다. 부록의 치트시트로 정리하세요. → 99_부록-치트시트.md