Github Actions (CI/CD) 적용하기 (1) - Vercel Preview와 Production 배포 자동화

송태하·2026년 5월 25일

CI/CD

목록 보기
1/2
post-thumbnail

Github Actions : 빌드, 테스트 및 배포 파이프라인을 자동화할 수 있는 CI/CD 플랫폼
CI(Continuous Integration): 지속적인 통합
CD(Continuous Delivery/Deployment): 지속적인 제공/배포

GitHub Actions는 GitHub 사용자들에게 직관적이고 통합적인 CI/CD 환경을 제공하며, Github 페이지에서 Actions 탭을 통해 제공하기 때문에 GitHub Actions를 사용하면 코드 관리와 자동화를 한 플랫폼에서 수행할 수 있어, 팀 협업과 효율적인 소프트웨어 개발 프로세스를 구현할 수 있다.

주요 컴포넌트

GitHub Actions는 아래와 같은 주요 컴포넌트로 구성된다.

워크플로우(Workflow)

  • Github Actions에서 실행되는 자동화 작업의 집합
  • 레포지토리의. github/workflows YAML파일로 정의

이벤트(Event)

  • 워크플로우를 트리거하는 Github 이벤트
  • push, pull_request, schedule, workflow_dispatch 등이 있음

잡(Job)

  • 워크폴로우 내에서 실행 가능한 가장 작은 작업 단위
  • 각 잡은 독립적으로 실행되고, 필요하면 의존성을 가질 수 있음

단계(Step)

  • 잡 내부에서 순차적으로 실행되는 명령
  • 스크립트 명령어나 미리 정의된 액션으로 구성

액션(Action)

  • Github Actions에서 실행 가능한 재사용 가능한 작업단위.
  • 커스텀 액션을 작성하거나 마켓 플레이스에서 다운로드 가능

브랜치 전략

feat → develop → main

feat/~ 브랜치에서 기능 개발을 진행한 후 develop 브랜치로 Pull Request를 요청합니다.
빌드 및 테스트 안정성이 검증되면 develop에 병합되며, 최종적으로 main 브랜치에 통합되어 운영 환경에 배포되는 방식

목표

Next.js 프로젝트에 GitHub Actions 기반 CI/CD를 적용한다.

  • PR 또는 push 시 lint/test/build 검증
  • develop 브랜치는 Vercel Preview 배포
  • main 브랜치는 Vercel Production 배포

CI/CD 워크플로우 설계

이번 프로젝트에서는 CI와 CD를 역할별 파일로 완전히 분리하지 않고,
Preview와 Production 환경을 기준으로 워크플로우를 분리한다.

위의 브랜치 전략에 따라 develop 브랜치는 기능 검증을 위한 Preview 배포가 필요하고, main 브랜치는 실제 사용자에게 제공되는 Production 배포가 필요하기 때문이다.

따라서 .github/workflows/preview-cicd.yml.github/workflows/production-cicd.yml 두 개의 워크플로우를 만들고, 각 워크플로우 내부에서 검증 단계와 배포 단계를 job으로 나누었다.
검증 job이 성공해야만 배포 job이 실행되도록 needs를 사용했다.

Preview CI/CD 설계

vercel 공식문서에서 제공하는 preview 배포 workflows는 다음과 같다.

name: Vercel Preview Deployment #워크플로우
env:
  VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
  VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
on: #이벤트 main을 제외한 브랜치에서 push가 발생할 때 실행
  push:
    branches-ignore:
      - main
jobs: #잡
  Deploy-Preview:
    runs-on: ubuntu-latest
    steps: #스텝
      - uses: actions/checkout@v2 #액션 
      - name: Install Vercel CLI
        run: npm install --global vercel@latest
      - name: Pull Vercel Environment Information
        run: vercel pull --yes --environment=preview --token=${{ secrets.VERCEL_TOKEN }}
      - name: Build Project Artifacts
        run: vercel build --token=${{ secrets.VERCEL_TOKEN }}
      - name: Deploy Project Artifacts to Vercel
        run: vercel deploy --prebuilt --token=${{ secrets.VERCEL_TOKEN }}

위 workflow는 Vercel Preview 배포만 수행한다.

이번 프로젝트에서는 배포 전에 코드 안정성을 먼저 검증해야 하므로, Preview CI/CD를 다음 흐름으로 구성했다.

  • 의존성 설치
  • ESLint 검사
  • Vitest 단위 테스트
  • Next.js production build
  • Vercel Preview 배포
  • PR에 Preview URL 댓글 작성

단순히 배포만 하는 것이 아니라 lint, test, build가 모두 성공을 때만 Preview 배포가 실행되도록 구성했다.

Preview CI/CD 워크플로우 작성

먼저 .github/workflows/preview-cicd.yml 파일을 생성한다.
이 워크플로우는 develop 브랜치로 Pull Request가 생성되었을 때 실행된다.

