[이제와서 시작하는 GitHub 마스터하기 - 고급편 #1-2] GitHub Actions: CI/CD 파이프라인 구축하기
들어가며
이전 편에서 GitHub Actions의 기본 개념과 첫 워크플로우를 만들어봤습니다. 이번 편에서는 본격적으로 CI/CD 파이프라인을 구축하는 방법을 배워보겠습니다.
💡 이 글은 누구를 위한 글인가요?
- [고급편 #1]을 읽고 기본 워크플로우를 만들어본 분
- 본인 프로젝트에 자동 테스트와 배포를 적용하고 싶은 분
- 언어별(Python, Java, Go 등) CI 설정 방법을 알고 싶은 분
1. CI/CD 파이프라인이란? (⭐ 초보자 필수)
한 줄 정의
- CI (Continuous Integration): 코드 변경 시 자동으로 테스트
- CD (Continuous Deployment): 테스트 통과 시 자동으로 배포
실생활 비유
CI/CD 없이:
1
2
3
개발자: 코드 작성 → 수동 테스트 → 빌드 → 서버 접속 → 배포
↓ (실수 발생!)
테스트 깜빡함 → 버그가 프로덕션에 배포됨 😱
CI/CD 사용:
1
2
3
4
5
개발자: 코드 작성 → Push
↓
GitHub Actions: 자동 테스트 → 자동 빌드 → 자동 배포 ✨
↓
테스트 실패하면 배포 안 됨 (안전!) ✅
CI/CD 파이프라인 흐름
graph LR
A[코드 작성] --> B[Git Push]
B --> C{CI: 자동 테스트}
C -->|성공| D[빌드]
C -->|실패| E[알림: 수정 필요]
D --> F{CD: 자동 배포}
F -->|main 브랜치| G[프로덕션]
F -->|develop 브랜치| H[스테이징]
F -->|PR| I[미리보기]
style C fill:#bbf,stroke:#333,stroke-width:2px
style F fill:#fbf,stroke:#333,stroke-width:2px
style G fill:#9f9,stroke:#333,stroke-width:2px
2. 언어별 CI 템플릿 (⭐ 초보자 필수)
Node.js / TypeScript 프로젝트
목표: 코드 푸시 시 자동으로 린트 → 타입 체크 → 테스트 → 빌드
.github/workflows/node-ci.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
name: Node.js CI
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Node.js 설정
uses: actions/setup-node@v4
with:
node-version: '20.x'
cache: 'npm' # npm 캐시 활성화
- name: 의존성 설치
run: npm ci
- name: 코드 포맷 검사
run: npm run lint
- name: 타입 체크
run: npm run type-check
- name: 단위 테스트
run: npm test
- name: 빌드
run: npm run build
npm ci vs npm install
| 명령어 | 속도 | 용도 | CI에서 권장? |
|---|---|---|---|
npm ci | 빠름 | CI 환경 | ✅ 권장 |
npm install | 느림 | 개발 환경 | ❌ |
💡 팁:
npm ci는package-lock.json을 그대로 사용하여 정확히 같은 버전을 설치합니다.
Python 프로젝트
목표: Poetry를 사용한 Python 프로젝트 테스트
.github/workflows/python-ci.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
42
43
44
name: Python CI
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Python 설정
uses: actions/setup-python@v4
with:
python-version: '3.11'
- name: Poetry 설치
uses: snok/install-poetry@v1
with:
version: 1.7.1
virtualenvs-create: true
virtualenvs-in-project: true
- name: 의존성 캐시
uses: actions/cache@v3
with:
path: .venv
key: venv-${{ runner.os }}-${{ hashFiles('**/poetry.lock') }}
- name: 의존성 설치
run: poetry install --no-interaction
- name: 린트 (ruff)
run: poetry run ruff check src/
- name: 타입 체크 (mypy)
run: poetry run mypy src/
- name: 테스트 (pytest)
run: poetry run pytest tests/ --cov=src
📚 pip 사용 시 설정 (클릭하여 펼치기)
1
2
3
4
5
6
7
8
9
10
- name: 의존성 캐시
uses: actions/cache@v3
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
- name: 의존성 설치
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
Java / Spring Boot 프로젝트
목표: Maven으로 Java 프로젝트 빌드 및 테스트
.github/workflows/java-ci.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
name: Java CI with Maven
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: JDK 17 설정
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'
cache: maven
- name: Maven 빌드
run: ./mvnw clean install
- name: 테스트
run: ./mvnw test
- name: JAR 파일 업로드
uses: actions/upload-artifact@v3
with:
name: app-jar
path: target/*.jar
📚 Gradle 사용 시 설정 (클릭하여 펼치기)
1
2
3
4
5
- name: Gradle 빌드
run: ./gradlew build
- name: 테스트
run: ./gradlew test
Go 프로젝트
목표: Go 프로젝트 테스트 및 빌드
.github/workflows/go-ci.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
name: Go CI
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Go 설정
uses: actions/setup-go@v4
with:
go-version: '1.21'
cache: true
- name: 의존성 다운로드
run: go mod download
- name: 린트
run: go vet ./...
- name: 테스트
run: go test -v ./...
- name: 빌드
run: go build -v ./...
3. CD: 자동 배포 설정 (⭐ 초보자 필수)
GitHub Pages 배포 (가장 쉬운 배포)
사용 예시: 정적 사이트, 문서, React/Vue 앱
.github/workflows/deploy-pages.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
42
43
44
45
name: Deploy to GitHub Pages
on:
push:
branches: [ main ]
# 권한 설정 (중요!)
permissions:
contents: read
pages: write
id-token: write
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Node.js 설정
uses: actions/setup-node@v4
with:
node-version: '20.x'
cache: 'npm'
- name: 의존성 설치
run: npm ci
- name: 빌드
run: npm run build
- name: GitHub Pages 업로드
uses: actions/upload-pages-artifact@v2
with:
path: ./dist
deploy:
environment:
name: github-pages
url: $
runs-on: ubuntu-latest
needs: build
steps:
- name: GitHub Pages 배포
id: deployment
uses: actions/deploy-pages@v2
설정 방법
- Repository → Settings → Pages
- Source: GitHub Actions 선택
- 위 워크플로우 추가 후 Push
https://your-username.github.io/repo-name/에서 확인
Vercel 배포 (Next.js 추천)
사용 예시: Next.js, React, Vue 앱
.github/workflows/deploy-vercel.yml:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
name: Deploy to Vercel
on:
push:
branches: [ main ]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Vercel 배포
uses: amondnet/vercel-action@v25
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
vercel-args: '--prod'
시크릿 설정 방법
- Vercel에서 토큰 생성
- GitHub Repository → Settings → Secrets and variables → Actions
- New repository secret 클릭
- 다음 시크릿 추가:
VERCEL_TOKENVERCEL_ORG_IDVERCEL_PROJECT_ID
📚 시크릿이 뭔가요? (클릭하여 펼치기)
시크릿 (Secret) = 비밀 정보 (API 키, 토큰, 비밀번호 등)
- 코드에 직접 쓰면 안 됨! (보안 위험 🔓)
- GitHub Secrets에 저장하면 안전하게 사용 🔒
- 로그에서 자동으로 가려짐 (마스킹)
사용 방법:
1
2
env:
API_KEY: ${{ secrets.MY_API_KEY }}
AWS S3 + CloudFront 배포
사용 예시: 정적 웹사이트, React/Vue 빌드 결과
.github/workflows/deploy-aws.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
name: Deploy to AWS S3
on:
push:
branches: [ main ]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Node.js 설정
uses: actions/setup-node@v4
with:
node-version: '20.x'
- name: 빌드
run: |
npm ci
npm run build
- name: AWS 자격 증명 설정
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-east-1
- name: S3에 업로드
run: |
aws s3 sync dist/ s3://${{ secrets.S3_BUCKET }} --delete
- name: CloudFront 캐시 무효화
run: |
aws cloudfront create-invalidation \
--distribution-id ${{ secrets.CLOUDFRONT_ID }} \
--paths "/*"
4. 환경 변수와 시크릿 (⭐ 초보자 필수)
환경 변수 사용
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
env:
# 워크플로우 전체에서 사용
NODE_ENV: production
API_URL: https://api.example.com
jobs:
build:
runs-on: ubuntu-latest
env:
# 이 Job에서만 사용
BUILD_ENV: staging
steps:
- name: 환경 변수 출력
env:
# 이 Step에서만 사용
STEP_VAR: hello
run: |
echo "NODE_ENV: $NODE_ENV"
echo "BUILD_ENV: $BUILD_ENV"
echo "STEP_VAR: $STEP_VAR"
동적 환경 변수
1
2
3
4
5
6
steps:
- name: 빌드 시간 저장
run: echo "BUILD_TIME=$(date +%s)" >> $GITHUB_ENV
- name: 빌드 시간 사용
run: echo "Built at: $BUILD_TIME"
5. 캐싱으로 속도 올리기 (⭐ 초보자 필수)
왜 캐싱이 필요한가요?
캐싱 전:
1
2
3
매번 의존성 다운로드: 2분
테스트 실행: 1분
총 시간: 3분
캐싱 후:
1
2
3
캐시에서 의존성 복원: 10초
테스트 실행: 1분
총 시간: 1분 10초 (약 60% 단축!)
Node.js 캐싱
1
2
3
4
5
- name: Node.js 설정 (캐싱 포함)
uses: actions/setup-node@v4
with:
node-version: '20.x'
cache: 'npm' # 자동 캐싱!
이것만으로 자동 캐싱 완료! ✨
수동 캐싱 (고급)
1
2
3
4
5
6
7
- name: 의존성 캐시
uses: actions/cache@v3
with:
path: ~/.npm
key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-npm-
📚 캐시 키 설명 (클릭하여 펼치기)
Cache Key 구조:
1
2
3
${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
↓ ↓ ↓
운영체제 용도 파일 해시 (변경 감지)
package-lock.json이 변경되면 새 캐시 생성- 변경되지 않으면 캐시 재사용
6. 아티팩트: 빌드 결과 저장 (⭐ 초보자 필수)
아티팩트란?
아티팩트 = 빌드 결과물 (JAR, Docker 이미지, 배포 파일 등)
사용 예시
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:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: 빌드
run: npm run build
- name: 빌드 결과 업로드
uses: actions/upload-artifact@v3
with:
name: build-files
path: dist/
retention-days: 7 # 7일 후 자동 삭제
deploy:
needs: build
runs-on: ubuntu-latest
steps:
- name: 빌드 결과 다운로드
uses: actions/download-artifact@v3
with:
name: build-files
path: dist/
- name: 배포
run: |
ls -la dist/
# 배포 명령어
7. 실습 과제
📝 초보자 과제 (필수)
- 본인이 사용하는 언어로 CI 워크플로우 만들기
- GitHub Pages에 정적 사이트 배포해보기
- 캐싱 추가하여 실행 시간 단축하기
- 빌드 결과를 아티팩트로 저장해보기
🚀 중급자 과제 (도전)
- 프로덕션/스테이징 환경 분리하기
- 테스트 커버리지 리포트 생성하기
- Docker 이미지 빌드 및 배포하기
- Slack/Discord 알림 추가하기
8. 자주 묻는 질문 (FAQ)
Q1: 시크릿을 로컬에서 테스트하려면?
A: act 도구를 사용하면 로컬에서 GitHub Actions를 실행할 수 있습니다.
1
2
3
4
5
# act 설치 (macOS)
brew install act
# 워크플로우 실행
act -s MY_SECRET=value
Q2: 여러 환경 (dev/staging/prod)으로 배포하려면?
A: GitHub Environments를 사용하세요!
1
2
3
4
5
6
7
8
jobs:
deploy-production:
environment:
name: production
url: https://prod.example.com
steps:
- name: 프로덕션 배포
run: echo "Deploying to production"
Repository → Settings → Environments에서 환경별 시크릿 설정 가능!
Q3: 배포 전에 수동 승인을 받으려면?
A: Environment에 Protection rules 설정!
- Settings → Environments → production
- Required reviewers 체크
- 승인자 지정
이제 프로덕션 배포 전에 승인 필요! ✅
9. 다음 단계
✅ 이번 편에서 배운 것
- 언어별 CI 템플릿 (Node.js, Python, Java, Go)
- 다양한 플랫폼에 자동 배포 (GitHub Pages, Vercel, AWS)
- 환경 변수와 시크릿 관리
- 캐싱과 아티팩트로 속도 최적화
📚 다음 편 예고
[고급편 #1-3] GitHub Actions: 고급 기능과 최적화에서는:
- 재사용 가능한 워크플로우 (Reusable Workflows)
- 매트릭스 전략 (여러 버전 동시 테스트)
- 조건부 실행과 제어 흐름
- 2025년 최신 기능 (GPU Runners, Copilot 통합)
- 비용 최적화 전략
10. 정리
CI/CD는 더 이상 선택이 아닌 필수입니다! GitHub Actions로 쉽게 시작할 수 있습니다.
초보자가 기억할 핵심 3가지:
- 언어별 템플릿 활용 - 처음부터 만들지 말고 템플릿 복사
- 시크릿 활용 - API 키는 절대 코드에 쓰지 말기
- 캐싱 활용 -
cache:옵션 하나면 속도 2배 향상
다음 단계:
- 본인 프로젝트에 CI/CD 파이프라인 추가
- 배포 자동화로 시간 절약하기
- 다음 편에서 고급 기능 배우기
📚 GitHub 마스터하기 시리즈
🚀 고급편 (전문가)
- GitHub Actions 입문 ⬅️ 이전 편
- [GitHub Actions: CI/CD 파이프라인] (현재 글)
- GitHub Actions: 고급 기능 ⬅️ 다음 편
- Webhooks와 API
- GitHub Apps 개발
- 보안 기능
- GitHub Packages
- Codespaces
- GitHub CLI
- 통계와 인사이트
“늦었다고 생각할 때가 가장 빠른 때입니다. 지금 시작하세요!” 🚀