본문 바로가기
개발 팁

GitHub Actions 입문 — Push하면 자동으로 배포되는 CI/CD 세팅

by 루까(Luka) 2026. 3. 18.
반응형

왜 이 글을 쓰나

프로젝트를 GitHub에 올릴 때마다 손으로 빌드하고, 직접 배포 명령을 치고, 결과를 확인하는 과정을 반복하고 있었다. 실수도 잦았다. 빌드 전 커밋을 올리거나, 배포 명령을 빠뜨리거나.

GitHub Actions를 붙이고 나서는 git push 하나로 끝난다. 빌드, 테스트, 배포가 자동으로 돌아간다. 설정 파일 하나가 그 모든 걸 대체한다.

이 글에서는 GitHub Actions의 개념부터 실제 .github/workflows/deploy.yml 예시까지 정리한다. GitHub Pages 연동도 포함이다.


GitHub Actions 개념

GitHub Actions는 GitHub 저장소에 내장된 CI/CD 플랫폼이다. 별도 서비스 연동 없이 저장소 안에서 파이프라인을 구성할 수 있다.

핵심 용어를 먼저 정리한다:

용어 설명
Workflow 자동화 단위. .github/workflows/*.yml 파일로 정의한다
Event Workflow를 실행하는 트리거 (push, PR, schedule 등)
Job Workflow 안의 실행 단위. 병렬/순차 실행 가능
Step Job 안의 명령어 단위. shell 명령 또는 Action
Action 재사용 가능한 Step 묶음. GitHub Marketplace에서 가져다 쓴다
Runner Job이 실행되는 가상 머신 (ubuntu-latest, windows-latest 등)

구조를 도식으로 보면:

Workflow
└── Event (on: push)
    └── Job (build-and-deploy)
        ├── Step 1: checkout
        ├── Step 2: install
        ├── Step 3: build
        └── Step 4: deploy

실제 workflow yaml — GitHub Pages 자동 배포

React 프로젝트를 GitHub Pages에 자동으로 배포하는 완전한 예시다.

# .github/workflows/deploy.yml

name: Deploy to GitHub Pages

on:
  push:
    branches:
      - main  # main 브랜치에 push될 때만 실행

permissions:
  contents: read
  pages: write
  id-token: write

concurrency:
  group: "pages"
  cancel-in-progress: false

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: "20"
          cache: "npm"

      - name: Install dependencies
        run: npm ci

      - name: Build
        run: npm run build
        env:
          VITE_API_URL: ${{ secrets.VITE_API_URL }}

      - name: Upload artifact
        uses: actions/upload-pages-artifact@v3
        with:
          path: "./dist"

  deploy:
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}
    runs-on: ubuntu-latest
    needs: build

    steps:
      - name: Deploy to GitHub Pages
        id: deployment
        uses: actions/deploy-pages@v4

주요 포인트를 설명한다:

on: push: branches: [main] — main 브랜치 push 시에만 실행된다. feature 브랜치에서 작업하면 워크플로가 돌지 않는다.

permissions — GitHub Pages 배포에는 pages: writeid-token: write 권한이 필요하다. 없으면 배포 단계에서 권한 오류가 난다.

npm cinpm install 대신 npm ci를 쓴다. package-lock.json을 기준으로 정확히 설치하고, 속도도 빠르다. CI 환경에서는 항상 ci를 쓰는 것이 맞다.

cache: "npm"node_modules를 캐시한다. 처음 실행 이후에는 설치 시간이 크게 줄어든다.

needs: builddeploy Job이 build Job 완료 후에만 실행된다. 순서 보장이다.

secrets.VITE_API_URL — 환경변수는 저장소 Settings → Secrets에 등록하고 참조한다. 하드코딩하면 보안 문제가 생긴다.


주요 트리거 패턴

트리거는 목적에 맞게 골라야 한다.

트리거 yaml 예시 언제 쓰나
push (특정 브랜치) on: push: branches: [main] 메인 브랜치 배포 자동화
pull_request on: pull_request: branches: [main] PR 열릴 때 테스트 실행
schedule on: schedule: - cron: '0 9 * * 1' 정기 작업 (매주 월요일 오전 9시)
workflow_dispatch on: workflow_dispatch 수동 실행 버튼 추가
push (특정 경로) on: push: paths: ['src/**'] 특정 디렉토리 변경 시만 실행

workflow_dispatch는 특히 유용하다. GitHub Actions 탭에서 직접 실행 버튼이 생겨서, 자동 트리거 없이도 수동으로 워크플로를 돌릴 수 있다.


PR 열릴 때 테스트만 도는 예시

배포와 별개로 PR 리뷰 시 테스트를 자동으로 실행하는 워크플로다.

# .github/workflows/test.yml

name: Run Tests on PR

on:
  pull_request:
    branches:
      - main

jobs:
  test:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: "20"
          cache: "npm"

      - name: Install
        run: npm ci

      - name: Lint
        run: npm run lint

      - name: Test
        run: npm test -- --coverage

이렇게 하면 PR을 열 때마다 린트와 테스트가 돌고, GitHub UI에서 통과/실패 여부가 바로 보인다. 머지 전 체크 수단으로 쓴다.


GitHub Actions vs 타 CI/CD 비교

  GitHub Actions Jenkins CircleCI Vercel
설정 위치 저장소 내 yaml 별도 서버 외부 서비스 플랫폼 내장
무료 한도 공개: 무제한 / 비공개: 2,000분/월 자체 서버 비용 6,000분/월 무료 플랜 있음
설치 필요 없음 서버 필요 없음 없음
커스텀 환경 Runner 자유 설정 높음 중간 제한적
Marketplace GitHub Marketplace 플러그인 생태계 Orbs 없음
학습 곡선 낮음 높음 중간 매우 낮음

GitHub을 쓰고 있다면 Actions가 가장 자연스러운 선택이다. 저장소와 같은 곳에 설정 파일이 있고, PR/이슈/릴리즈 이벤트와 직접 연동된다.


GitHub Pages 설정 주의사항

yaml만 작성한다고 배포되지 않는다. 저장소 설정도 맞춰야 한다.

1. Pages 소스를 "GitHub Actions"로 변경한다

저장소 → Settings → Pages → Source를 Deploy from a branch가 아닌 GitHub Actions로 바꾼다. 이걸 안 하면 deploy job이 권한 오류로 실패한다.

2. Vite 프로젝트는 base 설정이 필요하다

https://username.github.io/repo-name/ 경로로 배포될 경우, vite.config.js에 base를 설정해야 정적 파일 경로가 맞다:

// vite.config.js
export default {
  base: '/repo-name/',
}

3. SPA 라우팅 이슈

React Router 같은 클라이언트 사이드 라우팅을 쓰면 새로고침 시 404가 난다. 404.htmlindex.html로 복사하는 우회책이 있다:

- name: Copy index.html to 404.html for SPA routing
  run: cp dist/index.html dist/404.html

삽질 기록

권한 오류로 deploy가 계속 실패했다

permissions 블록을 빠뜨렸던 게 원인이었다. Actions 탭에서 보면 "Resource not accessible by integration"이라는 메시지가 뜬다. pages: writeid-token: write를 추가하면 해결된다.

npm install로 했더니 lock 파일이 바뀌는 문제

CI 환경에서 npm install을 쓰면 package-lock.json이 업데이트되면서 커밋되지 않은 변경이 생기는 경우가 있다. npm ci는 lock 파일을 수정하지 않는다. CI에서는 ci 명령을 써야 한다.

캐시가 쌓이면서 구버전 의존성이 남았다

cache: "npm"을 켜두면 node_modules를 재사용하는데, 가끔 낡은 캐시가 남아서 이상하게 동작했다. Actions 탭 → Caches에서 수동으로 삭제하거나, cache key에 날짜나 파일 해시를 포함해서 강제로 갱신할 수 있다.

환경변수를 yaml에 직접 써서 보안 경고가 떴다

API 키를 env: KEY: "actual-key"로 하드코딩했다가 GitHub의 secret scanning에 걸렸다. Secrets에 등록하고 ${{ secrets.KEY }}로 참조해야 한다.


마무리

GitHub Actions는 진입 장벽이 낮은 편이다. yaml 파일 하나, 저장소 설정 한 곳만 바꾸면 push 이후의 빌드/배포가 자동으로 돌아간다.

처음에는 deploy.yml 하나로 시작하는 걸 권한다. 배포가 안정되면 test.yml을 따로 분리하고, 나중에 환경별 워크플로(staging, production)를 추가하면 된다. 복잡한 구성보다 작동하는 단순한 것이 먼저다.


기술 스택: GitHub Actions, Node.js 20, Vite, GitHub Pages
참고: GitHub Actions 공식 문서 | GitHub Marketplace

반응형