main
프로덕션에 배포된 코드와 릴리즈 태그가 남는 영구 브랜치다.
운영 장애 시 마지막 정상 버전과 rollback 기준이 필요할 때.분류: Layer 5 - 플랫폼 엔지니어링 & 자동화
브랜치 전략은 팀이 코드를 어디에서 나누고, 어떤 검증을 거쳐, 언제 기본 브랜치에 합칠지 정한 협업 규칙이다. 이름 규칙이 아니라 충돌, 리뷰, 배포, 롤백의 경계를 설계하는 방법이다.
Git을 혼자 쓸 때는 브랜치가 “작업 공간”처럼 보인다. 팀에서 쓰기 시작하면 브랜치는 “누가 어떤 위험을 언제 흡수할 것인가”를 정하는 경계가 된다. Git Flow, GitHub Flow, Trunk-Based Development는 이 경계를 서로 다르게 둔다.
브랜치 전략 문서는 용어가 비슷해서 처음 읽을 때 헷갈리기 쉽다. 아래 용어를 먼저 잡고 뒤 절을 읽으면 세 전략의 차이가 명확해진다.
| 용어 | 첫 정의 | 왜 필요한가 |
|---|---|---|
| 브랜치 전략 | 브랜치를 만들고 병합하고 배포하는 팀 규칙 | 충돌, 리뷰, 릴리즈 경계를 우연이 아니라 규칙으로 만든다 |
main | 현재 제품의 기준이 되는 기본 브랜치 | 배포, 긴급 복구, 새 작업 시작점의 기준점이다 |
| trunk | TBD에서 부르는 단일 통합 브랜치, 보통 main | 모두가 자주 합류하는 중심선을 강조하는 말이다 |
| feature branch | 기능, 버그 수정, 실험을 위해 만든 짧은 작업 브랜치 | 미완성 변경을 기본 브랜치에서 잠시 분리한다 |
| release branch | 배포 후보를 안정화하기 위해 만든 임시 브랜치 | 새 기능 유입을 막고 QA, 버전 태깅, 마지막 버그 픽스를 모은다 |
| hotfix branch | 운영 장애를 빠르게 고치기 위해 main에서 파생한 브랜치 | 다음 정규 릴리즈를 기다리지 않고 프로덕션을 먼저 복구한다 |
| PR(Pull Request) | 한 브랜치의 변경을 다른 브랜치에 합치자고 요청하는 리뷰 단위 | 코드 리뷰, CI 결과, 토론, 승인 기록이 모이는 경계다 |
| status check | PR 또는 커밋에 붙는 자동 검증 결과 | 테스트, 빌드, 린트가 통과했는지 병합 전에 확인한다 |
| branch protection | 특정 브랜치에 직접 push, 미검증 병합을 막는 규칙 | 팀 규칙을 선의가 아니라 시스템 제약으로 만든다 |
| merge queue | 여러 PR을 최신 main 조합 위에서 순서대로 검증한 뒤 병합하는 큐 | green check가 낡은 base에서 나온 결과일 때 생기는 조합 실패를 줄인다 |
| feature flag | 코드 배포와 사용자 노출을 분리하는 조건 스위치 | 미완성 코드를 main에 넣어도 기능은 OFF로 숨길 수 있다 |
| deploy | 코드를 환경에 배포하는 행위 | 사용자가 볼지와 별개로 실행 가능한 코드가 서버에 올라간다 |
| release | 기능을 사용자에게 노출하는 행위 | flag, 트래픽 가중치, 앱스토어 릴리즈 등으로 통제된다 |
| merge commit | 두 브랜치 이력을 합쳤다는 커밋 | 기능 묶음, 릴리즈 묶음, revert 단위를 남긴다 |
| squash merge | PR의 여러 커밋을 하나로 압축해 병합하는 방식 | main 이력을 읽기 쉽게 만들지만 개별 커밋 맥락은 줄어든다 |
| rebase | 내 커밋을 다른 base 위에 다시 올리는 방식 | 선형 이력을 만들지만 커밋 해시가 바뀐다 |
중요한 구분은 deploy와 release다. GitHub Flow에서는 main에 머지하면 바로 배포되는 팀이 많다. TBD에서는 더 나아가 미완성 코드도 main에 자주 합치되, feature flag로 사용자 노출을 따로 제어한다. Git Flow는 반대로 release/* 브랜치로 배포 후보를 따로 안정화한다.
브랜치 전략은 Git 명령어를 예쁘게 정리하려고 생긴 규칙이 아니다. 팀 개발에서 반복적으로 터지는 세 가지 문제가 배경이다.
첫째, 충돌 경계가 없다. 개발자 5명이 같은 main에 직접 push하면 충돌은 빨리 드러날 수 있지만, 어떤 변경이 어떤 기능에 속하는지, 어떤 변경을 되돌려야 하는지 알기 어렵다. 결제 기능이 반쯤 들어간 상태에서 인증 수정과 긴급 버그 픽스가 섞이면 커밋 그래프는 남아도 의사결정 경계가 사라진다.
둘째, 리뷰와 검증 경계가 없다. CI/CD 문서에서 본 것처럼 자동화 테스트는 빠른 피드백을 주지만, 어떤 변경이 테스트 대상인지 정해져야 한다. PR이 없거나 branch protection이 없으면 “테스트가 실패했지만 급하니까 머지”가 가능해지고, 그 순간 status check는 신뢰 신호가 아니라 참고 메모가 된다.
셋째, 릴리즈 경계가 없다. 웹 서비스처럼 언제든 배포 가능한 제품은 main을 곧 배포 후보로 둘 수 있다. 반대로 모바일 앱, SDK, 고객사별 설치형 제품처럼 여러 버전을 동시에 지원해야 하면 “이번 릴리즈에 포함되는 것”과 “다음 릴리즈로 넘어가는 것”을 분리해야 한다.
Git은 브랜치 생성과 병합 비용을 낮췄다. 하지만 브랜치를 쉽게 만들 수 있다는 사실만으로 협업 질서가 생기지는 않는다. Git Flow는 main, develop, release, hotfix 역할을 나눠 릴리즈 경계를 강하게 만든다. GitHub Flow는 PR, status check, branch protection으로 main 품질 경계를 세운다. Trunk-Based Development(TBD)는 브랜치 수명을 극단적으로 줄이고 feature flag로 배포와 노출을 분리한다.
세 전략은 모양이 다르지만 같은 질문에 답한다.
Git Flow는 격리를 길게 가져간다. 기능은 feature/*, 다음 릴리즈 후보는 develop, 안정화는 release/*, 운영 긴급 패치는 hotfix/*로 나눈다. 통합 비용은 늦게 오지만, 릴리즈 경계가 명확하다.
GitHub Flow는 격리를 짧게 가져간다. 모든 작업은 main에서 짧은 feature branch로 갈라졌다가 PR 검증을 거쳐 main으로 돌아온다. 릴리즈 브랜치를 기본으로 두지 않기 때문에 main이 항상 배포 가능해야 한다.
TBD는 격리를 더 짧게 가져간다. 모든 개발자가 trunk에 자주 합친다. 미완성 기능은 브랜치가 아니라 feature flag로 숨긴다. 통합 비용을 거의 매일 지불하는 대신, 긴 통합 단계와 대형 충돌을 피한다.
브랜치 전략을 고르는 일은 “어떤 이름이 더 표준인가”가 아니다. 우리 팀이 통합 비용을 초반에 자주 낼 수 있는지, 아니면 릴리즈 시점에 늦게 크게 내는 편이 맞는지 정하는 일이다.
Git Flow는 main과 develop이라는 두 장기 브랜치를 두고, 기능 개발·릴리즈 준비·긴급 패치를 각각 다른 임시 브랜치로 처리하는 전략이다. Vincent Driessen의 “A successful Git branching model”에서 널리 알려졌고, 원문도 Git 이전의 branch/merge가 두려운 작업으로 취급되던 맥락을 설명한다.
프로덕션에 배포된 코드와 릴리즈 태그가 남는 영구 브랜치다.
운영 장애 시 마지막 정상 버전과 rollback 기준이 필요할 때.다음 릴리즈 후보가 될 기능들이 모이는 통합 개발 브랜치다.
새 기능은 계속 합치되 production 배포와는 분리해야 할 때.개별 기능이나 수정 작업을 위해 develop에서 파생하는 짧은 브랜치다.
기능 개발 중 develop과 production 경계를 건드리지 않으려 할 때.QA, 버전 태깅, 배포 전 버그 픽스만 담는 임시 안정화 브랜치다.
이번 버전에 들어갈 범위를 얼리고 새 기능 유입을 막아야 할 때.프로덕션 긴급 패치를 main에서 파생해 main과 develop에 모두 되돌려 넣는 브랜치다.
운영 장애를 먼저 복구하고 다음 릴리즈에서도 같은 버그가 재발하지 않게 해야 할 때.Git Flow의 핵심은 develop에서 계속 기능을 모으다가, 특정 시점에 release/1.4.0 같은 브랜치를 잘라 “이번 배포 기차”를 만든다는 점이다.
main A───────────────M1──────────────M2 ╲ ↑ ↑develop D0──F1──F2────R1────F3──F4────H' ╲ ╲ ╲feature/login L1──L2 ╲feature/billing B1──B2release/1.4.0 r1──r2hotfix/pay-crash h1이 그래프에서 release/1.4.0이 만들어진 뒤 develop에는 F3, F4가 계속 들어올 수 있다. 하지만 release/1.4.0에는 QA 중 발견된 r1, r2 같은 안정화 변경만 들어간다. 배포가 끝나면 release branch는 main에 병합되고 태그가 붙는다. release 중 발견된 수정은 develop에도 다시 반영해야 다음 릴리즈에서 사라지지 않는다.
상황을 작게 만들어보자.
main에는 v1.0.0이 배포되어 있다.develop에는 다음 릴리즈용 결제 리팩터링이 들어가 있다.hotfix/payment-null을 main에서 만든다.main에만 병합하고 develop에는 반영하지 않는다.develop -> release -> main 흐름으로 v1.1.0을 배포한다.이때 v1.1.0은 develop에서 나온다. develop에 hotfix가 없었다면 v1.0.1에서 고친 버그가 v1.1.0에서 다시 나타난다. Git Flow의 hotfix 규칙은 형식이 아니라 “장기 브랜치가 두 개라서 같은 패치를 두 흐름에 모두 넣어야 한다”는 구조적 필요에서 나온다.
--no-ff가 남기는 경계--no-ff는 no fast-forward merge를 뜻한다. fast-forward는 Git이 별도 merge commit 없이 브랜치 포인터만 앞으로 옮기는 병합이다. --no-ff는 fast-forward가 가능한 상황에서도 merge commit을 만들어 “이 기능 묶음이 여기서 들어왔다”는 경계를 남긴다.
Fast-forward:main: A──B──C──D ↑ feature 커밋이 main에 직접 이어붙는다
No-fast-forward:main: A────────M ╲ ╱feature: B──C──D ↑ M이 기능 묶음의 경계가 된다Git Flow에서 이 경계는 유용하다. release에 어떤 feature 묶음이 들어갔는지 감사할 수 있고, 특정 기능 전체를 되돌릴 때 merge commit 하나를 기준으로 삼을 수 있다. 반대로 작은 PR이 매우 많은 웹 서비스에서 모든 PR을 merge commit으로 남기면 main 이력이 장황해질 수 있다. 그래서 GitHub Flow 팀은 squash merge를 기본으로 고르는 경우가 많다.
Git Flow는 긴 릴리즈 기차가 실제로 필요한 팀에서 빛난다.
| 조건 | Git Flow가 유리한 이유 |
|---|---|
| 모바일 앱, SDK, 설치형 제품처럼 여러 버전이 동시에 살아 있다 | release/*, hotfix/*, tag가 버전별 지원 경계를 만든다 |
| QA 단계가 별도 조직/일정으로 운영된다 | release branch가 새 기능 유입을 막고 안정화 변경만 받는다 |
| 배포 주기가 주간, 격주, 월간처럼 묶여 있다 | ”이번 기차에 타는 변경”을 명확히 정할 수 있다 |
| 고객사별 패치나 장기 지원 브랜치가 필요하다 | main/tag/release branch가 감사와 지원 기준이 된다 |
Git Flow가 안정적이라는 말은 충돌이 적다는 뜻이 아니다. 오히려 feature branch나 release branch가 길어지면 통합 비용은 뒤로 밀린다. Git Flow의 장점은 충돌을 없애는 것이 아니라 릴리즈 범위와 긴급 패치 경계를 명확히 하는 데 있다.
Git Flow 원저자도 2020년 업데이트에서 웹 애플리케이션처럼 지속 배포가 자연스러운 제품에는 Git Flow가 적합하지 않을 수 있다고 설명했다. 이 문장은 “Git Flow가 낡았다”가 아니라 “제품의 릴리즈 모델이 다르면 브랜치 전략도 달라져야 한다”로 읽어야 한다.
develop과 차이가 계속 커진다.main에만 넣고 develop이나 다음 release branch에 되돌리지 않는다.develop이 자주 깨져 feature branch의 시작점으로 신뢰받지 못한다.--no-ff merge commit은 남지만 어떤 기능 묶음인지 PR 설명과 태그가 부실하다.GitHub Flow는 main과 짧은 feature branch만 두는 단순한 전략이다. 모든 작업은 main에서 브랜치를 만들고, PR을 열고, 리뷰와 status check를 통과한 뒤 main으로 병합한다. 별도의 develop이나 release/* 브랜치를 기본으로 두지 않는다.
main A──────────────M1──────────────M2 ╲ ╱ ╲ ╱feature/search s1──s2────── feature/pay p1 PR + CI PR + CI이 전략의 핵심 문장은 “main은 항상 배포 가능해야 한다”이다. main이 깨져 있으면 모든 feature branch가 깨진 시작점에서 갈라지고, CI 실패가 “원래 실패하는 것”이 된다. 한 번 그렇게 되면 status check는 품질 경계로 작동하지 않는다.
PR(Pull Request)은 코드 리뷰 UI 이상의 의미를 가진다. PR은 한 번에 검증할 변경 단위다. PR이 너무 크면 리뷰어는 의도를 놓치고, CI가 실패해도 어느 변경이 원인인지 찾기 어렵다. PR이 너무 작아도 여러 PR이 서로 의존하면서 merge 순서 문제가 생긴다.
좋은 PR 단위는 “리뷰어가 한 번에 의도와 위험을 설명할 수 있는 변경”이다. 예를 들어 상품 검색 API 추가는 검색 endpoint, service, test를 포함할 수 있다. 하지만 검색 API + 결제 리팩터링 + Dockerfile 변경은 서로 다른 실패 원인을 한 PR에 묶는다.
status check는 CI가 PR 또는 commit에 붙이는 결과다. 예를 들어 ci/test, ci/build, lint, typecheck가 성공해야 병합할 수 있게 만들 수 있다. branch protection은 이 결과를 병합 조건으로 강제한다.
| 보호 규칙 | 막는 실패 | 학습 포인트 |
|---|---|---|
| Require pull request before merging | main 직접 push | 리뷰와 CI가 우회되지 않는다 |
| Require approvals | 혼자 승인하고 병합 | 최소 한 명이 의도를 검토한다 |
| Require status checks to pass | 깨진 빌드/테스트 병합 | 자동 검증이 병합 게이트가 된다 |
| Require conversation resolution | 미해결 리뷰 코멘트 방치 | 토론의 끝을 명확히 한다 |
| Require linear history | merge commit 난립 | squash/rebase 중심 이력을 강제한다 |
| Do not allow bypassing | 관리자 예외 남용 | 긴급 상황에서도 규칙 우회가 보편화되지 않는다 |
GitHub Flow에서 자주 놓치는 실패가 있다. PR A와 PR B가 모두 같은 main 커밋 위에서 CI를 통과했다고 하자. PR A가 먼저 병합되면 PR B의 green check는 낡은 base에서 나온 결과다. strict status check가 꺼져 있거나 merge queue가 없으면 PR B는 최신 main + B 조합으로 다시 검증되지 않은 채 병합될 수 있다.
시간 1: main = APR-1 = A + 검색 변경 CI PASSPR-2 = A + 인증 변경 CI PASS
시간 2: PR-1 병합main = A + 검색 변경
시간 3: PR-2 병합검증된 조합: A + 인증 변경실제 배포 조합: A + 검색 변경 + 인증 변경두 PR이 서로 다른 파일을 바꾸면 보통 괜찮다. 하지만 검색 endpoint가 인증 middleware 이름에 의존한다면 main에서만 실패한다. strict mode는 PR을 최신 base로 업데이트한 뒤 다시 CI를 돌리게 하고, merge queue는 여러 PR을 순서대로 최신 조합에서 검증한다.
PR이 하루에 몇 개 안 들어오는 저장소라면 Update branch 후 재검증으로 충분할 수 있다. PR이 하루 수십 개씩 들어오거나 같은 모듈을 자주 건드린다면 merge queue가 더 안전하다. 숫자는 절대 기준이 아니라 “동시에 열린 PR이 서로 자주 겹치는가”를 보는 관찰 기준이다.
| 조건 | 이유 |
|---|---|
SaaS, 웹 서비스처럼 main에서 자주 배포한다 | 별도 release branch보다 PR 검증 경계가 단순하다 |
| 팀 규모가 작거나 중간이고 PR 리뷰 문화가 있다 | 복잡한 브랜치 교육 없이 품질 경계를 만들 수 있다 |
| CI/CD가 PR마다 빠르게 돈다 | main 보호 규칙이 실제 피드백으로 작동한다 |
| 여러 구버전을 장기 지원하지 않는다 | release/hotfix 브랜치 복잡도를 줄일 수 있다 |
main이 깨져 있는데도 “나중에 고치자”며 새 PR을 계속 병합한다.Trunk-Based Development(TBD)는 모든 개발자가 하나의 trunk, 보통 main에 매우 자주 통합하는 전략이다. 브랜치를 만들 수는 있지만 수명이 짧아야 한다. 미완성 기능은 오래 숨겨둔 브랜치가 아니라 feature flag로 감춘다.
trunk/main A──B──C──D──E──F──G──H ↑ ↑ ↑ ↑ ↑ 작고 자주 합쳐지는 변경TBD의 철학은 “통합은 고통스럽기 때문에 미루지 않는다”다. 통합을 2주 뒤로 미루면 2주치 변경과 한 번에 맞춰야 한다. 통합을 하루에 여러 번 하면 작은 차이를 계속 흡수한다. 충돌이 사라지는 것이 아니라 충돌 하나하나가 작아진다.
충돌 확률을 정확히 계산할 수는 없지만, 감각은 만들 수 있다. 팀이 하루에 main에 10개 커밋을 넣고, 한 feature branch가 10일 동안 열려 있다고 하자. 병합 시점에는 최대 100개 커밋이 같은 코드베이스에 먼저 들어와 있다. 같은 branch가 1일만 살아 있으면 비교해야 할 변경은 대략 10개 커밋이다.
long-lived branch:main: A──1──2──3──4──5──...──100feature: ╲────────────────────────F 병합 시 100개 변경과 한 번에 맞춘다
short-lived branch:main: A──1──2──3──4──5──...──100 ╲─F 병합 시 최근 몇 개 변경과 맞춘다DORA의 trunk-based development capability 문서는 활성 브랜치 수를 적게 유지하고 trunk에 자주 병합하는 관점을 제시한다. TBD 공식 사이트도 short-lived feature branch를 강조한다. 이 수치들은 “항상 오늘의 최신 정답”이라기보다 브랜치 전략을 측정 가능한 운영 습관으로 바꾸는 참고 기준으로 읽는 편이 안전하다.
실무에서 관찰할 만한 기준은 다음과 같다.
| 신호 | 읽는 법 |
|---|---|
| 평균 PR open -> merge 시간이 2일을 자주 넘는다 | TBD보다는 GitHub Flow의 단기 feature branch에 가깝다 |
| 동시에 오래 열린 feature branch가 3개를 계속 넘는다 | trunk와 분기들의 차이가 커지고 있다 |
| 하루에 한 번도 trunk/main에 합치지 못하는 날이 반복된다 | 통합 비용을 매일 흡수한다는 TBD 리듬이 깨지고 있다 |
| 같은 파일 충돌이 PR마다 반복된다 | 작업 쪼개기나 소유권 경계가 불분명하다 |
| CI가 10분 이상 걸려 피드백을 기다리지 않는다 | 자주 통합할 동기가 사라진다 |
TBD는 미완성 기능도 main에 넣을 수 있어야 한다. 그런데 미완성 기능이 사용자에게 노출되면 안 된다. feature flag는 이 간극을 메운다.
코드 통합 코드 배포 기능 출시main merge -> production deploy -> flag ON ↑ ↑ ↑ 통합 단위 실행 환경 사용자 노출작은 예를 보자.
async function searchProducts(query: string, userId: string) { if (await flags.enabled("new-search", userId)) { return searchV2(query); }
return searchV1(query);}이 코드는 searchV2가 완성되기 전에도 main에 들어갈 수 있다. 기본 flag가 OFF라면 사용자 대부분은 여전히 searchV1을 쓴다. 일부 내부 사용자에게만 1%, 10%, 50%, 100%처럼 노출 비율을 올릴 수 있다. 롤백도 code revert가 아니라 flag OFF로 시작한다.
feature flag는 모두 같은 목적이 아니다.
| flag 유형 | 목적 | 수명 |
|---|---|---|
| Release toggle | 미완성 기능을 숨기고 점진 노출한다 | 짧아야 한다 |
| Experiment toggle | A/B 실험으로 사용자 반응을 비교한다 | 실험 기간만 유지한다 |
| Ops toggle | 장애 시 특정 기능을 끄거나 우회한다 | 운영 정책에 따라 오래 갈 수 있다 |
| Permission toggle | 특정 고객/권한에 기능을 제공한다 | 제품 권한 모델의 일부가 될 수 있다 |
TBD에서 특히 위험한 것은 release toggle 방치다. 기능 출시가 끝났는데도 release toggle이 남으면 코드 경로가 둘로 유지된다. release toggle 10개가 독립적으로 남아 있으면 이론상 조합은 2의 10승, 즉 1024가지다. 모든 조합을 테스트하지 못하므로 “어떤 고객 조합에서만 깨지는 버그”가 생긴다.
| 조건 | 이유 |
|---|---|
| CI가 빠르고 모든 commit/PR에서 자동 실행된다 | 자주 통합해도 깨진 코드를 빠르게 잡는다 |
| 핵심 경로 테스트가 신뢰된다 | main을 항상 배포 가능하게 유지한다 |
| feature flag 인프라와 정리 루틴이 있다 | 미완성 코드 노출과 flag 부채를 통제한다 |
| PR/commit 단위가 작다 | 통합 비용을 작은 단위로 계속 지불한다 |
| 배포와 출시를 분리할 수 있다 | deploy는 자주, release는 통제해서 할 수 있다 |
팀이 “빠르게 가자”며 develop을 없애고 모두 main에 직접 머지한다고 하자. 그런데 CI가 모든 커밋에서 돌지 않고, feature flag도 없다. 그러면 미완성 결제 v2 코드가 main에 들어간 순간 사용자에게 노출된다. 다음 장애는 브랜치 전략 문제가 아니라 검증과 노출 제어가 없는 배포 문제가 된다.
TBD는 branch를 줄이는 전략이 아니라 자동 검증과 노출 제어를 전제로 통합을 자주 하는 전략이다. 전제가 없으면 “짧은 브랜치”가 아니라 “깨진 main”만 남는다.
main이 자주 깨지는데 원인 commit을 빨리 찾지 못한다.브랜치 전략과 병합 방식은 별개지만 서로 강하게 영향을 준다. Git Flow는 --no-ff merge commit으로 기능 경계를 남기는 쪽이 자연스럽다. GitHub Flow는 squash merge로 main 이력을 읽기 쉽게 만드는 경우가 많다. rebase는 개인 브랜치 정리에는 좋지만 공유 브랜치에는 위험하다.
시작 상태:main: 1──2feature: ╲──A──B──CMerge commit:main: 1──2────────M ╲ ╱feature: A──B──C
남는 것: A, B, C, M좋은 점: 기능 묶음 경계와 원본 이력이 남는다.주의점: 작은 PR이 많으면 main 그래프가 복잡해진다.Squash merge:main: 1──2──ABC
남는 것: ABC 하나좋은 점: main 이력이 PR 단위로 깔끔하다.주의점: feature branch의 개별 commit 해시가 main에 남지 않는다.Rebase:main: 1──2──A'──B'──C'
남는 것: 재작성된 A', B', C'좋은 점: 선형 이력이 된다.주의점: commit hash가 바뀌므로 공유 브랜치에 쓰면 다른 사람 이력을 흔든다.| 방식 | 쓰기 좋은 경우 | 피해야 할 경우 |
|---|---|---|
| Merge commit | release branch, 오래 유지된 브랜치, 감사 추적이 중요한 기능 묶음 | 작은 PR이 매우 많고 main 이력을 PR 단위로만 읽고 싶을 때 |
| Squash merge | feature branch를 병합 후 삭제하고 PR 단위로 히스토리를 보고 싶을 때 | 같은 branch를 계속 재사용하거나 commit 단위 bisect가 중요할 때 |
| Rebase | 아직 공유하지 않은 개인 branch를 최신 main 위로 정리할 때 | 이미 원격에 공유되어 다른 사람이 기반으로 삼은 branch |
feature branch에 A, B, C 세 commit이 있고, GitHub에서 squash merge로 ABC 한 commit을 main에 넣었다고 하자. feature branch에는 여전히 A, B, C가 남아 있다. 이후 같은 branch에서 D를 추가하고 다시 PR을 열면 Git은 A/B/C와 ABC를 같은 변경이라고 확신하지 못한다. 이미 들어간 변경이 다시 diff로 보이거나 충돌이 생긴다.
그래서 squash merge를 기본으로 쓰는 팀은 병합된 feature branch를 삭제하고 새 작업은 최신 main에서 새 branch로 시작한다. 이것은 취향이 아니라 Git 이력 모델의 결과다.
main, develop, feature, release, hotfix를 분리해 버전 릴리즈와 긴급 패치를 명확히 관리한다.
모바일 앱, SDK, 설치형 제품, 장기 지원 브랜치처럼 여러 버전을 동시에 지원할 때.main과 짧은 feature branch, PR 리뷰, status check, branch protection으로 운영한다.
SaaS나 웹서비스에서 PR 완료마다 배포하고 싶은 소~중규모 팀의 기본값.trunk에 자주 통합하고 미완성 기능은 feature flag로 숨긴다.
자동화 테스트와 flag 인프라가 충분하고 배포 빈도를 극대화해야 할 때.flowchart TD
Start["전략 선택"] --> MultiVersion{"여러 버전 동시 지원?"}
MultiVersion -->|yes| GitFlow["Git Flow"]
MultiVersion -->|no| ReleaseTrain{"릴리즈 기차가 필요한가?"}
ReleaseTrain -->|yes| GitFlow
ReleaseTrain -->|no| MainDeploy{"main을 항상 배포 가능하게 유지할 수 있는가?"}
MainDeploy -->|no| GitHubFlow["GitHub Flow부터 정비"]
MainDeploy -->|yes| Automation{"빠른 CI와 feature flag가 충분한가?"}
Automation -->|yes| TBD["Trunk-Based Development"]
Automation -->|no| GitHubFlow
GitHubFlow --> Guard["PR + status check + branch protection"]
TBD --> Flag["짧은 branch + flag 정리 루틴"] | 질문 | 예라면 | 아니라면 |
|---|---|---|
| 여러 버전을 동시에 지원하는가? | Git Flow 후보 | GitHub Flow/TBD 후보 |
main이 언제든 배포 가능해야 하는가? | GitHub Flow/TBD 후보 | Git Flow나 release branch 필요 |
| CI 결과를 10분 안팎으로 받을 수 있는가? | 짧은 PR 유지 가능 | TBD 전환은 이르다 |
| 미완성 기능을 flag로 안전하게 숨길 수 있는가? | TBD 후보 | GitHub Flow 기본값이 안전 |
| hotfix가 다음 정규 릴리즈에도 반드시 들어가야 하는가? | Git Flow의 되돌림 규칙 필요 | 단순 PR hotfix로 충분할 수 있다 |
| PR이 평균 2일 넘게 열려 있는가? | long-lived branch 위험 | 짧은 통합 리듬 유지 중 |
개발자 4명, ECS 배포 주 3~5회, 구버전 장기 지원이 없는 운영 도구를 가정하면 기본값은 GitHub Flow가 현실적이다. main 보호 규칙, PR squash merge, staging 자동 배포, production 수동 승인 정도로 시작하면 브랜치 모델은 단순하고 품질 경계는 분명하다.
이후 다음 신호가 쌓이면 TBD로 좁힐 수 있다.
반대로 고객사별 설치형 버전, 장기 지원 브랜치, 월간 QA 릴리즈가 생기면 Git Flow의 release/*와 hotfix/* 분리가 다시 비용보다 이득이 커질 수 있다.
브랜치 전략은 표로만 보면 “팀 취향”처럼 보인다. 실제 판단은 작은 상황을 놓고 어디에서 위험을 흡수할지 보는 방식이 더 좋다.
상품 검색 기능을 추가한다고 하자. 필요한 변경은 API endpoint, 검색 service, 인덱스 쿼리, 테스트 네 가지다.
| 전략 | 작업 흐름 | 위험을 흡수하는 지점 |
|---|---|---|
| Git Flow | develop에서 feature/search 생성 -> develop 병합 -> 다음 release/*에 포함 | release branch에서 QA와 버그 픽스를 흡수한다 |
| GitHub Flow | main에서 feature/search 생성 -> PR -> CI/status check -> main 병합 | PR과 branch protection에서 검증을 흡수한다 |
| TBD | 작은 commit/PR을 trunk에 자주 합침 -> new-search flag OFF -> 점진 rollout | CI와 feature flag가 통합/노출 위험을 나눠 흡수한다 |
같은 기능이어도 위험의 위치가 다르다. Git Flow는 “이번 릴리즈에 포함할지”가 중요한 질문이다. GitHub Flow는 “이 PR이 main을 깨지 않는지”가 중요하다. TBD는 “이 코드를 지금 trunk에 넣어도 사용자에게 노출되지 않는지”가 중요하다.
PR 크기에 절대 정답은 없지만, 리뷰 신뢰도는 변경량과 범위가 커질수록 빠르게 떨어진다. 40줄 변경은 리뷰어가 의도와 부작용을 머릿속에 유지하기 쉽다. 400줄 변경은 테스트가 있어도 “무엇이 위험한지”를 놓치기 쉽다. 2000줄 변경은 리뷰가 승인 절차로 변할 가능성이 크다.
다음 표는 규칙이 아니라 읽는 감각이다.
| PR 상태 | 리뷰어가 볼 수 있는 것 | 위험 |
|---|---|---|
| 1개 모듈, 50줄 안팎 | 의도, edge case, 테스트 누락 | 낮음 |
| 3개 모듈, 300줄 안팎 | 설계 의도는 보이나 세부 부작용은 놓칠 수 있음 | 중간 |
| 여러 계층, 1000줄 이상 | 리뷰어가 흐름을 재구성해야 함 | 높음 |
| migration + business logic + 배포 설정 동시 변경 | 실패 원인이 섞임 | 매우 높음 |
GitHub Flow나 TBD에서 큰 PR이 반복되면 전략은 짧은 branch를 쓰는 것처럼 보여도 실제로는 long-lived integration을 하고 있는 셈이다. 반대로 Git Flow에서도 feature branch를 작은 PR 단위로 develop에 자주 합치면 통합 위험이 줄어든다.
Git Flow 팀이 release/2.0.0을 만들었다. 원래 목적은 QA 중 발견된 버그만 고치는 것이었다. 그런데 배포가 밀리자 팀은 “이미 release branch가 있으니 여기에 넣자”며 새 기능을 계속 추가한다.
release/2.0.0 시작: 검색 v2, 결제 안정화3일 뒤: 관리자 통계 기능 추가5일 뒤: 로그인 UI 변경 추가8일 뒤: 검색 v2 버그 픽스와 통계 기능 버그가 섞임이 순간 release branch는 안정화 브랜치가 아니라 두 번째 develop이 된다. Git Flow의 장점인 “이번 릴리즈 범위 고정”이 사라지고, release와 develop의 차이는 계속 커진다. 해결책은 명령어가 아니라 scope discipline이다. release branch가 잘린 뒤에는 새 기능을 다음 release로 미루고, 현재 release에는 blocker와 regression만 넣는다.
branch protection이 있다고 해서 main이 자동으로 안전해지는 것은 아니다. 다음 상황을 보자.
ci/test 하나뿐이다.UserDto 필드명을 바꾸고 ci/test를 통과한다.ci/test를 통과한다.겉으로는 “보호 규칙이 있었다”. 실제로는 필요한 검증이 status check에 포함되지 않았고, 최신 조합 검증도 없었다. branch protection의 품질은 체크박스 수가 아니라 “어떤 실패를 병합 전에 잡는가”로 판단해야 한다.
TBD에서 new-search release toggle을 만든다고 하자. 좋은 flag는 생성, rollout, 삭제가 한 흐름으로 설계된다.
| 시점 | 상태 | 코드/운영 의미 |
|---|---|---|
| Day 0 | new-search 기본 OFF | 미완성 코드가 main에 들어가도 사용자에게 보이지 않는다 |
| Day 3 | 내부 사용자 1% | 오류율, latency, 검색 품질을 작은 범위에서 본다 |
| Day 7 | 50% | 기존 경로와 새 경로를 비교한다 |
| Day 10 | 100% | fallback이 필요한지 마지막으로 확인한다 |
| Day 14 | flag와 legacy path 삭제 | release toggle을 제품 코드에서 제거한다 |
삭제가 빠지면 Day 14 이후에도 두 코드 경로가 남는다. 다음 기능이 또 flag를 추가하면 경로 조합이 늘어난다. flag는 TBD를 가능하게 하는 장치이지만, cleanup이 없으면 장기 branch를 코드 안으로 옮겨놓은 것과 비슷해진다.
전략 전환은 “우리도 TBD 하자” 같은 선언으로 시작하면 위험하다. 현재 지표를 보고 어떤 병목을 줄일지 정해야 한다.
| 현재 관찰 | 해석 | 전환 방향 |
|---|---|---|
| PR 평균 수명 4일, 충돌 PR 20% | branch가 오래 살아 통합 비용이 크다 | PR 쪼개기와 GitHub Flow 정비가 먼저 |
| main 실패가 주 3회 이상 | main 품질 경계가 약하다 | branch protection/status check 강화 |
| release branch가 매번 2주 이상 유지 | release scope가 크거나 QA 병목이 있다 | release scope 축소 또는 배포 자동화 개선 |
| 배포는 자주 하지만 기능 노출은 조심스럽다 | deploy와 release 분리가 필요하다 | feature flag 도입 후 TBD 일부 적용 |
| hotfix가 다음 버전에서 재발 | 장기 브랜치 간 되돌림 누락 | Git Flow hotfix merge 규칙 강화 |
이 지표들은 외부 보고서의 숫자를 그대로 맞추기 위한 것이 아니다. 팀이 통합 비용을 어디서 내고 있는지 찾기 위한 관찰 도구다.
main을 배포 가능하게 지키는 PR 중심 기본값이 필요하면 쓴다.”반대로 피해야 하는 조건도 한 문장으로 외울 수 있다.
main이 자주 깨지고 status check가 우회된다.| 증상 | 연결되는 개념 | 왜 문제인가 | 첫 확인 |
|---|---|---|---|
| PR마다 충돌이 크고 리뷰가 늦어진다 | long-lived feature branch | 통합 비용을 뒤로 미루고 있다 | branch 나이와 PR open 시간을 본다 |
main CI가 늘 실패한다 | main always deployable 붕괴 | 실패가 정상 상태가 되어 status check 신뢰가 사라진다 | 최근 main 실패 commit과 bypass merge를 본다 |
| hotfix가 다음 release에서 사라진다 | Git Flow 이중 장기 브랜치 | main 패치가 develop에 되돌아가지 않았다 | hotfix merge 대상이 main/develop 둘 다인지 본다 |
| green check였는데 병합 후 main이 깨진다 | strict status / merge queue 부재 | 낡은 base에서 검증한 PR이 최신 조합에서 실패했다 | PR base sha와 병합 직전 재검증 여부를 본다 |
| squash merge 후 같은 branch에서 충돌이 반복된다 | squash와 branch 재사용 | ABC와 A/B/C를 Git이 같은 이력으로 보지 않는다 | 병합 후 branch 삭제 여부를 본다 |
| flag가 너무 많아 테스트 조합을 모른다 | release toggle cleanup 부채 | 코드 경로가 조합 폭발을 일으킨다 | 100% rollout flag와 owner/removal date를 본다 |
| TBD 도입 후 main이 자주 깨진다 | 전제 없는 TBD | CI/flag/test 없이 branch만 줄였다 | 모든 commit 검증과 flag 기본값을 본다 |
이 표의 목적은 즉시 명령어를 외우는 것이 아니다. 증상을 보면 어떤 브랜치 전략 원리가 깨졌는지 먼저 연결하는 것이다. 명령어는 그 다음이다.
브랜치가 많으면 미완성 변경이 보이지 않는 곳에 숨을 뿐이다. 안전은 브랜치 수가 아니라 검증 경계, 통합 주기, 릴리즈 경계에서 나온다. feature/big-refactor가 3주 동안 열려 있으면 main은 조용해 보이지만, 병합 시점에 충돌과 회귀가 한꺼번에 드러난다.
GitHub Flow는 브랜치 종류가 적을 뿐 규율은 강해야 한다. main이 항상 배포 가능해야 하고, PR과 status check가 병합 조건이어야 한다. branch protection 없이 운영되는 GitHub Flow는 단순한 전략이 아니라 무규칙 feature branch에 가깝다.
TBD는 main에 아무거나 넣는 방식이 아니다. 작은 변경, 빠른 CI, feature flag, code review 또는 pair review, 빠른 rollback이 모두 필요하다. trunk가 자주 깨지면 TBD의 장점인 빠른 통합이 아니라 팀 전체의 대기 시간이 늘어난다.
feature flag는 위험을 없애지 않고 위험의 종류를 바꾼다. 브랜치 충돌 위험은 줄지만, flag 조합과 cleanup 부채가 생긴다. flag OFF가 안전한 기본값인지, flag 서비스 장애 시 어떤 경로로 가는지, 100% rollout 뒤 누가 삭제하는지까지 설계해야 한다.
rebase는 commit을 새 base 위에 다시 쓰는 작업이다. 혼자 쓰는 branch에서는 이력을 정리할 수 있지만, 이미 원격에 push했고 다른 사람이 그 branch를 기반으로 작업했다면 rebase는 다른 사람의 commit 기준점을 흔든다. 공유 branch에는 merge나 새 branch 전략이 더 안전하다.
브랜치 이름은 필요하지만 전략의 본체는 아니다. 이름이 좋아도 PR이 크고 CI가 느리고 main이 깨져 있으면 전략은 실패한다.
feature/TICKET-123-user-authfix/TICKET-456-payment-nullchore/update-node-versionrelease/v1.4.0hotfix/payment-crash좋은 이름은 다음 정보를 짧게 담는다.
feature, fix, chore, release, hotfixuser-auth, payment-null처럼 검색 가능한 단어이름에서 피해야 할 것은 사람 이름만 있는 branch, 범위가 너무 큰 feature/sprint-12, 끝난 뒤에도 남아 있는 test2 같은 branch다. 이런 이름은 충돌이 생겼을 때 소유자와 의도를 찾기 어렵게 만든다.
| 팀/제품 상황 | 시작 전략 | 이유 | 다음 전환 신호 |
|---|---|---|---|
| 개인 프로젝트, 작은 웹 서비스 | GitHub Flow | PR과 CI만으로 충분히 단순하다 | PR이 매우 작고 flag가 갖춰지면 TBD 일부 도입 |
| 소규모 SaaS 팀, 배포 주 1~5회 | GitHub Flow | main 보호와 staging 배포로 품질 경계를 만들 수 있다 | 배포 빈도를 더 올리고 싶고 flag cleanup이 가능할 때 TBD |
| 모바일 앱, SDK, 설치형 제품 | Git Flow | 릴리즈 후보와 hotfix 경계가 필요하다 | 서버 사이드 컴포넌트는 GitHub Flow로 분리 가능 |
| 대규모 monorepo, 빠른 자동화 테스트 | TBD | 긴 branch보다 작은 통합을 자주 흡수하는 편이 낫다 | flag 부채나 CI 지연이 늘면 branch discipline 재점검 |
| 테스트가 약하고 release도 불규칙한 팀 | GitHub Flow 정비부터 | TBD/Git Flow보다 main 보호와 CI 신뢰 회복이 먼저다 | main 안정화 후 release 또는 TBD 선택 |
전략은 한 번 고르면 영원히 고정되는 아키텍처가 아니다. 제품의 릴리즈 모델과 팀의 자동화 수준이 바뀌면 브랜치 전략도 바뀐다. 중요한 것은 전략을 바꿀 때 어떤 실패 비용을 줄이고 어떤 새 비용을 받아들이는지 명시하는 것이다.
Git Flow가 무겁게 느껴진다고 바로 TBD로 가면 실패하기 쉽다. 먼저 release/*와 develop이 실제로 어떤 가치를 주는지 확인한다.
| 관찰 | 의미 | 조치 |
|---|---|---|
| release branch에서 버그 픽스보다 새 기능 추가가 많다 | release branch가 안정화 경계로 작동하지 않는다 | release branch 제거 후보 |
develop이 자주 깨져 feature branch 시작점으로 불안하다 | develop이 품질 경계가 아니라 임시 적재소가 됐다 | PR을 main 기준으로 단순화 |
| 운영 hotfix가 드물고 대부분 PR로 처리 가능하다 | Git Flow의 hotfix 복잡도가 과할 수 있다 | GitHub Flow + tag/release note로 대체 |
| 배포가 주 3회 이상이고 QA가 자동화되어 있다 | 릴리즈 기차보다 continuous delivery에 가깝다 | main 보호 규칙 강화 후 GitHub Flow |
전환은 브랜치 삭제가 아니라 품질 경계 이동이다. develop을 없앤다면 main에 required status check, 리뷰 승인, 배포 환경 승인 같은 경계가 대신 있어야 한다.
GitHub Flow에서 TBD로 가는 핵심은 PR을 없애는 것이 아니라 PR과 branch를 더 작게 만드는 것이다. 많은 팀은 완전한 direct-to-trunk보다 “짧은 PR + feature flag” 형태로 시작한다.
작은 전환 순서는 다음과 같다.
여기서 p90은 전체 branch 중 90%가 이 시간 안에 병합된다는 뜻이다. 평균이 1일이어도 p90이 7일이면 일부 큰 branch가 계속 통합 위험을 만들고 있다는 신호다.
브랜치 전략은 감으로만 운영하면 “우리 팀은 빠르다”와 “우리 팀은 안정적이다”가 동시에 주장된다. 최소한 다음 지표는 한 달 단위로 볼 만하다.
| 지표 | 왜 보는가 | 위험 신호 |
|---|---|---|
| PR open -> merge 평균/p90 | branch 수명과 리뷰 병목을 본다 | p90이 계속 5일 이상 |
| PR당 변경 파일 수 | 리뷰 가능한 범위인지 본다 | 10개 파일 이상 PR이 반복 |
| main 실패 횟수 | 기본 브랜치 신뢰도를 본다 | 실패가 정상 상태처럼 방치 |
| merge conflict 발생 PR 비율 | 통합 지연 비용을 본다 | 같은 모듈에서 반복 충돌 |
| revert/rollback 횟수 | 검증 경계가 충분한지 본다 | 특정 유형 변경에서 반복 |
| flag age | release toggle cleanup을 본다 | 100% rollout 후 2주 이상 잔존 |
이 지표의 목적은 팀을 감시하는 것이 아니다. 전략이 약속한 비용 구조가 실제로 작동하는지 확인하는 것이다. GitHub Flow를 쓰는데 PR p90이 10일이면 “짧은 feature branch”라는 약속이 깨진다. TBD를 쓰는데 flag age가 계속 늘면 “브랜치 대신 flag가 장기 분기점이 되는” 실패가 시작된다.
브랜치 전략을 고를 때는 눈에 보이는 명령어보다 숨은 비용을 봐야 한다.
| 숨은 비용 | Git Flow | GitHub Flow | TBD |
|---|---|---|---|
| 교육 비용 | 브랜치 역할이 많아 높다 | 낮다 | 원리는 단순하지만 실천 난도가 높다 |
| CI 비용 | release/develop/main 여러 경계에서 돈다 | PR/main 중심 | 매우 자주 돌아야 한다 |
| 리뷰 비용 | feature/release 경계가 크면 커진다 | PR 크기에 직접 비례 | 작은 리뷰를 자주 해야 한다 |
| 운영 비용 | hotfix 되돌림과 release tag 관리 | branch protection/merge queue 관리 | flag lifecycle과 빠른 rollback 관리 |
| 인지 비용 | 현재 변경이 어느 브랜치에 들어가야 하는지 판단 | main 기준으로 단순 | deploy/release/flag 상태를 함께 봐야 함 |
이 표를 보면 “단순한 전략”도 공짜가 아니라는 점이 보인다. GitHub Flow는 branch 역할은 단순하지만 branch protection을 정확히 걸어야 한다. TBD는 branch 구조는 가장 단순하지만 feature flag와 CI 운영 비용이 크다. Git Flow는 도구와 규칙이 많지만 여러 버전 지원 비용을 명시적으로 감당한다.
브랜치 전략을 바꿀 때는 결정 기록을 짧게 남기면 좋다. 다음 질문에 답하지 못하면 아직 전략 문제가 아니라 팀 운영 문제가 정리되지 않은 상태일 수 있다.
| 질문 | 답해야 하는 이유 |
|---|---|
| 지금 가장 큰 비용은 충돌, 리뷰 지연, 릴리즈 지연, rollback 중 무엇인가? | 바꾸려는 전략이 실제 병목을 줄이는지 확인한다 |
새 전략에서 main을 누가 어떻게 보호하는가? | 품질 경계가 사라지지 않게 한다 |
| 미완성 기능은 branch, release branch, feature flag 중 어디에 머무르는가? | 격리 위치를 명확히 한다 |
| 긴급 패치는 다음 정규 릴리즈로 어떻게 되돌아가는가? | hotfix 재발을 막는다 |
| 한 달 뒤 어떤 지표가 좋아져야 성공인가? | 전략 변경을 취향 논쟁이 아니라 관찰 가능한 실험으로 만든다 |
이 절은 브랜치 전략 본문을 이해한 뒤 그래프를 눈으로 확인하기 위한 부록이다. 긴 설치 runbook보다 “무엇을 관찰해야 하는가”에 집중한다.
git init merge-practicecd merge-practicegit commit --allow-empty -m "initial"git checkout -b feature/agit commit --allow-empty -m "feat: A 1"git commit --allow-empty -m "feat: A 2"git checkout maingit merge --no-ff feature/a -m "Merge feature/a"git log --oneline --graph --all관찰할 것:
feature/a의 두 commit이 그래프 옆 가지로 보이는가?git revert <merge-commit>을 하면 기능 묶음 단위 revert가 가능하다는 점을 이해했는가?Squash도 같은 저장소에서 새 branch로 확인할 수 있다.
git checkout -b feature/b maingit commit --allow-empty -m "feat: B 1"git commit --allow-empty -m "feat: B 2"git checkout maingit merge --squash feature/bgit commit -m "feat: B"git log --oneline --graph --all관찰할 것:
main에는 feat: B 하나만 남는가?feature/b에는 원래 commit 두 개가 여전히 남아 있는가?TBD나 GitHub Flow에서는 branch 수명 자체가 품질 신호다.
git for-each-ref \ --format="%(refname:short) %(committerdate:relative)" \ refs/heads관찰할 것:
GitHub CLI로 현재 저장소의 보호 규칙을 볼 수 있다.
gh api repos/{owner}/{repo}/branches/main/protection \ --jq '{ required_reviews: .required_pull_request_reviews.required_approving_review_count, status_checks: .required_status_checks.contexts, strict: .required_status_checks.strict, enforce_admins: .enforce_admins.enabled }'관찰할 것:
strict가 꺼져 있다면 merge queue나 update branch 습관이 있는가?Git Flow를 쓴다면 hotfix가 두 장기 브랜치에 모두 들어갔는지 확인하는 습관이 중요하다.
git log --oneline main --grep "payment-null"git log --oneline develop --grep "payment-null"관찰할 것:
main과 develop 양쪽에 있는가?퀴즈
힌트: Git Flow는 main과 develop이라는 두 장기 브랜치를 동시에 유지한다.
다음 release가 develop에서 만들어질 때 hotfix가 빠져 같은 버그가 다시 프로덕션에 들어갈 수 있다.
| 키워드 | 설명 |
|---|---|
main / master | 제품 기준이 되는 기본 브랜치. 최근 저장소는 보통 main을 쓴다 |
develop | Git Flow에서 다음 릴리즈 후보 기능이 모이는 통합 개발 브랜치 |
feature branch | 기능/수정 단위의 임시 작업 브랜치 |
release branch | 배포 후보를 안정화하는 임시 브랜치 |
hotfix branch | 프로덕션 긴급 패치를 위한 브랜치 |
| trunk | TBD에서 모두가 자주 합류하는 중심 브랜치 |
| Pull Request(PR) | 변경 단위, 리뷰 단위, 자동 검증 단위 |
| status check | CI 빌드, 테스트, 린트 같은 자동 검증 결과 |
| branch protection | 특정 브랜치에 병합/푸시 조건을 강제하는 규칙 |
| merge queue | 최신 base 조합에서 PR을 순차 검증한 뒤 병합하는 큐 |
| feature flag | 코드 배포와 기능 노출을 분리하는 스위치 |
| release toggle | 출시 전 기능을 숨기는 임시 flag |
| merge commit | 두 브랜치 이력을 합친 경계를 남기는 commit |
| squash merge | PR commit 여러 개를 하나로 압축해 병합하는 방식 |
| rebase | commit을 다른 base 위에 다시 쌓아 이력을 재작성하는 방식 |
| DORA | DevOps 성과를 배포 빈도, 리드타임, 변경 실패율, 복구 시간 등으로 보는 연구/측정 프레임 |
브랜치 전략은 naming convention이 아니라 충돌, 리뷰, 릴리즈, 롤백 경계를 설계하는 방식이다.
Git Flow는 main과 develop, release, hotfix를 나눠 여러 버전과 릴리즈 기차를 관리한다. 장점은 릴리즈 경계와 긴급 패치 추적성이고, 비용은 긴 branch와 복잡한 통합 흐름이다.
GitHub Flow는 짧은 feature branch와 PR, status check, branch protection으로 main을 지킨다. 기본값으로 쓰기 좋지만 main이 항상 배포 가능하다는 규율이 무너지면 전략 전체가 무너진다.
TBD는 trunk에 자주 통합하고 feature flag로 deploy와 release를 분리한다. 빠른 CI, 작은 변경, flag cleanup이 없으면 단순히 branch만 줄인 위험한 운영이 된다.
병합 방식도 전략의 일부처럼 다뤄야 한다. merge commit은 경계를 남기고, squash는 main을 깔끔하게 만들며, rebase는 이력을 재작성한다. 어떤 방식을 쓰든 “병합 후 branch를 어떻게 다룰지”까지 팀 규칙에 포함해야 한다.