GitHub Actions, CI/CD 파이프라인 없이도 git push 한 번만으로 서버에 자동 배포되는 환경을 구성할 수 있습니다. Git의 server-side hook 중 하나인 post-receive를 이용하면 됩니다.
Git Hooks란?
Git은 특정 이벤트가 발생할 때 자동으로 실행되는 스크립트인 Hooks를 지원합니다. 클라이언트 훅(pre-commit, pre-push 등)과 서버 훅(pre-receive, post-receive 등)으로 나뉩니다.
pre-receive— push 직전에 실행 (거부 가능)post-receive— push 완료 후 실행 ← 배포에 사용
설정 방법
1단계 — 서버에 bare 저장소 만들기
서버에 SSH로 접속 후, bare 저장소를 생성합니다. Bare 저장소는 작업 디렉토리 없이 Git 데이터만 가진 저장소입니다.
# 서버에서 실행
mkdir -p ~/repos/myproject.git
cd ~/repos/myproject.git
git init --bare
2단계 — post-receive 훅 작성
vi ~/repos/myproject.git/hooks/post-receive
#!/bin/bash
# 배포 디렉토리
TARGET="/var/www/myproject"
# bare 저장소 경로
GIT_DIR="/home/deploy/repos/myproject.git"
BRANCH="main"
while read oldrev newrev refname; do
# main 브랜치에 push 됐을 때만 배포
if [ "$refname" = "refs/heads/$BRANCH" ]; then
echo "🚀 배포 시작..."
git --work-tree="$TARGET" --git-dir="$GIT_DIR" checkout -f "$BRANCH"
echo "✅ 배포 완료!"
fi
done
# 실행 권한 부여 (필수!)
chmod +x ~/repos/myproject.git/hooks/post-receive
3단계 — 로컬에서 remote 추가
# 로컬 프로젝트에서 실행
git remote add production deploy@your-server.com:repos/myproject.git
# 이제 push 한 번으로 배포
git push production main
Node.js 프로젝트에 적용하기
Node.js 프로젝트라면 배포 후 패키지 설치와 프로세스 재시작도 훅에서 처리할 수 있습니다.
#!/bin/bash
TARGET="/var/www/myapp"
GIT_DIR="/home/deploy/repos/myapp.git"
while read oldrev newrev refname; do
if [ "$refname" = "refs/heads/main" ]; then
echo "📦 코드 동기화..."
git --work-tree="$TARGET" --git-dir="$GIT_DIR" checkout -f main
echo "📦 패키지 설치..."
cd "$TARGET"
npm install --production
echo "🔄 서버 재시작..."
pm2 restart myapp
echo "✅ 배포 완료! $(date)"
fi
done
GitHub Pages 방식 (무료)
개인 정적 사이트라면 GitHub Pages를 활용하면 서버 없이도 push만으로 배포됩니다.
# gh-pages 브랜치에 push하면 자동 배포
git subtree push --prefix dist origin gh-pages
# 또는 npm 패키지 활용
npm install -g gh-pages
gh-pages -d dist
서버 훅 방식을 사용할 때는 배포 전용 사용자(
deploy)를 따로 만들고, SSH 공개 키 인증만 허용하세요. 비밀번호 인증은 반드시 비활성화하세요.
배포 실패 대비 — 롤백 전략
어떤 배포 방식이든 실패에 대비한 롤백 절차를 미리 만들어두어야 합니다. Git 훅 방식에서는 이전 배포 커밋 해시를 기록해두고, 문제 발생 시 해당 커밋으로 강제 체크아웃하는 스크립트를 추가하면 됩니다.
#!/bin/bash
# rollback.sh — 특정 커밋으로 롤백
TARGET="/var/www/myproject"
GIT_DIR="/home/deploy/repos/myproject.git"
ROLLBACK_TO=$1 # 인수로 커밋 해시 전달
if [ -z "$ROLLBACK_TO" ]; then
echo "사용법: ./rollback.sh <커밋 해시>"
exit 1
fi
git --work-tree="$TARGET" --git-dir="$GIT_DIR" checkout -f "$ROLLBACK_TO"
echo "롤백 완료: $ROLLBACK_TO"
배포할 때마다 git log --oneline -5로 최근 커밋 해시를 기록해두거나, 배포 성공 시점의 해시를 별도 파일에 저장해두면 롤백 시 빠르게 대응할 수 있습니다.
Git 훅 vs GitHub Actions — 선택 기준
두 방식 모두 자동 배포를 구현하지만 적합한 상황이 다릅니다. 상황에 맞는 도구를 선택하세요.
| 항목 | Git Hooks | GitHub Actions |
|---|---|---|
| 설정 난이도 | 낮음 (스크립트 하나) | 중간 (YAML 파일 작성) |
| 서버 필요 | 필요 (SSH 접근) | 불필요 (GitHub 인프라 사용) |
| 비용 | 서버 운영 비용만 | 월 2,000분 무료, 초과 시 과금 |
| 테스트 연동 | 직접 구성 필요 | 테스트·린트·배포 파이프라인 통합 쉬움 |
| 적합한 상황 | 소규모 개인 서버, 간단한 배포 | 팀 프로젝트, CI/CD 전체 파이프라인 |
마치며
GitHub Actions가 강력하지만, 소규모 프로젝트나 개인 서버 환경에서는 git hooks만으로도 충분한 자동화가 가능합니다. 롤백 스크립트도 함께 준비해두면 배포 사고 발생 시 빠르게 복구할 수 있습니다. 한번 설정해두면 이후 git push production main 한 줄로 배포가 끝납니다.
GitHub Actions로 자동 배포 파이프라인 구축
Git Hook은 로컬 개발자 환경에만 적용되지만, GitHub Actions는 팀 전체에 적용되는 서버 사이드 자동화입니다. 프로덕션 배포에는 GitHub Actions가 더 적합합니다.
# .github/workflows/deploy.yml
name: Deploy to Production
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'
cache: 'npm'
- name: 의존성 설치 및 테스트
run: |
npm ci
npm test
- name: 빌드
run: npm run build
- name: 서버 배포 (SSH)
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
cd /var/www/myapp
git pull origin main
npm ci --production
pm2 restart myapp
무중단 배포 (Zero-downtime Deployment)
트래픽이 있는 서비스는 배포 중에도 서비스가 중단되지 않아야 합니다. PM2 클러스터 모드와 Nginx를 활용한 무중단 배포 방법입니다.
# PM2 클러스터 모드로 무중단 재시작
pm2 start app.js -i max # CPU 코어 수만큼 프로세스 생성
pm2 reload myapp # 순차적으로 재시작 (무중단)
# ecosystem.config.js
module.exports = {
apps: [{
name: 'myapp',
script: 'server.js',
instances: 'max',
exec_mode: 'cluster',
wait_ready: true, # 앱이 준비 신호를 보낼 때까지 대기
listen_timeout: 10000,
}]
};
Nginx upstream에 복수 서버를 구성하고 헬스체크를 추가하면 한 서버 업데이트 중에도 다른 서버가 요청을 처리합니다. 배포 후에는 에러율과 응답 시간을 모니터링해 이상 징후를 빠르게 감지하는 것이 중요합니다.
배포 체크리스트
배포 전후 반드시 확인해야 할 항목을 정리하면 실수를 크게 줄일 수 있습니다.
- ✅ 배포 전 — 스테이징 환경에서 기능 검증 완료, 환경 변수 설정 확인, 데이터베이스 마이그레이션 스크립트 검토
- ✅ 배포 중 — 에러 로그 실시간 모니터링, 503 에러율 확인
- ✅ 배포 후 — 주요 기능 스모크 테스트, 응답 시간 정상 여부 확인, 롤백 커밋 해시 기록
Git 브랜치 전략과 배포 환경의 연결
배포 파이프라인을 효과적으로 운영하려면 브랜치 전략과 배포 환경을 일관성 있게 연결해야 합니다. 가장 널리 쓰이는 방식은 세 환경 모델입니다. develop 브랜치에 푸시하면 개발(dev) 환경에 자동 배포, staging 브랜치 병합 시 스테이징 환경에 배포, main 브랜치 병합 시 프로덕션 배포가 되도록 설정합니다. 이렇게 하면 QA팀이 스테이징에서 검증한 것과 정확히 동일한 코드가 프로덕션에 올라가기 때문에 "내 환경에서는 됐는데"라는 문제가 줄어듭니다. 환경별로 다른 환경 변수를 주입하는 것도 중요합니다. 데이터베이스 연결 문자열, API 키, 기능 플래그 같은 값들은 코드에 하드코딩하지 말고 각 환경의 시크릿 관리 시스템(AWS Secrets Manager, GitHub Secrets 등)에서 불러와야 합니다.
핫픽스 상황도 브랜치 전략에 미리 포함해야 합니다. 프로덕션에서 긴급한 버그가 발생했을 때 개발 중인 미완성 기능이 섞인 develop 브랜치를 바로 배포하면 안 됩니다. main에서 직접 hotfix 브랜치를 만들고, 수정 후 main과 develop 양쪽에 병합하는 흐름이 Git Flow 방식의 표준입니다. 평소에 이 흐름을 팀 전체가 숙지하고 있어야 위기 상황에서도 침착하게 대응할 수 있습니다.