본문 바로가기
개발 팁

개발 브랜치 전략 완전 정복 — Git Flow, GitHub Flow, Trunk-based 비교

by 루까(Luka) 2026. 3. 19.
반응형

왜 이 글을 쓰나

4명짜리 팀 프로젝트에서 브랜치 전략 없이 개발을 시작한 적이 있다. 처음엔 main에 직접 push하고, 나중엔 각자 이름 딴 브랜치(작업중, 이번꺼, final, final-진짜)를 만들었다. 2주 뒤 머지를 시도하자 충돌이 200개 넘게 쏟아졌다. 누가 무엇을 어디에 변경했는지 아무도 몰랐다.

그때 알았다. 브랜치 전략은 선택이 아니라 팀 작업의 최소 기반 인프라다.

Git Flow, GitHub Flow, Trunk-based Development — 세 가지 전략을 쓰는 상황과 장단점, 실전 명령어까지 정리한다. 처음부터 끝까지 읽으면 "우리 팀에 뭘 쓸지"가 명확해진다.


Git Flow

구조

Vincent Driessen이 2010년에 제안한 모델이다. 브랜치를 용도별로 철저하게 나눈다.

main        ─────●──────────────────────────────●─────
                 │                              ↑ release 머지
develop     ─────●────●──────●──────────────●───
                      ↑      ↑              ↑
