본문 바로가기
TYLER SONGBlog
← Blog index
CS학습6 min read

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는 여섯 단계로 이루어진다.

  1. 브랜치 생성: main에서 새 브랜치를 만든다.
  2. 커밋: 브랜치에서 변경을 작은 단위로 커밋하고 원격 저장소에 푸시(push)한다.
  3. PR 생성: 변경 내용과 의도를 설명하는 PR을 연다. 초안(Draft) PR로 일찍 공유할 수도 있다.
  4. 리뷰와 검증: 리뷰어가 코멘트를 남기고, CI가 테스트와 린트를 실행한다. 피드백은 같은 브랜치에 커밋을 추가해 반영한다.
  5. 병합: 승인과 CI 통과 후 main에 병합한다. GitHub는 병합 방식으로 Merge commit, Squash and merge, Rebase and merge 세 가지를 제공한다.
  6. 배포와 정리: main을 배포하고 작업 브랜치를 삭제한다.

브랜치는 커밋을 가리키는 포인터에 불과하므로 생성과 삭제 비용이 매우 작다. 이 특성 덕분에 "작업마다 브랜치 하나"라는 규칙이 부담 없이 성립한다.

4. 코드 예제

4-1. 기본 흐름 (Shell)

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-validation

4-2. 리뷰 중 main이 앞서 나갔을 때

shell
git fetch origin git rebase origin/main # 충돌이 나면 파일 수정 후 git add <충돌 해결한 파일> git rebase --continue # 이미 푸시한 브랜치이므로 안전한 강제 푸시 사용 git push --force-with-lease

4-3. GitHub Actions로 PR마다 CI 실행

.github/workflows/ci.yml 파일 예시다.

yaml
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은 작게 유지할수록 충돌과 리뷰 부담이 줄어든다.
  • 하나의 최신 버전을 지속적으로 배포하는 웹 서비스에 적합하며, 다중 버전 유지가 필요하면 다른 전략을 검토한다.