name: Preview CI/CD

  on:
    pull_request:
      branches:
        - develop

name은 GitHub Actions 탭에 표시될 워크플로우 이름이다.
on은 워크플로우가 언제 실행될지 정의하는 부분이다.
위 설정은 develop 브랜치를 대상으로 하는 Pull Request가 생성되거나 업데이트될 때 Preview CI/CD 를 실행한다.

다음은 중복 실행 방지 부분.

concurrency:
    group: preview-cicd-${{ github.event.pull_request.number || github.ref }}
    cancel-in-progress: true

concurrency는 같은 Pull Request에서 워크플로우가 중복 실행되는 것을 방지하기 위해 사용했다.

예를 들어 PR에 여러 번 push하면 이전 실행이 끝나기 전에 새로운 실행이 시작될 수 있다.
이때 cancel-in-progress: true를 설정하면 이전 실행을 취소하고 가장 최근 커밋 기준으로 다시 실행한다.

권한 설정

permissions:
    contents: read

permissions는 GitHub Actions에서 사용할 권한을 설정하는 부분이다.

기본적으로 코드를 가져오기 위해 contents: read 권한만 부여했다.
PR 댓글을 작성하는 job에서는 별도로 댓글 작성 권한을 추가한다.

project-check job

첫 번째 job은 project-check이다.
이 job에서는 프로젝트를 배포하기 전에 lint, test, build를 실행해서 코드 안정성을 검증한다.