feature          ─────┘   ───┘           ───┘
release                             ────●──────
hotfix                                        ────●
브랜치 역할 수명
main 프로덕션 코드. 태그로 릴리즈 관리 영구
develop 다음 릴리즈를 위한 통합 브랜치 영구
feature/* 기능 개발. develop에서 분기, develop으로 머지 단기
release/* 릴리즈 준비. 버그 수정, 버전 번호 업 단기
hotfix/* 프로덕션 긴급 패치. main에서 분기, main+develop 양쪽 머지 단기

실전 명령어 플로우

기능 개발

# develop에서 feature 브랜치 분기
git checkout develop
git pull origin develop
git checkout -b feature/user-authentication

# 작업 후 develop에 머지
git checkout develop
git merge --no-ff feature/user-authentication
git push origin develop
git branch -d feature/user-authentication

릴리즈

# develop에서 release 브랜치 분기
git checkout develop
git checkout -b release/1.2.0

# 버전 번호 업, 최종 QA 버그 수정 후
git checkout main
git merge --no-ff release/1.2.0
git tag -a v1.2.0 -m "Release version 1.2.0"

# develop에도 릴리즈 변경사항 반영
git checkout develop
git merge --no-ff release/1.2.0
git branch -d release/1.2.0

핫픽스

# main에서 hotfix 분기
git checkout main
git checkout -b hotfix/payment-null-pointer

# 수정 후 main과 develop 양쪽에 머지
git checkout main
git merge --no-ff hotfix/payment-null-pointer
git tag -a v1.2.1 -m "Hotfix: payment null pointer"

git checkout develop
git merge --no-ff hotfix/payment-null-pointer
git branch -d hotfix/payment-null-pointer

--no-ff 옵션은 fast-forward를 막고 머지 커밋을 강제 생성한다. 히스토리에 "이 기능은 이 시점에 머지됐다"는 기록이 남는다. Git Flow에서는 이걸 빼면 나중에 히스토리 추적이 어려워진다.

장단점

장점 단점
릴리즈 주기가 명확하게 관리됨 브랜치 구조가 복잡하고 규칙이 많음
핫픽스와 일반 개발을 완전히 분리 develop과 main 간 이중 관리 피로
버전 관리가 체계적 CI/CD와 연동할 때 설정 복잡
팀원이 많아도 충돌 관리 용이 배포 주기가 긴 팀에는 맞지만, 짧은 팀엔 오버헤드

GitHub Flow

구조

GitHub이 내부적으로 사용하는 방식이다. 단순하다.

main     ────●──────────────────●──────●──────
             ↑                  ↑      ↑
feature  ────┘             ─────┘   ───┘
              (PR → 리뷰 → 머지)

브랜치는 main 하나와 feature/* 브랜치뿐이다. develop, release, hotfix 없다. 모든 변경사항은 feature 브랜치에서 작업하고 PR로 main에 머지한다. main에 머지되면 즉시 배포한다.

PR 기반 워크플로우

# main에서 feature 브랜치 분기
git checkout main
git pull origin main
git checkout -b feature/add-dark-mode

# 작업 → push → PR 생성
git add .
git commit -m "feat: add dark mode toggle"
git push origin feature/add-dark-mode

# GitHub에서 PR 생성
# - 리뷰어 지정
# - CI 파이프라인 통과 확인
# - 리뷰 승인 후 main에 머지

# 머지 후 로컬 정리
git checkout main
git pull origin main
git branch -d feature/add-dark-mode

PR이 핵심이다. 코드 리뷰, CI 통과 여부, 배포 가능 여부를 PR 하나에서 모두 확인한다. main에 머지되는 순간 프로덕션에 나간다는 가정으로 운영한다.

장단점

장점 단점
배우기 쉽고 규칙이 단순 배포 전 QA 기간이 필요한 팀엔 적합하지 않음
CI/CD와 자연스럽게 연결 릴리즈 버전 관리가 따로 없음
빠른 배포 주기 지원 main이 항상 배포 가능 상태여야 한다는 전제 필요
PR 기반 코드 리뷰 문화와 잘 맞음 대규모 팀에서 브랜치 충돌 증가 가능

Trunk-based Development

구조

모든 개발자가 단일 브랜치(main 또는 trunk)에 하루에 한 번 이상 커밋한다. feature 브랜치가 아예 없거나, 있어도 수명이 1~2일을 넘지 않는다.

main    ────●────●────●────●────●────●────
            ↑    ↑    ↑    ↑    ↑    ↑
           (각 개발자가 짧은 간격으로 직접 커밋)

짧은 수명 브랜치를 쓰는 경우:

# 브랜치 생성 후 하루 이내에 머지
git checkout -b short-lived/fix-button-color

# 당일 작업 완료 → main에 머지
git checkout main
git merge short-lived/fix-button-color
git branch -d short-lived/fix-button-color

Feature Flag

완성되지 않은 기능을 main에 올리면서 사용자에게 노출되지 않도록 하는 기법이다.

// 서버 또는 환경변수로 플래그 관리
const FEATURE_FLAGS = {
  newCheckoutFlow: process.env.FEATURE_NEW_CHECKOUT === 'true',
  darkMode: process.env.FEATURE_DARK_MODE === 'true',
};

// 컴포넌트에서 사용
function App() {
  return (
    <div>
      {FEATURE_FLAGS.newCheckoutFlow ? (
        <NewCheckoutFlow />
      ) : (
        <LegacyCheckoutFlow />
      )}
    </div>
  );
}

미완성 코드도 main에 올릴 수 있다. 플래그를 false로 두면 사용자에게 보이지 않는다. 기능이 완성되면 플래그를 켠다. 일정 시간 후 플래그 코드를 정리(cleanup)한다.

장단점

장점 단점
머지 충돌이 거의 없음 (자주 통합하므로) Feature Flag 관리 비용 발생
CI/CD 파이프라인이 단순해짐 높은 테스트 커버리지 필수
배포 주기가 매우 짧아짐 팀원 모두가 규율을 지켜야 함
Google, Facebook 등 대형 팀에서 검증됨 불완전한 코드가 main에 올라가는 위험 존재

브랜치 네이밍 컨벤션

팀에서 브랜치 이름 규칙을 정해두면 git branch -a 결과를 봤을 때 목적이 바로 읽힌다.

feature/   기능 개발
fix/       버그 수정 (개발 중)
hotfix/    프로덕션 긴급 수정
release/   릴리즈 준비
chore/     빌드, 설정, 문서 등 코드 변경 없는 작업
refactor/  리팩토링

예시

feature/user-profile-edit
feature/payment-kakao-integration
fix/login-redirect-loop
hotfix/cart-total-calculation
release/2.3.0
chore/update-node-18
refactor/auth-middleware

이슈 트래커를 쓰는 경우 이슈 번호를 붙이는 것도 흔하다.

feature/GH-123-user-profile-edit
fix/JIRA-456-login-redirect-loop

커밋 메시지 컨벤션

Conventional Commits 스펙이 업계 표준으로 자리잡았다.

<type>(<scope>): <subject>

<body>  (선택)

<footer>  (선택, Breaking Change, 이슈 참조)

타입 목록

타입 용도
feat 새로운 기능 추가
fix 버그 수정
docs 문서 변경 (코드 변경 없음)
style 포매팅, 세미콜론 누락 등 (로직 변경 없음)
refactor 리팩토링 (기능 추가/버그 수정 아님)
test 테스트 추가/수정
chore 빌드 스크립트, 패키지 매니저 설정 변경
perf 성능 개선
ci CI 설정 변경
revert 커밋 되돌리기

실전 예시

feat(auth): add OAuth2 Google login
fix(cart): prevent duplicate item addition on double click
docs(api): update user endpoint response format
refactor(payment): extract validation logic to separate module
chore: upgrade eslint to v9
test(auth): add unit tests for JWT refresh logic

# Breaking Change
feat(api)!: change user response format

BREAKING CHANGE: user.name is now split into user.firstName and user.lastName

Breaking Change는 !로 표시하거나 footer에 BREAKING CHANGE: 키워드를 쓴다. 이걸 기반으로 자동 버전 번호 업(semantic release)을 연동할 수 있다.


세 가지 전략 비교

항목 Git Flow GitHub Flow Trunk-based
브랜치 수 많음 (5종) 적음 (2종) 최소 (1~2종)
배포 주기 릴리즈 단위 (주/월) 수시 (하루 여러 번 가능) 하루 여러 번
복잡도 높음 낮음 낮음 (but 규율 필요)
팀 규모 중대형 팀 소중형 팀 모든 규모 (특히 대형)
하위 호환 배포 지원 우수 (release 브랜치) 미흡 Feature Flag로 해결
핫픽스 처리 구조화됨 (hotfix 브랜치) main에서 즉시 main에서 즉시
CI/CD 연동 복잡 쉬움 매우 쉬움
코드 리뷰 PR 또는 직접 머지 PR 필수 선택적
추천 상황 패키지/앱 배포, 버전 관리 필요 웹 서비스, 지속적 배포 SaaS, 빠른 이터레이션

실무 추천 조합 (규모별)

1~3인 팀 / 사이드 프로젝트

GitHub Flow + Conventional Commits 조합이 가장 실용적이다. 규칙 최소화, 속도 최대화. main에서 feature 브랜치 분기 → 작업 → PR 없이 직접 머지도 허용.

# 실전 운용
git checkout -b feature/add-search
# ... 작업 ...
git commit -m "feat: add product search with filtering"
git checkout main
git merge feature/add-search

4~10인 팀 / 스타트업

GitHub Flow + PR 필수 + 브랜치 네이밍 컨벤션. PR에 CI(린트+테스트) 통과 필수 조건 추가. Squash merge로 main 히스토리를 깔끔하게 유지.

# PR 머지 시 Squash merge 권장
git merge --squash feature/user-profile-edit
git commit -m "feat(user): add profile edit feature (#42)"

10인 이상 / 정기 릴리즈가 있는 팀

Git Flow 또는 수정된 Git Flow. develop 브랜치를 스테이징 환경으로 자동 배포, main 브랜치를 프로덕션 자동 배포로 CI/CD 구성.

# GitHub Actions 예시
on:
  push:
    branches:
      - develop   # 스테이징 자동 배포
      - main      # 프로덕션 자동 배포

대형 팀 / SaaS / 빠른 배포 필요

Trunk-based Development + Feature Flag + 높은 테스트 커버리지. 처음엔 진입 장벽이 있지만, 제대로 운용되면 머지 충돌이 거의 사라진다.


삽질 기록

long-lived 브랜치 머지 지옥

3주짜리 대형 기능을 feature/new-dashboard에서 혼자 개발했다. 3주 동안 develop에는 다른 팀원들이 수십 개 커밋을 쌓았다.

머지를 시도하자 충돌이 130개. 파일마다 <<<<<<< HEAD가 넘쳐났다. 상대 코드가 어떤 의도로 작성됐는지 알 수 없으니 어떻게 해결해야 할지도 몰랐다.

해결에 하루 반이 걸렸다. 그 과정에서 버그를 두 개 심었고, 그 버그를 잡는 데 또 이틀이 걸렸다.

교훈: feature 브랜치는 2~3일을 넘기지 않는다. 기능이 크다면 더 작은 단위로 나눠서 중간 머지를 자주 한다. 최소한 매일 develop을 rebase한다.

# 매일 아침 feature 브랜치에서
git fetch origin
git rebase origin/develop

hotfix 빠뜨린 케이스

긴급 버그를 hotfix/fix-login으로 수정해서 main에 머지하고 배포했다. 그런데 develop에 머지하는 걸 빠뜨렸다.

2주 뒤 다음 릴리즈를 준비하면서 release 브랜치를 만들어 develop에서 분기했을 때, 이미 main에 있는 버그 수정이 develop에는 없었다. 버그가 다시 살아났다. QA에서 발견해서 다행이었지, 그대로 릴리즈됐다면 프로덕션에 나갔을 것이다.

Git Flow에서 hotfix는 반드시 maindevelop 양쪽에 머지해야 한다는 규칙이 있는 이유가 이것이다. 체크리스트를 만들어두는 것이 낫다.

hotfix 체크리스트:
[ ] main에 머지
[ ] 버전 태그 생성 (v1.x.y)
[ ] develop에도 머지
[ ] 릴리즈 노트 작성
[ ] 배포 확인

브랜치 네이밍 충돌

브랜치 이름 규칙 없이 쓰다 보니 feature/userfeature/users가 동시에 존재한 적이 있다. 서로 다른 팀원이 만든 브랜치였고, 이름이 비슷해서 checkout 명령을 잘못 치는 일이 반복됐다.

더 큰 문제는 fix/authfix/auth-v2가 동시에 존재했던 케이스다. 어느 게 최신인지, 어느 게 머지됐는지 아무도 몰랐다.

브랜치 네이밍에 이슈 번호를 포함시키는 것만으로도 이런 혼선이 사라진다.

# 이슈 번호 포함
feature/123-user-profile-edit   # GitHub Issue #123
fix/456-auth-token-expiry       # Issue #456

같은 기능을 두 사람이 작업할 일이 없어지고, 브랜치를 보면 어느 이슈의 작업인지 바로 추적된다.

브랜치 전략을 도중에 바꾼 케이스

GitHub Flow로 시작한 프로젝트가 팀이 커지면서 배포 주기를 주 1회로 조정해야 했다. 그때 Git Flow로 전환을 시도했는데, 기존 히스토리에 develop 브랜치가 없어서 분기 시점을 어디로 잡을지 혼란이 생겼다.

전략 전환은 새 스프린트 시작 시점에, 팀 전원이 동일한 커밋을 베이스로 두고 시작하는 게 깔끔하다. 전환 전에 모든 feature 브랜치가 머지되고 깨끗한 상태에서 시작해야 한다.


마무리

브랜치 전략에 정답은 없다. 팀 규모, 배포 주기, 제품 특성에 따라 다르다.

가장 중요한 건 팀이 합의하고 일관되게 지키는 것이다. 완벽한 전략을 세우는 것보다, 단순하더라도 모두가 지키는 전략이 훨씬 낫다.

처음 팀 프로젝트를 시작하면서 정해두면 좋은 것들:

  • 어떤 브랜치 전략을 쓸지 (GitHub Flow 추천, 소규모)
  • 브랜치 네이밍 규칙
  • 커밋 메시지 컨벤션 (Conventional Commits)
  • PR 머지 방식 (Merge commit / Squash / Rebase 중 통일)
  • main 브랜치 보호 규칙 (직접 push 금지, CI 통과 필수)

이것들이 문서 한 장으로 정리되면, 신규 팀원이 합류했을 때 온보딩 비용이 크게 줄어든다.


기술 스택: Git, GitHub, GitHub Actions, Conventional Commits

반응형