GitHub Flow: main을 항상 배포 가능하게 유지하는 브랜치와 풀 리퀘스트 중심 워크플로
GitHub Flow는 main 브랜치를 항상 배포 가능한 상태로 두고, 모든 변경을 짧은 수명의 기능 브랜치와 풀 리퀘스트(Pull Request)로 통합하는 경량 워크플로다. 브랜치 생성, 커밋, PR, 리뷰, 병합, 배포의 단순한 흐름으로 구성된다. 이 글에서는 원리와 실제 명령어, 실무 설정, 주의할 점을 정리한다.
By 송민성
1. 개념
GitHub Flow는 main 브랜치 하나를 기준으로 삼고, 모든 작업을 별도의 기능 브랜치(feature branch)에서 진행한 뒤 풀 리퀘스트(Pull Request, PR)를 통해 main에 병합(merge)하는 브랜치 전략이다.
핵심 규칙은 다음과 같다.
main의 코드는 언제든 배포할 수 있는 상태여야 한다.- 새 작업은
main에서 분기한 브랜치에서 시작하며, 브랜치 이름은 작업 내용을 드러내게 짓는다. - 변경은 PR로 올려 리뷰와 자동 검사(CI)를 거친다.
- 승인된 PR만
main에 병합하고, 병합 후 배포한다.
Git Flow처럼 develop, release, hotfix 브랜치를 따로 두지 않는다. 장기 브랜치가 main 하나뿐이라는 점이 가장 큰 차이다.
2. 왜 사용하는가
- 단순함: 브랜치 종류가 적어 팀원이 규칙을 외우기 쉽다.
- 지속적 배포(Continuous Deployment)에 적합: 병합이 곧 배포 후보가 되므로 배포 주기를 짧게 가져갈 수 있다.
- 리뷰 문화 정착: 모든 변경이 PR을 거치므로 코드 리뷰와 CI 검증이 기본 절차가 된다.
- 충돌 최소화: 브랜치 수명이 짧을수록
main과의 차이가 작아 병합 충돌(conflict)이 줄어든다.
웹 서비스처럼 버전을 여러 개 동시에 유지보수하지 않고, 하나의 최신 버전만 운영하는 경우에 특히 잘 맞는다.
3. 동작 원리
GitHub Flow는 여섯 단계로 이루어진다.
- 브랜치 생성:
main에서 새 브랜치를 만든다. - 커밋: 브랜치에서 변경을 작은 단위로 커밋하고 원격 저장소에 푸시(push)한다.
- PR 생성: 변경 내용과 의도를 설명하는 PR을 연다. 초안(Draft) PR로 일찍 공유할 수도 있다.
- 리뷰와 검증: 리뷰어가 코멘트를 남기고, CI가 테스트와 린트를 실행한다. 피드백은 같은 브랜치에 커밋을 추가해 반영한다.
- 병합: 승인과 CI 통과 후
main에 병합한다. GitHub는 병합 방식으로 Merge commit, Squash and merge, Rebase and merge 세 가지를 제공한다. - 배포와 정리:
main을 배포하고 작업 브랜치를 삭제한다.
브랜치는 커밋을 가리키는 포인터에 불과하므로 생성과 삭제 비용이 매우 작다. 이 특성 덕분에 "작업마다 브랜치 하나"라는 규칙이 부담 없이 성립한다.
4. 코드 예제
4-1. 기본 흐름 (Shell)
# 1. 최신 main 가져오기
git switch main
git pull origin main
# 2. 작업 브랜치 생성
git switch -c feat/add-login-validation
# 3. 변경 후 커밋
git add .
git commit -m "feat: 로그인 입력값 검증 추가"
# 4. 원격에 푸시 (upstream 설정 포함)
git push -u origin feat/add-login-validation
# 5. GitHub CLI로 PR 생성
gh pr create --title "로그인 입력값 검증 추가" --body "이메일 형식과 비밀번호 길이를 검증합니다."
# 6. 승인 후 squash 병합, 원격 브랜치 삭제
gh pr merge --squash --delete-branch
# 7. 로컬 정리
git switch main
git pull origin main
git branch -d feat/add-login-validation4-2. 리뷰 중 main이 앞서 나갔을 때
git fetch origin
git rebase origin/main
# 충돌이 나면 파일 수정 후
git add <충돌 해결한 파일>
git rebase --continue
# 이미 푸시한 브랜치이므로 안전한 강제 푸시 사용
git push --force-with-lease4-3. GitHub Actions로 PR마다 CI 실행
.github/workflows/ci.yml 파일 예시다.
name: CI
on:
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm ci
- run: npm test이 워크플로를 브랜치 보호 규칙(branch protection rule)의 필수 상태 검사(required status checks)로 지정하면, 테스트를 통과하지 못한 PR은 병합할 수 없다.
5. 시간 복잡도 또는 성능 특성
알고리즘적 시간 복잡도를 따지는 주제는 아니지만, 운영 관점의 특성은 다음과 같다.
- 브랜치 생성과 삭제는 커밋 해시를 담은 작은 참조(ref)를 만들고 지우는 작업이라 저장소 크기와 거의 무관하게 가볍다.
- 브랜치 수명이 길어질수록
main과의 차이가 커져 충돌 해결 비용이 늘어난다. 하루 이틀 안에 병합될 만큼 작업 단위를 쪼개는 것이 이상적이다. - PR 크기가 클수록 리뷰 품질과 속도가 떨어지는 경향이 있으므로, 변경 범위를 작게 유지한다.
6. 실무 사용 사례
- 웹 서비스 팀: SaaS처럼 하나의 최신 버전만 운영하는 팀이 PR 병합 직후 자동 배포를 연결해 쓴다.
- 오픈소스 프로젝트: 외부 기여자가 저장소를 포크(fork)해 PR을 보내는 방식도 같은 원리다.
- 브랜치 보호 규칙 조합:
main직접 푸시 금지, 최소 리뷰어 1명 이상 승인, CI 통과 필수를 설정한다. - 브랜치 이름 규칙:
feat/,fix/,docs/같은 접두사와 이슈 번호를 붙여 목적을 드러낸다. 예:fix/123-null-pointer. - PR 템플릿:
.github/pull_request_template.md에 변경 요약, 테스트 방법 체크리스트를 두어 리뷰 품질을 맞춘다.
7. 주의할 점
- main 직접 푸시 금지: 규칙이 문서에만 있으면 지켜지지 않는다. 브랜치 보호 규칙으로 강제한다.
- 장기 브랜치 방치: 며칠씩 병합하지 않은 브랜치는 충돌의 원인이 된다. 미완성 기능은 기능 플래그(feature flag)로 감춘 채 작게 병합하는 방법을 쓴다.
- 강제 푸시: 공유 브랜치에서
git push --force는 다른 사람의 커밋을 덮어쓸 수 있다. 필요하면--force-with-lease를 쓰고,main에는 쓰지 않는다. - 배포 자동화 부재: GitHub Flow는 "병합하면 배포 가능"을 전제한다. 테스트와 배포 자동화가 부실하면
main이 불안정해진다. - 적용 한계: 여러 버전을 동시에 지원해야 하거나 정해진 릴리스 일정이 있는 제품(모바일 앱, 설치형 소프트웨어 등)은 릴리스 브랜치가 필요할 수 있어 Git Flow나 별도 전략이 더 맞을 수 있다.
- Squash 병합의 이력: Squash는 PR의 커밋을 하나로 합쳐
main이력을 깔끔하게 하지만, 세부 커밋 이력은 PR 페이지에만 남는다.
8. 핵심 정리
- GitHub Flow는
main하나를 항상 배포 가능하게 두고, 모든 변경을 짧은 기능 브랜치와 PR로 통합하는 전략이다. - 흐름은 브랜치 생성 → 커밋 → PR → 리뷰/CI → 병합 → 배포 순서다.
- 브랜치 보호 규칙과 CI를 함께 설정해야 규칙이 실제로 지켜진다.
- 브랜치는 짧게, PR은 작게 유지할수록 충돌과 리뷰 부담이 줄어든다.
- 하나의 최신 버전을 지속적으로 배포하는 웹 서비스에 적합하며, 다중 버전 유지가 필요하면 다른 전략을 검토한다.