포스트

[이제와서 시작하는 GitHub 마스터하기 - 고급편 #1-2] GitHub Actions: CI/CD 파이프라인 구축하기

[이제와서 시작하는 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 cipackage-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

설정 방법

  1. Repository → SettingsPages
  2. Source: GitHub Actions 선택
  3. 위 워크플로우 추가 후 Push
  4. 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'

시크릿 설정 방법

  1. Vercel에서 토큰 생성
  2. GitHub Repository → SettingsSecrets and variablesActions
  3. New repository secret 클릭
  4. 다음 시크릿 추가:
    • VERCEL_TOKEN
    • VERCEL_ORG_ID
    • VERCEL_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 → SettingsEnvironments에서 환경별 시크릿 설정 가능!

Q3: 배포 전에 수동 승인을 받으려면?

A: Environment에 Protection rules 설정!

  1. Settings → Environments → production
  2. Required reviewers 체크
  3. 승인자 지정

이제 프로덕션 배포 전에 승인 필요! ✅


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가지:

  1. 언어별 템플릿 활용 - 처음부터 만들지 말고 템플릿 복사
  2. 시크릿 활용 - API 키는 절대 코드에 쓰지 말기
  3. 캐싱 활용 - cache: 옵션 하나면 속도 2배 향상

다음 단계:

  • 본인 프로젝트에 CI/CD 파이프라인 추가
  • 배포 자동화로 시간 절약하기
  • 다음 편에서 고급 기능 배우기

📚 GitHub 마스터하기 시리즈

🚀 고급편 (전문가)

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

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

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