jobs:
    project-check:
      name: Lint, test, and build
      runs-on: ubuntu-latest
      env:
        NEXT_TELEMETRY_DISABLED: "1"
        NEXT_PUBLIC_SUPABASE_URL: ${{ secrets.NEXT_PUBLIC_SUPABASE_URL ||
  'https://example.supabase.co' }}
        SUPABASE_SERVICE_ROLE_KEY: ${{ secrets.SUPABASE_SERVICE_ROLE_KEY || 'ci-placeholder-
  service-role-key' }}

runs-on: ubuntu-latest는 이 job이 Ubuntu 환경의 GitHub Actions runner에서 실행된다는 의미다
env에는 빌드 과정에서 필요한 환경변수를 정의했다.
Next.js 빌드 과정에서 Supabase 환경변수를 참조하기 때문에, GitHub Secrets에 값이 있으면 해당 값을 사용하고 없으면 placeholder 값을 사용하도록 했다.

그리고 steps.

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: Lint
          run: npm run lint

        - name: Unit tests
          run: npm run test -- --run

        - name: Build
          run: npm run build

각 step은 순서대로 실행된다.

  • actions/checkout@v4: 레포지토리 코드를 runner로 가져온다.

  • actions/setup-node@v4: Node.js 20 환경을 설정한다.

  • cache: npm: npm 의존성 캐시를 사용해서 설치 시간을 줄인다.

  • npm ci: package-lock.json 기준으로 의존성을 설치한다.

  • npm run lint: ESLint 검사를 실행한다.

  • npm run test -- --run: Vitest 테스트를 한 번 실행하고 종료한다.

  • npm run build: Next.js production build가 가능한지 확인한다.

    이 단계 중 하나라도 실패하면 이후 배포 job은 실행되지 않는다.

deploy-to-vercel job

project-check job이 성공하면 다음으로 deploy-to-vercel job이 실행된다.

이 job에서는 Vercel CLI를 설치하고, Vercel Preview 환경 정보를 가져온 뒤, 프로젝트를 빌드하고 배포한다.

deploy-to-vercel:
      name: Deploy to Vercel Preview
      runs-on: ubuntu-latest
      needs: project-check
      outputs:
        preview_url: ${{ steps.deploy.outputs.preview_url }}
      env:
        NEXT_TELEMETRY_DISABLED: "1"
        VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
        VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}

여기서 가장 중요한 설정은 needs: project-check이다.

needs는 현재 job이 실행되기 전에 완료되어야 하는 job을 지정한다.

따라서 deploy-to-vercel job은 project-check job이 성공했을 때만 실행된다.
즉, lint, test, build 중 하나라도 실패하면 Preview 배포는 진행되지 않는다.

outputs는 이 job에서 생성한 값을 다른 job에서 사용할 수 있도록 내보내는 설정이다.

배포 job의 steps

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

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

        - name: Install Vercel CLI
          run: npm install --global vercel@latest

        - name: Pull Vercel environment
          run: vercel pull --yes --environment=preview --token=${{ secrets.VERCEL_TOKEN }}

        - name: Build with Vercel
          run: vercel build --token=${{ secrets.VERCEL_TOKEN }}

        - name: Deploy prebuilt output
          id: deploy
          run: |
            preview_url="$(vercel deploy --prebuilt --token=${{ secrets.VERCEL_TOKEN }})"
            echo "preview_url=$preview_url" >> "$GITHUB_OUTPUT"

배포 job의 step은 다음 순서로 실행된다.

  • Checkout: 배포할 프로젝트 코드를 가져온다.
  • Setup Node.js: Node.js 20 환경을 설정한다.
  • Install Vercel CLI: GitHub Actions 환경에서 Vercel 명령어를 사용하기 위해 Vercel CLI를 설치한다.
  • Pull Vercel environment: Vercel 프로젝트 설정과 Preview 환경변수를 가져온다.
  • Build with Vercel: Vercel 환경 기준으로 프로젝트를 빌드한다.
  • Deploy prebuilt output: 미리 빌드한 결과물을 Vercel Preview 환경에 배포한다.

preview-comment job

마지막 job은 preview-comment이다.
이 job은 Vercel Preview 배포가 완료된 뒤, 생성된 Preview URL을 Pull Request 댓글로 남기는 역할을 한다.

preview-comment:
      name: Comment preview URL
      runs-on: ubuntu-latest
      needs: deploy-to-vercel
      permissions:
        issues: write
        pull-requests: write

여기서도 needs를 사용해 preview-comment job은 deploy-to-vercel job이 성공해야만 실행된다.
또한 Pull Request에 댓글을 작성해야 하므로 권한을 별도로 설정했다.

steps.

steps:
    - name: Comment on pull request
      uses: actions/github-script@v7
      with:
        script: |
          const previewUrl = '${{ needs.deploy-to-vercel.outputs.preview_url }}'
          const currentTime = '${{ needs.deploy-to-vercel.outputs.current_time }}'

          await github.rest.issues.createComment({
            owner: context.repo.owner,
            repo: context.repo.repo,
            issue_number: context.issue.number,
            body: [
              `🧷 Preview: ${previewUrl}`,
              '',
              `⏰ Update: ${currentTime}`,
            ].join('\n'),
          })

댓글 작성에는 actions/github-script@v7 액션을 사용했다.

이 액션을 사용하면 별도의 라이브러리를 설치하지 않고도 GitHub API를 호출할 수 있다.

이렇게 Preview CI/CD 워크플로우를 구성하면, develop 브랜치로 Pull Request를 생성했을 때 다음 흐름으로 자동화된다.
1. 프로젝트 코드 checkout
2. Node.js 환경 설정
3. 의존성 설치
4. lint 실행
5. unit test 실행
6. production build 실행
7. Vercel Preview 배포
8. Pull Request 댓글로 Preview URL 공유

따라서 리뷰어는 로컬에서 프로젝트를 직접 실행하지 않아도, PR 댓글의 Preview URL을 통해 변경 사항을 바로 확인할 수 있다.

Production CI/CD 구축

Preview CI/CD와 마찬가지로 Production 배포도 배포 전에 프로젝트 검증 단계를 먼저 실행하도록 구성했다.

다만 Production 배포는 실제 사용자에게 제공되는 환경이므로 main 브랜치에 push 되었을 때만 실행되도록 했다.

name: Production CI/CD

  on:
    push:
      branches:
        - main

이후 흐름은 Preview CI/CD와 동일하다.

  1. 의존성 설치
  2. ESLint 검사
  3. Vitest 단위 테스트
  4. Next.js production build
  5. Vercel Production 배포

Vercel Production 배포에서는 Preview 배포와 다르게 --prod 옵션을 사용한다

- name: Pull Vercel environment
    run: vercel pull --yes --environment=production --token=${{ secrets.VERCEL_TOKEN }}

  - name: Build with Vercel
    run: vercel build --prod --token=${{ secrets.VERCEL_TOKEN }}

  - name: Deploy prebuilt output
    run: vercel deploy --prebuilt --prod --token=${{ secrets.VERCEL_TOKEN }}

--prod 옵션을 사용하면 Vercel의 Production 환경으로 배포된다.

마무리

이번 프로젝트에서는 GitHub Actions를 사용해 Next.js 프로젝트의 CI/CD 파이프라인을 구성했다.
develop 브랜치로 Pull Request가 생성되면 Preview CI/CD가 실행되고, lint, test, build 검증이 성공한 경우에만 Vercel Preview 배포가 진행된다.
배포가 완료되면 Pull Request 댓글에 Preview URL과 업데이트 시간이 자동으로 남는다.
main 브랜치에 코드가 반영되면 Production CI/CD가 실행되고, 동일한 검증 과정을 통과한 뒤 Vercel Production 환경에 배포된다.

처음 CI/CD를 설정할 때는 생각보다 신경 써야 할 부분이 많아서 시간이 꽤 걸렸다.
하지만 한 번 설정해두면 이후에는 PR을 올릴 때마다 자동으로 lint, test, build가 실행되고, Preview 배포까지 자동으로 생성된다.
초기 설정에는 시간이 걸리지만 이후 개발 과정에서는 반복 작업을 줄여주고, 배포 전 실수를 미리 발견할 수 있게 해준다는 점에서 충분히 가치 있는 작업이라고 느꼈다

0개의 댓글