매번 vercel —prod를 입력하는 것이 번거롭지 않으세요?
이전 글 우리는 Vercel로 간다! 프론트엔드 배포 가이드에서 Vercel CLI를 사용한 수동 배포 방법을 다뤘습니다.
코드를 수정하고 배포할 때마다:
vercel # Preview 배포
vercel --prod # Production 배포bash이 명령어들을 반복해서 입력해야 했죠.
프로젝트 초반에는 괜찮지만, 팀 프로젝트에서 여러 명이 배포하거나 하루에도 몇 번씩 배포해야 한다면 어떨까요?
매번 터미널을 열고 명령어를 입력하는 것이 점점 번거로워집니다.
더 큰 문제는 배포 전 검증이에요. 린트 검사나 테스트를 깜빡하고 바로 배포하면, 에러가 있는 코드가 프로덕션에 올라갈 수 있습니다.
실제로 이런 실수는 생각보다 자주 발생해요! 😱
GitHub에 푸시만 하면 자동으로 배포되면 얼마나 편할까요?
이제 여러분과 함께 코드를 GitHub에 푸시하기만 하면 자동으로 배포되는 CI/CD 파이프라인을 구축해보겠습니다.
CI/CD란 무엇일까요?
CI (Continuous Integration, 지속적 통합): 코드 변경사항을 자동으로 빌드하고 테스트하는 프로세스입니다CD (Continuous Deployment, 지속적 배포): 테스트를 통과한 코드를 자동으로 프로덕션에 배포하는 프로세스입니다
GitHub Actions CI/CD 파이프라인을 구축하면 이런 장점이 있어요:
- 자동 검증: 린트, 타입 체크, 테스트가 자동으로 실행됩니다
- 자동 배포: main 브랜치에 머지하면 자동으로 프로덕션에 배포됩니다
- Preview 배포: Pull Request를 만들면 자동으로 미리보기 환경이 생성됩니다
- 배포 실패 방지: 테스트가 실패하면 배포가 중단되어 에러가 있는 코드가 배포되는 것을 막아줍니다
더 이상 vercel --prod 명령어를 입력할 필요가 없어요. GitHub에 코드를 푸시하기만 하면 됩니다! ✨
Vercel의 두 가지 배포 방식
Vercel에서 GitHub 레포지토리를 배포하는 방법은 크게 두 가지예요.
Vercel Git Integration (기본 방식)
Vercel 대시보드에서 GitHub 레포지토리를 직접 연결하는 방식입니다.
설정 방법:
- Vercel 대시보드에서
New Project클릭 - 배포하고 싶은 프로젝트의 GitHub 레포지토리 선택 (Github 계정과 Vercel이 연동되어 있어야 합니다..!)
- 빌드 설정 확인 후
Deploy클릭
정말 간단하죠? 클릭 몇 번이면 설정이 완료됩니다.
위 화면은 Vercel에서 프로젝트를 생성할 때 표시되는 설정 화면이에요.
프레임워크(React, Next.js, Nuxt.js 등)를 자동 감지하고 빌드 명령어를 제안해줍니다.
장점:
- 설정이 매우 간단해요 (클릭 몇 번이면 끝!)
- Vercel이 빌드 환경을 자동으로 제공합니다
- PR마다 자동으로 Preview 배포가 생성돼요
- 배포 상태가 GitHub PR에 자동으로 코멘트됩니다
단점:
- Vercel 서버에서 빌드가 진행됩니다 (소스 코드가 Vercel 서버로 전송됨)
- 커스텀 빌드 스텝을 추가하기 어려워요
- 린트, 테스트 등 추가 검증 단계가 제한적입니다
- GitHub Enterprise Server는 지원하지 않아요
GitHub Actions (커스텀 CI/CD)
GitHub Actions 워크플로우에서 직접 빌드한 후 Vercel에 배포하는 방식입니다.
장점:
- 빌드를 GitHub에서 진행: Vercel에 소스 코드를 노출하지 않고 빌드된 결과물만 업로드해요
- 린트, 테스트, 보안 스캔: 원하는 검증 단계를 자유롭게 추가할 수 있습니다
- 빌드 캐싱, 병렬 처리: 빌드 최적화가 가능해요
- GitHub Enterprise Server: 온프레미스 환경에서도 사용 가능합니다
단점:
- 초기 설정이 복잡해요 (워크플로우 파일 작성 필요)
- GitHub Actions 빌드 시간을 사용합니다 (무료 플랜은 월 2,000분 제한)
어떤 방식을 선택해야 할까요?
Vercel Git Integration을 추천하는 경우:
- 빠른 프로토타입을 만들 때
- 소규모 프로젝트 (1~2명)
- 빠르게 배포 환경을 구축하고 싶을 때
GitHub Actions를 추천하는 경우:
- 엔터프라이즈 프로젝트 (보안이 중요한 경우)
- 커스텀 빌드 스텝이 많은 경우 (E2E 테스트, 보안 스캔 등)
- 팀 프로젝트 (여러 명이 함께 작업)
- GitHub Enterprise Server를 사용하는 경우
비교 표:
| 항목 | Vercel Git Integration | GitHub Actions |
|---|---|---|
| 설정 난이도 | 매우 쉬움 (클릭 몇 번) | 중간 (YAML 작성) |
| 소스 코드 보안 | Vercel 서버에 노출 | GitHub에서만 빌드 |
| 커스텀 빌드 스텝 | 제한적 | 자유롭게 추가 |
| 린트/테스트 통합 | 어려움 | 쉬움 |
| GitHub Enterprise Server | 지원 안 함 | 지원 |
| 추천 대상 | 빠른 프로토타입, 소규모 프로젝트 | 엔터프라이즈, 커스텀 요구사항 많은 프로젝트 |
이 글에서는 GitHub Actions를 사용한 CI/CD 파이프라인 구축 방법을 다뤄볼게요!
왜 pnpm인가요?
본격적인 설정에 앞서 이 글에서 사용할 패키지 매니저인 pnpm에 대해 간단히 알아볼게요.
pnpm은 npm, yarn과 함께 가장 널리 사용되는 패키지 매니저입니다. 다른 패키지 매니저와 비교해서 다음과 같은 장점이 있어요:
- 디스크 공간 절약: 하드 링크를 사용해서 중복 패키지를 저장하지 않습니다
- 빠른 설치 속도: 병렬 설치와 캐싱으로 설치 속도가 빨라요
- 엄격한 의존성 관리: 명시적으로 선언한 패키지만 접근 가능합니다
프로젝트에 pnpm이 설정되어 있지 않다면 다음과 같이 설치할 수 있어요:
# pnpm 설치
npm install -g pnpm
# 기존 프로젝트에서 pnpm으로 전환
pnpm import # package-lock.json 또는 yarn.lock에서 변환
pnpm install # 의존성 설치 및 pnpm-lock.yaml 생성bash이미 npm이나 yarn을 사용하고 있다면 pnpm import 명령어로 기존 lock 파일을 변환할 수 있습니다.
GitHub Actions 설정을 위한 사전 준비
GitHub Actions에서 Vercel에 배포하려면 세 가지 정보가 필요해요.
🔑 Vercel 토큰 발급
Vercel CLI나 GitHub Actions에서 Vercel API를 사용하려면 인증 토큰이 필요합니다.
발급 방법:
- Vercel 대시보드에서
Settings→Tokens이동 Create Token클릭- 원하는 토큰 이름 입력 (예:
GitHub Actions) - Scope는
Full Account,깃허브이름's projects선택 (팀레포라면, 팀의 프로젝트 등) - 만료 기간은 원하는 기간으로 설정 (예:
30 days)
- 만료됐을때는 새로 생성해야 합니다.
- 저는 No Expiration을 선택했습니다.
- 생성된 토큰 복사
⚠️ 주의: 토큰은 한 번만 표시되므로 바로 복사하고, 저장해두세요! 분실하면 새로 생성해야 합니다.
위 화면에서 토큰 이름과 만료 기간을 설정할 수 있어요. Full Account 권한을 선택해야 배포가 가능합니다.
📋 프로젝트 ID와 Organization ID 확인
GitHub Actions에서 어떤 Vercel 프로젝트에 배포할지 지정하려면 두 가지 ID가 필요해요.
Vercel CLI로 확인하기:
# Vercel CLI 설치 (없다면)
pnpm add -g vercel@latest
# Vercel 로그인
vercel login
# 작업하고 있는 프로젝트 디렉토리로 이동
cd /path/to/your/project
# 작업 중인 프로젝트 디렉토리 이동 후
# Vercel 프로젝트 연결 (처음 한 번만)
# 이전 글에서 이미 배포했다면 이미 연결되어 있을 수 있어요
vercel link ✔ 6s 20:20:03
Vercel CLI 50.9.0
? Set up “~/Desktop/ci-cd-vercel”? yes
? Which scope should contain your project? dydals3440's projects
? Found project “dydals3440s-projects/ci-cd-vercel”. Link to it? yes
✅ Linked to dydals3440s-projects/ci-cd-vercel (created .vercel and added it to .gitignore)
? Would you like to pull environment variables now? no
# .vercel/project.json 파일 생성 확인
cat .vercel/project.jsonbash출력 예시:
{
"projectId": "prj_xxxxxxxxxxxxxxxxxxxx",
"orgId": "team_xxxxxxxxxxxxxxxxxxxx"
}json.vercel/ 디렉토리는 로컬 설정 파일이 담겨있으므로 .gitignore에 추가되어 있어야 해요. (자동으로 해줘요!)
대부분의 프레임워크 템플릿에는 이미 포함되어 있습니다.
# .gitignore
.vercel/🔐 GitHub Secrets에 등록
위에서 준비한 3개 값을 GitHub 레포지토리 Secrets에 등록합니다.
등록 방법:
- GitHub 레포지토리에서
Settings→Secrets and variables→Actions이동 New repository secret클릭하여 다음 3개 추가:VERCEL_TOKEN: Vercel 토큰VERCEL_ORG_ID: Organization ID (orgId)VERCEL_PROJECT_ID: Project ID (projectId)
위 화면에서 New repository secret 버튼을 클릭하면 이름과 값을 입력할 수 있어요. 등록된 시크릿은 워크플로우에서 ${{ secrets.VERCEL_TOKEN }} 형태로 참조합니다.
왜 GitHub Secrets를 사용하나요?
GitHub Secrets는 암호화된 환경변수예요. 워크플로우 파일에 직접 토큰을 적으면 GitHub에 공개되어 보안 문제가 발생할 수 있습니다.
Secrets에 저장하면 암호화되어 저장되고, 워크플로우 실행 시에만 복호화되어 사용돼요.
🚀 Preview 배포 자동화 (Pull Request)
PR을 생성하거나 업데이트할 때마다 자동으로 Preview 환경에 배포되도록 설정해볼게요.
워크플로우 파일 생성
프로젝트 루트에 .github/workflows/preview.yml 파일을 생성합니다.
name: Vercel Preview Deployment
env:
VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
on:
push:
branches-ignore:
- main # main 브랜치 외 모든 브랜치에서 실행
jobs:
Deploy-Preview:
runs-on: ubuntu-latest
steps:
# 1. 코드 체크아웃
- name: Checkout code
uses: actions/checkout@v4
# 2. pnpm 설정
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 10
# 3. Node.js 설정 (pnpm 캐싱 포함)
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22
cache: "pnpm"
# 4. 의존성 설치
- name: Install dependencies
run: pnpm install --frozen-lockfile
# 5. 린트 검사 (선택사항)
- name: Run lint
run: pnpm run lint
# 6. 테스트 실행 (선택사항)
- name: Run tests
run: pnpm test
continue-on-error: true
# 7. Vercel CLI 설치
- name: Install Vercel CLI
run: pnpm add -g vercel@latest
# 8. Vercel 환경변수 다운로드
- name: Pull Vercel Environment Information
run: vercel pull --yes --environment=preview --token=${{ secrets.VERCEL_TOKEN }}
# 9. 프로젝트 빌드
- name: Build Project Artifacts
run: vercel build --token=${{ secrets.VERCEL_TOKEN }}
# 10. Vercel에 배포 (prebuilt 사용)
- name: Deploy Project Artifacts to Vercel
run: vercel deploy --prebuilt --token=${{ secrets.VERCEL_TOKEN }}
yaml--prebuilt 옵션이란?
일반적으로 vercel deploy를 실행하면 Vercel 서버에서 빌드가 진행됩니다.
하지만 --prebuilt 옵션을 사용하면 이미 빌드된 .vercel/output 폴더를 업로드하기만 합니다.
GitHub Actions에서 prebuilt를 사용하는 이유:
- 소스 코드 보안: Vercel에 소스 코드를 보내지 않고 빌드 결과물만 전송합니다
- 커스텀 검증: 빌드 전에 린트, 테스트 등을 먼저 실행할 수 있습니다
- 빌드 환경 제어: GitHub Actions의 빌드 환경을 직접 관리할 수 있습니다
동작 흐름:
# 8단계: 환경변수 다운로드
vercel pull --yes --environment=preview --token=${{ secrets.VERCEL_TOKEN }}
# 9단계: GitHub Actions에서 빌드 (.vercel/output 생성)
vercel build --token=${{ secrets.VERCEL_TOKEN }}
# 10단계: 빌드된 결과물만 Vercel에 업로드
vercel deploy --prebuilt --token=${{ secrets.VERCEL_TOKEN }}bash코드 설명
주요 옵션들을 살펴볼게요:
branches-ignore: - main: main 브랜치는 제외합니다 (Production 배포용으로 분리)pnpm install --frozen-lockfile:pnpm-lock.yaml파일을 기반으로 일관된 의존성 설치를 보장해요pnpm/action-setup@v4: pnpm 패키지 매니저를 설정합니다actions/setup-node@v4의cache: 'pnpm': Node.js 설정과 함께 pnpm 의존성 캐싱을 활성화해요vercel pull --environment=preview: Preview 환경의 환경변수를 다운로드합니다vercel build: GitHub Actions에서 빌드해요 (소스 코드 노출 방지)vercel deploy --prebuilt: 미리 빌드된.vercel/output폴더를 업로드합니다
검증 단계 (선택사항):
린트, 타입 체크, 테스트 단계는 선택사항이에요. 프로젝트에 따라 필요한 단계만 추가하면 됩니다.
이 단계들이 실패하면 배포가 중단되므로, 문제가 있는 코드가 배포되는 것을 방지할 수 있어요.
만약 프로젝트에 테스트가 없다면?
테스트 단계를 제거하거나, continue-on-error: true를 추가해서 실패해도 계속 진행하도록 설정할 수 있습니다.
# 테스트 실행 (실패해도 계속 진행)
- name: Run tests
run: pnpm test
# 테스트가 없는 프로젝트는 이 옵션을 추가하세요
continue-on-error: trueyaml✅ 동작 확인
이제 실제로 Preview 배포가 작동하는지 확인해볼게요!
- feature 브랜치 생성:
git checkout -b feature/test-previewbash- 코드 수정 후 커밋:
# 간단한 파일 수정
echo "# Test Preview Deployment" >> README.md
git add .
git commit -m "test: preview deployment"bash- GitHub에 푸시:
git push origin feature/test-previewbash- GitHub Actions 탭에서 워크플로우 실행 확인
위 화면에서 각 단계의 실행 상태를 확인할 수 있어요. 녹색 체크 표시는 성공, 빨간 X 표시는 실패를 의미합니다.
실제로 해당 워크플로우를 클릭해보면 상세하게 어떤 위치에서 에러가 발생했는지 확인할 수 있어요.
혹여나 워크플로우가 실패한다면 어떤 이유인지 확인하시고 해결하셔서 다시 푸시하시면 또 워크플로우가 돌면서 초록색이 될 떄까지 반복해주세요.
성공적으로 배포되면 Vercel 대시보드에서 Preview URL을 확인할 수 있습니다!
여기서 유의할점은 Preview Url은 해당 Vercel Project에 권한이 있는 사람끼리만 접근할 수 있어요.
실제 배포 된 URL이랑 다른 개념이에요, 내부 테스팅이나 이런 용도로 미리 확인할 수 있는 용도다 라고 생각하면되요!
Preview URL의 특징:
- 각 커밋마다 고유한 URL이 생성돼요
- PR을 통해 팀원들과 미리보기를 공유할 수 있습니다
- Vercel 계정으로 로그인하지 않아도 접근 가능해요 (Git Integration과 다른 점)
🧪 테스트 자동화로 코드 품질 보장하기
배포 전에 자동으로 테스트를 실행하면 버그가 있는 코드가 프로덕션에 배포되는 것을 방지할 수 있어요.
Vitest 설정하기
Vite 기반 프로젝트라면 Vitest를 사용하는 것이 좋습니다. 2026년 1월 기준 현재 Vitest 4.0이 최신 버전이에요.
Vitest 설치:
pnpm add -D vitestbashpackage.json에 테스트 스크립트 추가:
{
"scripts": {
"test": "vitest run",
"test:watch": "vitest"
}
}jsonvitest.config.ts 파일 생성:
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
globals: true,
// jsdom 환경으로 설정.
environment: 'jsdom',
},
});typescript간단한 테스트 작성하기
CI에서 테스트가 실행되는 것을 확인하는 게 목표이니, 아주 간단한 add 함수를 만들어 테스트해볼게요.
src/utils/add.ts 파일 생성:
export function add(a: number, b: number): number {
return a + b;
}typescriptsrc/utils/add.test.ts 파일 생성:
import { describe, it, expect } from 'vitest';
import { add } from './add';
describe('add', () => {
it('두 숫자를 더할 수 있다', () => {
expect(add(1, 2)).toBe(3);
});
it('음수도 처리할 수 있다', () => {
expect(add(-1, 1)).toBe(0);
});
});typescript정말 간단하죠? 핵심은 CI에서 테스트가 자동으로 실행되고, 실패하면 배포가 중단된다는 점이에요.
로컬에서 테스트 실행하기
# 테스트 실행
pnpm test
# 위에서 우리가 테스트 실행 환경을 environment: "jsdom"으로 설정했어요.
# 따라서, jsdom을 설치해야 테스트를 성공적으로 실행할 수 있어요.
✔ Do you want to install jsdom? … yes
bash출력 예시:
✓ src/utils/add.test.ts (2)
✓ add (2)
✓ 두 숫자를 더할 수 있다
✓ 음수도 처리할 수 있다
Test Files 1 passed (1)
Tests 2 passed (2)테스트가 통과했어요! 🎉
GitHub Actions에서 테스트 실행하기
워크플로우 파일에 이미 테스트 단계가 포함되어 있어요.
단, 이전에는 continue-on-error: true로 되어있어 테스트 단계를 실패해도 배포가 진행되도록 설정했어요.
이제는 continue-on-error: true를 제거해주세요.
# 6. 테스트 실행
- name: Run tests
run: pnpm testyaml테스트가 실패하면 배포가 중단됩니다.
테스트 실패 시 동작:
pnpm test실행- 테스트 실패 감지
- 워크플로우 실행 중단
- GitHub Actions에 실패 상태 표시
- 배포가 진행되지 않음
모든 테스트 코드가 성공적인 배포를 보장하지 않아요
개발자가, 어려운 테스트 케이스는 추후에 작성하거나, 회사마다 테스트 코드를 작성하기 어려운 상황이라 코드 품질 관리를 못하는 경우도 많이 존재해요.
그래도 테스트로 문제가 발생하는 상황들을 잘 해결해놓는다면
이렇게 하면 버그가 있는 코드가 프로덕션에 배포되는 것을 완전하게 막지는 못해도, 코드의 안정성을 점차 늘려나갈 수 있어요.
🔍 린트 설정하기
코드 품질을 유지하려면 린트(Lint) 도구가 필수예요. 린트는 코드 스타일을 일관되게 유지하고, 잠재적인 버그를 미리 발견해줍니다.
두 가지 방법을 소개할게요:
- ESLint + Prettier: 가장 널리 사용되는 조합
- Biome: 빠르고 간편한 올인원 도구 (2026년 트렌드!)
방법 1: ESLint + Prettier 설정
ESLint는 코드 품질을, Prettier는 코드 포맷팅을 담당해요.
패키지 설치:
pnpm add -D eslint prettier eslint-config-prettier @typescript-eslint/eslint-plugin @typescript-eslint/parserbasheslint.config.js 파일 생성 (ESLint v9 Flat Config):
import eslint from '@eslint/js';
import tseslint from '@typescript-eslint/eslint-plugin';
import tsparser from '@typescript-eslint/parser';
import prettier from 'eslint-config-prettier';
import globals from 'globals';
export default [
eslint.configs.recommended,
{
files: ['**/*.ts', '**/*.tsx'],
languageOptions: {
parser: tsparser,
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
},
globals: {
...globals.browser,
},
},
plugins: {
'@typescript-eslint': tseslint,
},
rules: {
...tseslint.configs.recommended.rules,
'no-unused-vars': 'off',
'@typescript-eslint/no-unused-vars': 'error',
},
},
prettier, // Prettier와 충돌하는 규칙 비활성화
];
javascript.prettierrc 파일 생성:
{
"semi": true,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "es5",
"printWidth": 80
}jsonpackage.json에 스크립트 추가:
{
"scripts": {
"lint": "eslint . --fix",
"format": "prettier --write ."
}
}json로컬에서 실행:
# 린트 검사 및 자동 수정
pnpm run lint
# 코드 포맷팅
pnpm run formatbash방법 2: Biome 설정 (추천 🔥)
Biome은 ESLint + Prettier를 하나로 합친 도구예요. 기존에 ESLint + Prettier로 작성된 규칙들을 대부분 Biome에서 제공해주고, Rust로 작성되어서 속도 면에서도 엄청 빠릅니다!
단, 기존에 ESLint + Prettier로 작성된 규칙들을 99% 이상 다 제공해주기는 하지만 본인이 쓰는 크리티컬한 기능들이 적용되지않는다면, ESLint + Prettier를 사용해도 무방해요.
패키지 설치:
pnpm add -D @biomejs/biomebashbiome.json 파일 생성:
pnpm biome initbash이 명령어를 실행하면 기본 설정 파일이 생성돼요. 필요에 따라 수정할 수 있습니다:
{
"$schema": "https://biomejs.dev/schemas/2.3.11/schema.json",
"vcs": {
"enabled": true,
"clientKind": "git",
"useIgnoreFile": true
},
"organizeImports": {
"enabled": true
},
"linter": {
"enabled": true,
"rules": {
"recommended": true
}
},
"formatter": {
"enabled": true,
"indentStyle": "space",
"indentWidth": 2
}
}jsonpackage.json에 스크립트 추가:
{
"scripts": {
"lint": "biome check --write .",
"format": "biome format --write ."
}
}json로컬에서 실행:
# 린트 + 포맷팅 한 번에
pnpm run lintbashGitHub Actions에서 린트 실행하기
워크플로우 파일에 이미 린트 단계가 포함되어 있어요:
# 5. 린트 검사
- name: Run lint
run: pnpm run lintyaml린트 실패 시 동작:
pnpm run lint실행- 코드 스타일 오류 감지
- 워크플로우 실행 중단
- GitHub Actions에 실패 상태 표시
이렇게 하면 코드 스타일이 맞지 않는 코드가 머지되는 것을 방지할 수 있어요! ✨
혼자 개발하는 프로젝트에서는 린트 설정을 무시할 수 있지만, 여러 명이 작업하는 팀 프로젝트에서는 필수입니다.
린트가 없으면 발생하는 문제:
- 코드 스타일 불일치: 각자의 에디터 설정에 따라 들여쓰기, 따옴표 스타일이 달라집니다
- 불필요한 Diff 발생: 줄 간격, 세미콜론 등의 차이가 코드 변경으로 표시되어 리뷰가 어려워집니다
- 검토 시간 낭비: PR에서 실제 로직 변경과 스타일 변경을 구분하기 힘듭니다
린트 설정으로 모든 개발자가 일관된 규칙 아래에서 작업하면 코드 리뷰 효율이 크게 향상됩니다.
🏭 Production 배포 자동화 (Main 브랜치)
main 브랜치에 머지되면 자동으로 Production 환경에 배포되도록 설정해볼게요.
워크플로우 파일 생성
.github/workflows/production.yml 파일을 생성합니다.
name: Vercel Production Deployment
env:
VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
on:
push:
branches:
- main # main 브랜치에만 실행
jobs:
Deploy-Production:
runs-on: ubuntu-latest
steps:
# 1. 코드 체크아웃
- name: Checkout code
uses: actions/checkout@v4
# 2. pnpm 설정
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 10
# 3. Node.js 설정 (pnpm 캐싱 포함)
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22
cache: 'pnpm'
# 4. 의존성 설치
- name: Install dependencies
run: pnpm install --frozen-lockfile
# 5. 린트 검사
- name: Run lint
run: pnpm run lint
# 6. 타입 체크
- name: Type check
run: pnpm run type-check
# 7. 테스트 실행
- name: Run tests
run: pnpm test
# 8. Vercel CLI 설치
- name: Install Vercel CLI
run: pnpm add -g vercel@latest
# 9. Vercel 환경변수 다운로드 (Production)
- name: Pull Vercel Environment Information
run: vercel pull --yes --environment=production --token=${{ secrets.VERCEL_TOKEN }}
# 10. 프로젝트 빌드 (Production)
- name: Build Project Artifacts
run: vercel build --prod --token=${{ secrets.VERCEL_TOKEN }}
# 11. Vercel에 배포 (Production)
- name: Deploy Project Artifacts to Vercel
run: vercel deploy --prebuilt --prod --token=${{ secrets.VERCEL_TOKEN }}yamlPreview와의 차이점
Production 배포 워크플로우는 Preview와 거의 동일하지만 몇 가지 중요한 차이가 있어요.
차이점:
branches: - main: main 브랜치에서만 실행됩니다--environment=production: Production 환경변수를 사용해요--prod플래그: Production으로 배포합니다
--frozen-lockfile 옵션이란?
pnpm install --frozen-lockfile은 CI 환경에서 필수적인 옵션입니다.
일반 pnpm install과의 차이:
- 일반
pnpm install:pnpm-lock.yaml이package.json과 맞지 않으면 자동으로 lock 파일을 업데이트합니다 pnpm install --frozen-lockfile: lock 파일과package.json이 맞지 않으면 에러를 발생시키고 설치를 중단합니다
왜 CI에서 필수인가요?
- 일관성 보장: 로컬 환경과 CI 환경에서 정확히 동일한 버전의 패키지가 설치됩니다
- 예측 가능성: lock 파일에 명시된 버전만 설치되므로 “로컬에서는 되는데 CI에서는 안 되는” 문제를 예방합니다
- 보안: 의도하지 않은 패키지 버전 변경을 차단합니다
실제 시나리오:
# 로컬에서 개발 (lock 파일 생성됨)
pnpm install
git add pnpm-lock.yaml
git commit -m "feat: add new feature"
# GitHub Actions CI에서 실행
# frozen-lockfile 옵션으로 정확히 같은 버전 설치 보장
pnpm install --frozen-lockfilebash만약 pnpm-lock.yaml 파일이 없거나 package.json과 맞지 않으면 다음 에러가 발생합니다:
ERR_PNPM_NO_LOCKFILE Cannot install with frozen-lockfile because pnpm-lock.yaml is absent이 경우 로컬에서 pnpm install을 실행해 lock 파일을 업데이트하고 커밋해야 합니다.
왜 Preview와 Production을 분리하나요?
- 다른 환경변수: Preview는 스테이징 API를, Production은 실제 API를 사용할 수 있어요
- 다른 빌드 옵션: Production은 더 엄격한 검증을 거칠 수 있습니다
- 배포 타이밍 제어: main에 머지될 때만 Production에 배포돼요
📊 배포 흐름
전체 배포 흐름을 정리하면 다음과 같습니다:
- feature 브랜치에서 작업
- GitHub에 푸시 → Preview 배포 자동 실행
- PR 생성 → Preview URL 생성
- 코드 리뷰 완료 후 main에 머지
- main에 머지되는 순간 Production 배포 자동 실행
- 실제 사용자가 접근하는 도메인에 배포 완료! 🎉
타임라인 예시:
09:00 - feature 브랜치에서 개발 시작
10:00 - GitHub에 푸시 → Preview 배포 시작
10:02 - Preview URL 생성 → https://project-abc123.vercel.app
10:05 - PR 생성 → 팀원에게 리뷰 요청
11:00 - 리뷰 완료 후 main에 머지
11:01 - Production 배포 시작
11:03 - Production 배포 완료 → https://myproject.com 업데이트💬 Pull Request에 Preview URL 자동 추가하기
Preview 배포가 완료되면 PR에 자동으로 배포 URL을 코멘트로 남기면 편리하죠?
워크플로우 수정
.github/workflows/preview.yml을 다음과 같이 수정합니다.
GitHub Actions 권한 설정
PR에 댓글을 남기려면 워크플로우에 쓰기 권한이 필요합니다.
두 가지 설정 필요:
- 워크플로우 파일에 권한 추가 (필수)
- 레포지토리 설정 확인 (권한 에러 발생 시)
1단계: 워크플로우 권한 추가
워크플로우 파일 상단에 다음 권한을 추가하세요:
permissions:
contents: read # 코드 읽기 권한
pull-requests: write # PR 댓글 작성 권한
issues: write # 이슈 댓글 작성 권한
name: Vercel Preview Deployment
env:
VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
on:
pull_request:
types: [opened, synchronize, reopened]
jobs:
Deploy-Preview:
runs-on: ubuntu-latest
steps:
# 1. 코드 체크아웃
- name: Checkout code
uses: actions/checkout@v4
# 2. pnpm 설정
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 10
# 3. Node.js 설정
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22
cache: 'pnpm'
# 4. 의존성 설치
- name: Install dependencies
run: pnpm install --frozen-lockfile
# 5. 린트 검사
- name: Run lint
run: pnpm run lint
# 6. 타입 체크
- name: Type check
run: pnpm run type-check
# 7. 테스트 실행
- name: Run tests
run: pnpm test
# 8. Vercel CLI 설치
- name: Install Vercel CLI
run: pnpm add -g vercel@latest
# 9. Vercel 환경변수 다운로드
- name: Pull Vercel Environment Information
run: vercel pull --yes --environment=preview --token=${{ secrets.VERCEL_TOKEN }}
# 10. 프로젝트 빌드
- name: Build Project Artifacts
run: vercel build --token=${{ secrets.VERCEL_TOKEN }}
# 11. 배포 URL을 출력 변수로 저장
- name: Deploy and capture URL
id: deploy
run: |
DEPLOY_URL=$(vercel deploy --prebuilt --token=${{ secrets.VERCEL_TOKEN }})
echo "url=$DEPLOY_URL" >> $GITHUB_OUTPUT
# 12. PR에 배포 URL 코멘트
- name: Comment PR with deployment URL
uses: actions/github-script@v7
if: github.event_name == 'pull_request'
with:
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: `✅ Preview 배포가 완료되었습니다!\n\n🔗 Preview URL: ${{ steps.deploy.outputs.url }}`
})yaml2단계: 레포지토리 설정 확인
만약 위 설정 후에도 권한 에러가 발생한다면:
- GitHub 레포지토리 →
Settings→Actions→General Workflow permissions섹션에서Read and write permissions선택Save클릭
대부분의 경우 1단계만으로 충분하지만, 조직(Organization) 레포지토리는 2단계 설정이 필요할 수 있습니다.
코드 설명
주요 변경사항:
on: pull_request: PR 이벤트에서만 실행되도록 변경id: deploy: 배포 단계에 ID를 부여해서 출력 변수를 참조할 수 있게 함echo "url=$DEPLOY_URL" >> $GITHUB_OUTPUT: 배포 URL을 출력 변수로 저장actions/github-script@v7: GitHub API를 사용해서 PR에 코멘트 추가
왜 actions/github-script를 사용하나요?
GitHub API를 직접 호출하려면 인증 토큰을 설정하고 HTTP 요청을 보내야 해요.
actions/github-script는 이 과정을 간소화해서 JavaScript 코드로 GitHub API를 쉽게 사용할 수 있게 해줍니다.
동작 확인
이제 PR을 생성하면 자동으로 Preview URL이 코멘트로 추가돼요!
물론 Vercel이 PR에 Preview URL과, Comment를 줘요.
그래서 위에서 한 작업이 불필요할 수도 있지만, 이를 커스텀하여 Slack Bot이나, 메일 등으로 알림을 줄 수 있어요.
깃허브를 잘 보지 않는 다른 이해관계자들에게 우리의 작업 결과물을 검토받는데 이를 활용할 수 있어요.
이렇게 사전 작업을 하면, 보다 팀원들이 PR 리뷰를 하는데 편리하니 꼭 설정해두세요!
🔥 자주 발생하는 문제와 해결 방법
VERCEL_TOKEN 권한 오류
오류 메시지:
Error: The specified token is not valid해결 방법:
- Vercel 토큰이 올바른지 확인합니다
- Scope가
Full Account로 설정되어 있는지 확인해요 - GitHub Secrets에 올바르게 등록되었는지 확인합니다 (대소문자 구분)
확인 방법:
# 로컬에서 토큰 테스트
VERCEL_TOKEN=your-token-here vercel whoamibash정상적으로 계정 정보가 표시되면 토큰이 유효합니다.
pnpm-lock.yaml 누락 오류
오류 메시지:
ERR_PNPM_NO_LOCKFILE Cannot install with frozen-lockfile because pnpm-lock.yaml is absent해결 방법:
- 로컬에서
pnpm install을 실행해서pnpm-lock.yaml생성 - 생성된
pnpm-lock.yaml을 Git에 커밋 - GitHub에 푸시
pnpm install
git add pnpm-lock.yaml
git commit -m "chore: add pnpm-lock.yaml"
git pushbash왜 lock 파일이 필요한가요?
--frozen-lockfile 옵션은 lock 파일에 명시된 정확한 버전의 패키지를 설치해요. 이렇게 하면 로컬 환경과 CI 환경에서 동일한 의존성을 보장할 수 있습니다.
빌드 실패: 환경변수 없음
오류 메시지:
Error: VITE_API_URL is not defined해결 방법:
- Vercel 대시보드에서 환경변수가 올바르게 설정되었는지 확인합니다
vercel pull단계가 워크플로우에 포함되어 있는지 확인해요- 환경변수 이름이 정확한지 확인합니다 (
VITE_,NEXT_PUBLIC_등 접두사)
디버깅 팁:
워크플로우에 환경변수 출력 단계를 추가해서 확인할 수 있어요.
- name: Debug environment variables
run: |
echo "Environment variables:"
env | grep VITE_ || echo "No VITE_ variables found"yaml⚠️ 주의: 민감한 정보가 포함된 환경변수는 출력하지 않아야 해요!
린트/테스트 스킵하기
개발 중에 일시적으로 린트나 테스트를 스킵하고 싶다면 두 가지 방법이 있어요.
방법 1: continue-on-error 사용
# 린트 실행 (실패해도 계속 진행)
- name: Run lint
run: pnpm run lint
continue-on-error: trueyaml방법 2: 커밋 메시지에 [skip ci] 포함
커밋 메시지에 [skip ci]를 포함하면 워크플로우 자체를 스킵합니다.
git commit -m "docs: update README [skip ci]"bash이 방법은 문서 수정처럼 빌드가 필요 없는 변경사항에 유용해요.
⚡ 배포 속도 최적화
빌드 시간을 줄이는 것은 팀의 빠른 성장을 위해 필수적이에요, 심지어 처음 프로젝트를 할 떄는, 배포를 하는데 2,000분이 넉넉하다고 느낄 수 있지만 실제로 프로젝트의 규모가 커지고, 테스팅할 요소들이 많아진다면 2,000분을 금방 채울 수 있어요.
그러면 우리는 자연스럽게 깃허브에게 추가 결제를 통해 사용량을 늘려야합니다.
빌드 시간을 줄이는 것은 어찌보면 팀의 효율적인 운영에 매우 중요한 요소에요.
가볍게 아래와 같은 기능으로 우리는 해결해볼 수 있어요.
pnpm 의존성 캐싱:
actions/setup-node@v4는 pnpm 캐싱을 기본 지원해요:
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 10
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22
cache: 'pnpm' # pnpm 캐싱 활성화yaml이렇게 설정하면 pnpm install 실행 시간이 크게 단축됩니다. 첫 번째 실행에서 의존성을 캐시하고, 이후 실행에서는 캐시된 의존성을 재사용해요.
빌드 결과물 캐싱:
- name: Cache build output
uses: actions/cache@v4
with:
path: |
.next/cache
node_modules/.cache
key: ${{ runner.os }}-build-${{ hashFiles('pnpm-lock.yaml') }}yaml
캐시 히트 이후 이전 워크플로우와 빌드 시간을 비교해보면 줄어든 것을 확인할 수 있어요.
단, 프레임워크마다 캐시 경로가 다르므로 공식 문서를 참고하세요.
🎯 GitHub Actions CI/CD 파이프라인의 장점 정리
GitHub Actions를 사용한 Vercel 배포 파이프라인을 구축하면 다음과 같은 장점이 있어요.
코드 푸시만으로 자동 배포
더 이상 터미널을 열고 vercel --prod 명령어를 입력할 필요가 없습니다!
- main 브랜치에 머지 → Production 자동 배포
- feature 브랜치에 푸시 → Preview 자동 배포
배포 전 자동 검증
린트, 타입 체크, 테스트가 모두 통과해야 배포돼요.
문제가 있는 코드가 배포되는 것을 원천 차단할 수 있습니다.
- name: Run lint
run: pnpm run lint # 실패하면 배포 중단
- name: Run tests
run: pnpm test # 실패하면 배포 중단yaml보안 강화
GitHub에서 빌드하므로 Vercel에 소스 코드를 노출하지 않아요.
민감한 환경변수는 GitHub Secrets로 안전하게 관리됩니다.
팀 협업 개선
PR마다 Preview URL이 자동으로 생성돼요.
배포 상태를 GitHub에서 한눈에 확인할 수 있습니다.
코드 리뷰와 배포 검증을 한 곳에서 진행할 수 있어요.
유연한 커스터마이징
원하는 검증 단계를 자유롭게 추가할 수 있습니다.
- 보안 스캔 (npm audit, Snyk 등)
- 성능 측정 (Lighthouse CI 등)
- E2E 테스트 (Playwright, Cypress 등)
- 배포 후 슬랙 알림
- 모니터링 연동 (Sentry, DataDog 등)
🎉 마무리
이제 코드를 푸시하기만 하면 자동으로 린트, 테스트, 빌드, 배포까지 완료됩니다!
더 이상 vercel --prod 명령어를 입력할 필요가 없어요. GitHub Actions가 대신 처리해줍니다.
처음 설정은 조금 복잡하지만, 한 번 구축해두면 팀 전체의 생산성이 크게 향상돼요.
GitHub Actions를 활용한 CI/CD 파이프라인을 구축하는 데 도움이 되었기를 바랍니다! 🚀
📚 관련 글:
🔗 참고 자료: