Git Flow & Branching
분류: Layer 9 - 아키텍처 & 설계 패턴
Git 브랜치 전략 (Git Flow / GitHub Flow / Trunk-Based Development)
섹션 제목: “Git 브랜치 전략 (Git Flow / GitHub Flow / Trunk-Based Development)”1. 한 줄 정의
섹션 제목: “1. 한 줄 정의”팀이 코드를 어떻게 나누고, 합치고, 배포할지를 정의하는 브랜치 운영 규칙으로, 협업 충돌을 줄이고 안정적인 배포를 가능하게 하는 개발 워크플로우다.
2. 왜 중요한가
섹션 제목: “2. 왜 중요한가”브랜치 전략이 없으면 생기는 문제
섹션 제목: “브랜치 전략이 없으면 생기는 문제”개발자 5명이 동시에 main 브랜치에 직접 커밋한다고 상상해보자. A가 결제 기능을 개발하는 도중 B가 인증 모듈을 수정하고, 그 사이 C가 긴급 버그를 고쳐야 한다. 결과는 예측 불가능한 충돌, 테스트되지 않은 코드의 배포, 롤백의 악몽이다.
브랜치 전략이 해결하는 것
섹션 제목: “브랜치 전략이 해결하는 것”- 병렬 개발 안정성: 서로 다른 기능을 독립된 공간에서 개발
- 배포 품질 보장: 리뷰, 테스트를 거친 코드만
main에 합류 - 롤백 용이성: 문제가 생기면 어느 지점으로 되돌릴지 명확
- 이력 추적: “언제 어떤 기능이 왜 들어갔는지” 히스토리 파악
현업에서의 체감
섹션 제목: “현업에서의 체감”DevOps Research and Assessment(DORA) 지표에 따르면 Trunk-Based Development를 채택한 고성능 팀은 그렇지 않은 팀보다 배포 빈도가 182배, 변경 리드타임이 127배 빠른 것으로 나타났다(2024년 기준). 브랜치 전략 선택은 팀의 속도와 안정성 모두에 직결된다.
3. 핵심 개념
섹션 제목: “3. 핵심 개념”3-1. Git Flow
섹션 제목: “3-1. Git Flow”비유: 대형 마트의 물류 시스템
섹션 제목: “비유: 대형 마트의 물류 시스템”Git Flow는 대형 마트처럼 동작한다. 상품이 **진열대(main)**에 오르려면 입고 창고(develop) → 검품실(release) → 진열대(main) 순서를 거쳐야 한다. 급히 필요한 상품은 검품실을 건너뛰고 **응급 코너(hotfix)**를 통해 바로 진열대에 오른다.
브랜치 구조 원리
섹션 제목: “브랜치 구조 원리”main ──●──────────────────────●── (v1.0) │ │ │ hotfix/bug ──┤── (긴급 패치) │ │develop ─────●──●──●──●──●──────────●── │ │feature/login ────●────────┘feature/payment └──●──●──┘ │release/1.0 ──────────────────●──● (QA, 버전 태깅)| 브랜치 | 역할 | 생성 기준 | 삭제 시점 |
|---|---|---|---|
main | 배포된 프로덕션 코드 | 최초 1회 | 영구 유지 |
develop | 통합 개발 브랜치 | main에서 파생 | 영구 유지 |
feature/* | 기능 단위 개발 | develop에서 파생 | develop 병합 후 삭제 |
release/* | 배포 준비 (QA, 버그 픽스) | develop에서 파생 | main 병합 후 삭제 |
hotfix/* | 프로덕션 긴급 패치 | main에서 파생 | main + develop 병합 후 삭제 |
Git 명령어 예시
섹션 제목: “Git 명령어 예시”# 1. feature 브랜치 생성 (develop에서 파생)git checkout developgit checkout -b feature/user-auth
# 기능 개발 후git add .git commit -m "feat: JWT 인증 구현"
# 2. develop에 병합git checkout developgit merge --no-ff feature/user-authgit branch -d feature/user-auth
# 3. release 브랜치 생성git checkout -b release/1.0.0
# 버전 정보 업데이트, QA 버그 픽스git commit -m "chore: 버전 1.0.0 설정"
# 4. main에 병합 (배포)git checkout maingit merge --no-ff release/1.0.0git tag -a v1.0.0 -m "Release 1.0.0"
# 5. develop에도 병합 (release에서 수정된 내용 반영)git checkout developgit merge --no-ff release/1.0.0git branch -d release/1.0.0예상 출력:
Switched to branch 'develop'Switched to a new branch 'feature/user-auth'[feature/user-auth abc1234] feat: JWT 인증 구현 3 files changed, 87 insertions(+)Switched to branch 'develop'Merge made by the 'ort' strategy.Deleted branch feature/user-auth (was abc1234).Hotfix 흐름
섹션 제목: “Hotfix 흐름”# 프로덕션 긴급 버그 발생git checkout maingit checkout -b hotfix/null-pointer-fix
git commit -m "fix: 결제 시 null pointer 예외 수정"
# main과 develop 모두에 반영git checkout maingit merge --no-ff hotfix/null-pointer-fixgit tag -a v1.0.1 -m "Hotfix 1.0.1"
git checkout developgit merge --no-ff hotfix/null-pointer-fixgit branch -d hotfix/null-pointer-fix”왜 —no-ff 옵션이 중요한가” — 머지 전략의 원리
섹션 제목: “”왜 —no-ff 옵션이 중요한가” — 머지 전략의 원리”Git Flow에서 --no-ff(no fast-forward)는 단순한 습관이 아니라 이력 추적성을 위한 필수 옵션이다.
Fast-forward 머지 (--ff, 기본값):main: ──A──B──C──D── (feature 커밋이 main에 직접 이어붙음)→ "언제 어떤 feature가 main에 들어왔는지" 경계가 보이지 않음
No-fast-forward 머지 (--no-ff):main: ──A──────────M── (머지 커밋 M이 경계를 만듦) ╲ ╱feature: ──B──C──D→ git log --graph로 "이 기능이 언제 합쳐졌는지" 명확하게 추적 가능→ git revert M 한 번으로 기능 전체를 되돌릴 수 있음fast-forward로 머지하면 feature 브랜치의 커밋이 main에 직접 이어붙어서, 나중에 “이 기능을 통째로 되돌리고 싶다”면 커밋 하나하나를 일일이 revert해야 한다. --no-ff는 머지 커밋을 남겨서 하나의 revert로 기능 전체를 안전하게 되돌릴 수 있게 해준다.
📖 더 보기: A successful Git branching model (nvie.com) — Git Flow 원저자가 —no-ff를 필수로 권장하는 이유 설명 (입문)
Git Flow 평가
섹션 제목: “Git Flow 평가”장점:
- 릴리즈 버전 관리가 명확하다 (v1.0, v1.1, v2.0 체계적 관리)
- 여러 버전을 동시에 지원해야 하는 경우 적합 (모바일 앱, 패키지 라이브러리)
- QA 팀이 별도로 있는 조직에 적합
단점:
- 브랜치가 많아 복잡하다
develop→release→main단계가 많아 배포까지 시간이 걸린다- CI/CD를 구축해도 배포 속도 향상이 제한적이다
- 원저자 Vincent Driessen 본인도 2020년에 “웹 앱에는 Git Flow가 맞지 않는다”고 인정했다
3-2. GitHub Flow
섹션 제목: “3-2. GitHub Flow”비유: 편의점의 상품 입고
섹션 제목: “비유: 편의점의 상품 입고”GitHub Flow는 편의점처럼 단순하다. 물건이 필요하면 발주(feature 브랜치 생성) → 입고 검수(PR + 리뷰) → 바로 진열(main 머지 + 배포). 창고 단계 없이 빠르게 순환한다.
브랜치 구조 원리
섹션 제목: “브랜치 구조 원리”main ──●──────────────────────────────●── (항상 배포 가능) │ │feature/ └──●──●──●── (PR 생성) ────── ┘ ↑ ↑ 커밋 코드 리뷰GitHub Flow는 딱 두 가지 브랜치 유형만 존재한다:
main: 항상 배포 가능한 상태를 유지feature/*(또는fix/*,chore/*): 모든 작업 브랜치
흐름 단계
섹션 제목: “흐름 단계”1. main에서 브랜치 생성 git checkout -b feature/add-search
2. 작업 후 원격에 push git push -u origin feature/add-search
3. Pull Request 생성 (GitHub UI) - 제목: "feat: 상품 검색 기능 추가" - 리뷰어 지정 - CI/CD 자동 트리거
4. 코드 리뷰 + 수정
5. main에 Merge
6. 자동 배포 (CI/CD)Git 명령어 예시
섹션 제목: “Git 명령어 예시”# 1. 브랜치 생성git checkout maingit pull origin maingit checkout -b feature/product-search
# 2. 개발git add .git commit -m "feat: 상품명 기반 검색 API 추가"git push -u origin feature/product-search
# 3. PR 생성 (GitHub CLI 사용 시)gh pr create \ --title "feat: 상품 검색 기능 추가" \ --body "## 변경 내용\n- SearchController 추가\n- SearchService 구현" \ --reviewer team-lead
# 4. 리뷰 승인 후 머지gh pr merge --squash
# 5. 로컬 정리git checkout maingit pull origin maingit branch -d feature/product-search예상 출력:
Switched to a new branch 'feature/product-search'[feature/product-search def5678] feat: 상품명 기반 검색 API 추가 5 files changed, 142 insertions(+)Branch 'feature/product-search' set up to track remote branch 'feature/product-search' from 'origin'.https://github.com/team/repo/pull/42✓ Pull request #42 successfully merged and closed“왜 GitHub Flow에서 main은 항상 배포 가능해야 하는가” — 지속적 배포의 전제 조건
섹션 제목: ““왜 GitHub Flow에서 main은 항상 배포 가능해야 하는가” — 지속적 배포의 전제 조건”GitHub Flow의 핵심 규율은 **“main = 배포 가능”**이다. 이 규율이 무너지면 전체 전략이 붕괴한다.
main이 배포 불가능한 상태일 때 벌어지는 일:
1. 개발자 A가 feature 브랜치를 main에서 파생2. 그런데 main에 이미 깨진 코드가 있음3. A의 feature 브랜치도 시작부터 깨진 상태4. A가 PR을 올려도 CI가 실패 → "원래 실패하는 거예요"5. CI 실패가 정상이 되면 아무도 CI 결과를 신뢰하지 않음6. 결국 리뷰 없이 머지 → 품질 붕괴
main이 항상 배포 가능할 때:1. 어떤 시점에서든 main을 배포해도 안전2. feature 브랜치가 CI 실패 → "내 코드에 문제가 있다"로 확신3. CI 결과를 팀 전체가 신뢰4. 긴급 상황에 즉시 main을 배포해서 대응 가능이 원칙을 지키려면 Branch Protection Rules에서 “Require status checks to pass”가 필수다.
📖 더 보기: GitHub Flow Guide — GitHub Docs — GitHub Flow의 공식 가이드. main 브랜치 보호 전략 포함 (입문)
GitHub Flow 평가
섹션 제목: “GitHub Flow 평가”장점:
- 배우기 쉽고 관리 포인트가 적다
- PR이 협업 + 리뷰 + 배포 트리거를 모두 처리
- 소규모 팀(2~10명)에 가장 적합
- CI/CD와 자연스럽게 연동
단점:
- 여러 버전을 동시 지원하기 어렵다
main이 항상 배포 가능해야 한다는 규율이 필요하다- 대규모 팀에서는 PR 병목이 생길 수 있다
3-3. Trunk-Based Development (TBD)
섹션 제목: “3-3. Trunk-Based Development (TBD)”비유: 실시간 뉴스 방송
섹션 제목: “비유: 실시간 뉴스 방송”TBD는 실시간 뉴스 방송처럼 동작한다. 기자(개발자)들이 하루에도 여러 번 뉴스(커밋)를 방송(main)에 올린다. 미완성 기사는 방송하지 않는 것이 아니라, **자막으로 “준비 중”(Feature Flag)**이라고 표시한 채로 방송한다.
핵심 원리
섹션 제목: “핵심 원리”모든 개발자가 하루에 1~2회 이상 main 브랜치에 직접 병합한다. 브랜치를 만들더라도 최대 1~2일 안에 병합한다. 미완성 기능은 Feature Flag로 감춘다.
main ──●──●──●──●──●──●──●──● (하루에 수십 번 커밋) ↑ ↑ ↑ 개발자A B C (모두 main에 직접 or 단기 브랜치로)Feature Flag 패턴
섹션 제목: “Feature Flag 패턴”// Nest.js 예시: Feature Flag로 미완성 기능 숨기기@Injectable()export class ProductService { constructor(private readonly featureFlagService: FeatureFlagService) {}
async getProducts(userId: string) { // Flag가 켜진 사용자에게만 새 검색 기능 노출 if (await this.featureFlagService.isEnabled("new-search", userId)) { return this.newSearchProducts(); } return this.legacySearchProducts(); }}
// 환경변수 기반 간단 구현const FEATURE_FLAGS = { "new-search": process.env.FEATURE_NEW_SEARCH === "true", "payment-v2": process.env.FEATURE_PAYMENT_V2 === "true",};TBD에서의 커밋 전략
섹션 제목: “TBD에서의 커밋 전략”# 작은 단위로 자주 커밋 (큰 PR 대신 작은 커밋들)git checkout maingit pull origin main
# 기능의 일부만 구현 (완성되지 않아도 OK, Flag로 숨김)git add src/search/search.service.tsgit commit -m "feat: 검색 서비스 기본 구조 추가 (flag: new-search)"git push origin main
# CI가 자동으로 빌드/테스트 실행# 통과하면 자동 배포TBD가 머지 충돌을 줄이는 이유 (동작 원리 심화)
섹션 제목: “TBD가 머지 충돌을 줄이는 이유 (동작 원리 심화)”왜 브랜치 수명이 짧을수록 충돌이 적은가?
머지 충돌은 두 브랜치가 같은 코드 영역을 각자 다르게 수정할 때 발생한다. 브랜치가 2일이면 베이스(main)와 2일치 차이만 존재한다. 브랜치가 2주면 14일치 차이가 쌓인다. 충돌 가능성은 수명에 비례한다.
Git Flow (브랜치 수명 2주):main: A──B──C──D──E──F──G──H──I──J──K──L──M──N (14일간 14개 커밋)feature: └──────────────────────────────────────● (병합 시 14개와 충돌 가능)
TBD (브랜치 수명 1일):main: A──B──C──D──E──F──G──H──I──J──K──L──M──N └─● ← 1일치 = 최대 2~3개 커밋과 충돌 가능Feature Flag가 필요한 이유
TBD에서는 기능이 완성되기 전에도 main에 병합한다. 그러면 미완성 코드가 프로덕션에 배포된다. **Feature Flag는 “코드는 배포됐지만 기능은 꺼져있다”**는 상태를 만드는 장치다.
배포(Deploy) ≠ 기능 출시(Release)
코드 배포 기능 출시주문 서비스 v2.0 ──────── Flag ON(특정 팀만) ──── Flag ON(전체) main 병합 1주 후 2주 후 ↑ TBD: 미완성이어도 배포 Flag: OFF 상태로 숨겨짐// AWS AppConfig 기반 Feature Flag (실무 패턴)import { AppConfigDataClient, GetLatestConfigurationCommand,} from "@aws-sdk/client-appconfigdata";
@Injectable()export class FeatureFlagService { private readonly client = new AppConfigDataClient({ region: "ap-northeast-2", });
async isEnabled(flagName: string, userId?: string): Promise<boolean> { try { const command = new GetLatestConfigurationCommand({ ConfigurationToken: process.env.APPCONFIG_TOKEN, }); const response = await this.client.send(command); const config = JSON.parse( new TextDecoder().decode(response.Configuration), );
// 사용자별 점진적 롤아웃 (userId 해시로 10% 사용자에게만 노출) const flag = config.flags[flagName]; if (!flag?.enabled) return false; if (!userId || flag.rolloutPercentage >= 100) return flag.enabled;
// 사용자 ID 해시로 일관된 그룹 배정 const hash = userId .split("") .reduce((acc, c) => acc + c.charCodeAt(0), 0); return hash % 100 < flag.rolloutPercentage; } catch { return false; // Flag 서비스 장애 시 기능 OFF (안전한 기본값) } }}// AppConfig 설정 예시 (JSON){ "flags": { "new-search": { "enabled": true, "rolloutPercentage": 10 // 전체 사용자의 10%에게만 노출 }, "payment-v2": { "enabled": false // 완전히 꺼짐 } }}📖 더 보기: Trunk Based Development 공식 사이트 — Feature Flag의 유형(Release Toggle, Experiment Toggle 등)과 관리 전략 (중급)
TBD 평가
섹션 제목: “TBD 평가”장점:
- 배포 빈도 최대화 (하루 수십 번도 가능)
- 머지 충돌 최소화 (브랜치 수명이 짧아서)
- CI/CD 파이프라인과 완벽하게 연동
- DORA 지표 기준 고성능 팀의 표준
단점:
- 강력한 코드 리뷰 문화가 전제 조건
- 자동화 테스트 커버리지가 없으면 위험
- Feature Flag 관리 비용이 발생 (오래된 Flag는 기술 부채가 됨)
- 주니어가 많은 팀에서는 도입이 어렵다
4. 실무에서 어떻게 쓰이나
섹션 제목: “4. 실무에서 어떻게 쓰이나”Branch Protection Rules
섹션 제목: “Branch Protection Rules”브랜치 보호 규칙은 팀이 정한 브랜치 전략을 강제로 지키게 해주는 GitHub 설정이다.
설정 위치
섹션 제목: “설정 위치”GitHub Repository → Settings → Branches → Branch protection rules → Add rule핵심 옵션별 설명
섹션 제목: “핵심 옵션별 설명”| 설정 | 효과 | 권장 여부 |
|---|---|---|
| Require a pull request before merging | 직접 push 차단, PR만 허용 | 항상 권장 |
| Require approvals (최소 1명) | 최소 N명의 리뷰 승인 필요 | 소규모 팀: 1명, 대규모: 2명 |
| Require status checks to pass | CI 테스트 통과 후에만 머지 가능 | 항상 권장 |
| Require conversation resolution | 모든 리뷰 코멘트 해결 후 머지 | 권장 |
| Restrict who can push | 특정 사람/팀만 push 허용 | 대규모 팀 권장 |
| Require linear history | Squash 또는 Rebase만 허용 (Merge Commit 불가) | 히스토리 정리에 유용 |
| Do not allow bypassing | 관리자도 규칙 적용 | 실수 방지 |
실제 설정 예시 (GitHub API)
섹션 제목: “실제 설정 예시 (GitHub API)”# GitHub CLI로 브랜치 보호 규칙 설정gh api repos/{owner}/{repo}/branches/main/protection \ --method PUT \ --field required_status_checks='{"strict":true,"contexts":["ci/build","ci/test"]}' \ --field enforce_admins=true \ --field required_pull_request_reviews='{"required_approving_review_count":1}' \ --field restrictions=nullSquash Merge vs Merge Commit vs Rebase
섹션 제목: “Squash Merge vs Merge Commit vs Rebase”PR을 main에 합칠 때 세 가지 방식이 있다. 어떤 방식을 선택하느냐에 따라 git 이력이 달라진다.
시각적 비교
섹션 제목: “시각적 비교”시나리오: feature 브랜치에 커밋 3개 (A, B, C)가 있고 main에 머지
# Merge Commit (--no-ff)main: ──1──2──────────────M── ╲ ╱feature: ──A──B──C
결과: main에 A, B, C, M (머지 커밋) 모두 보임# Squash Mergemain: ──1──2────────ABC──
결과: main에 A+B+C가 하나의 커밋(ABC)으로 압축 feature 브랜치의 개별 커밋은 사라짐# Rebasemain: ──1──2──A'──B'──C'──
결과: A, B, C가 main 위에 재배치 (새 해시값) 선형 이력 유지선택 기준
섹션 제목: “선택 기준”| 방식 | 이력 | 추천 상황 | 주의사항 |
|---|---|---|---|
| Merge Commit | 복잡, 원본 보존 | 오래 유지할 브랜치, 감사 추적 필요 | git log가 복잡해짐 |
| Squash Merge | 깔끔, 정보 손실 | feature 브랜치 완료 후 삭제할 때 | git bisect 어려워짐 |
| Rebase | 선형, 해시 변경 | 공유되지 않은 브랜치 | 공유 브랜치에는 절대 사용 금지 |
GitHub 설정
섹션 제목: “GitHub 설정”Repository Settings → Pull Requests☑ Allow merge commits☑ Allow squash merging☑ Allow rebase merging팀 컨벤션을 통일하고 싶다면 두 개를 비활성화하고 하나만 남긴다.
CI/CD 파이프라인 연동
섹션 제목: “CI/CD 파이프라인 연동”# .github/workflows/main.yml (GitHub Actions 예시)name: CI/CD Pipeline
on: push: branches: [main, develop] pull_request: branches: [main, develop]
jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: "20" - run: npm ci - run: npm test # PR 단계: 테스트만 - run: npm run lint
deploy: needs: test if: github.ref == 'refs/heads/main' # main 머지 시만 배포 runs-on: ubuntu-latest steps: - name: Deploy to Production run: | echo "Deploying to AWS..." # aws ecs update-service ...5. 내 업무와의 연결고리
섹션 제목: “5. 내 업무와의 연결고리”BackOps / Nest.js + AWS 스택 기준으로 각 전략의 현실적 적합성을 분석한다.
상황별 추천
섹션 제목: “상황별 추천”소규모 팀 (2~5명) + 빠른 배포 필요 → GitHub Flow 추천
- PR 기반 리뷰로 품질 유지
- main 머지 즉시 AWS ECS/Lambda 자동 배포
- 복잡한 브랜치 관리 오버헤드 없음
중규모 팀 (5~15명) + 버전 릴리즈 존재 → Git Flow 추천
- 배포 주기가 주 1회 이상이라면 release 브랜치가 QA를 돕는다
- hotfix 흐름이 긴급 패치를 안전하게 처리
고성능 팀 + 강한 CI/CD 문화 → Trunk-Based Development 추천
- 자동화 테스트 커버리지 80% 이상 필요
- Feature Flag 인프라 (AWS AppConfig, LaunchDarkly 등)
Nest.js 프로젝트에서의 브랜치 네이밍
섹션 제목: “Nest.js 프로젝트에서의 브랜치 네이밍”# 추천 브랜치 네이밍 컨벤션feature/TICKET-123-user-auth # Jira 티켓 번호 포함fix/TICKET-456-null-pointer # 버그 수정chore/update-nestjs-version # 유지보수release/v1.2.0 # 릴리즈 (Git Flow 사용 시)hotfix/payment-crash # 긴급 패치6. 비슷한 개념과 비교
섹션 제목: “6. 비슷한 개념과 비교”세 가지 전략 비교표
섹션 제목: “세 가지 전략 비교표”| 기준 | Git Flow | GitHub Flow | Trunk-Based Dev |
|---|---|---|---|
| 브랜치 수 | 5종 (main, develop, feature, release, hotfix) | 2종 (main, feature) | 1종 (main) + 단기 브랜치 |
| 배포 빈도 | 낮음 (주 1회~월 1회) | 중간 (PR 완료마다) | 높음 (하루 수십 회) |
| 팀 규모 | 중대형 (10명+) | 소중형 (2~15명) | 규모 무관, 문화 중요 |
| 학습 곡선 | 높음 | 낮음 | 중간 (규율 필요) |
| CI/CD 친화성 | 낮음 | 높음 | 매우 높음 |
| 버전 동시 지원 | 쉬움 | 어려움 | 어려움 |
| 머지 충돌 위험 | 높음 (브랜치 수명 길다) | 중간 | 낮음 (수명 짧다) |
| 적합한 제품 | 패키지, 앱 | SaaS, 웹서비스 | 클라우드 네이티브 |
”우리 팀에 맞는 전략” 판단 기준
섹션 제목: “”우리 팀에 맞는 전략” 판단 기준”1. 배포 주기가 얼마나 자주인가? - 월 1회 이하 → Git Flow - 주 1~3회 → GitHub Flow - 하루 여러 번 → TBD
2. 여러 버전을 동시에 지원해야 하는가? - YES → Git Flow - NO → GitHub Flow or TBD
3. 자동화 테스트 커버리지가 충분한가? - 낮음 (< 50%) → Git Flow or GitHub Flow (안전망 필요) - 높음 (> 80%) → TBD 도입 가능
4. 팀에 코드 리뷰 문화가 있는가? - 없거나 약함 → Git Flow (프로세스로 보완) - 있음 → GitHub Flow or TBD6.5. 트러블슈팅
섹션 제목: “6.5. 트러블슈팅”문제 1: main에 직접 push했을 때 브랜치 보호 규칙이 막는 경우
섹션 제목: “문제 1: main에 직접 push했을 때 브랜치 보호 규칙이 막는 경우”증상:
remote: error: GH006: Protected branch update failed for refs/heads/main.remote: error: Required status check "ci/test" is failing.To https://github.com/team/repo.git ! [remote rejected] main -> main (protected branch hook declined)error: failed to push some refs to 'https://github.com/team/repo.git'원인: Branch Protection Rules에서 PR 없이 직접 push를 차단하거나, CI 테스트가 통과하지 않으면 머지가 불가능하도록 설정됨
해결 방법:
# 1. 새 브랜치로 이동git checkout -b fix/my-direct-commit
# 2. PR을 통해 머지 요청git push -u origin fix/my-direct-commitgh pr create --title "fix: 직접 푸시한 변경사항" --base main
# (임시 방편: 관리자 권한으로 "Allow administrators" 비활성화 - 비권장)문제 2: feature 브랜치가 오래되어 develop과 충돌이 심각한 경우
섹션 제목: “문제 2: feature 브랜치가 오래되어 develop과 충돌이 심각한 경우”증상:
CONFLICT (content): Merge conflict in src/auth/auth.service.tsCONFLICT (content): Merge conflict in src/user/user.module.tsAutomatic merge failed; fix conflicts and then commit the result.원인: 오래된 feature 브랜치가 develop 브랜치와 많이 벌어져서 같은 파일을 동시에 수정한 경우
해결 방법:
# develop 최신 상태로 feature 브랜치 업데이트 (rebase 권장)git checkout feature/old-branchgit fetch origingit rebase origin/develop
# 충돌 발생 시 파일 직접 수정 후git add src/auth/auth.service.tsgit rebase --continue
# 해결 불가 시 abort 후 merge로 대체git rebase --abortgit merge origin/develop근본 해결: 브랜치 수명을 짧게 유지한다. feature 브랜치는 최대 3~5일 이내에 병합을 목표로 설계한다.
문제 3: Squash Merge 후 같은 브랜치를 계속 사용할 때 충돌
섹션 제목: “문제 3: Squash Merge 후 같은 브랜치를 계속 사용할 때 충돌”증상:
hint: You have divergent branches and need to specify how to reconcile them.fatal: Need to specify how to reconcile divergent branches.원인: Squash Merge는 feature 브랜치의 커밋들을 하나로 합쳐서 새 커밋을 만든다. 따라서 feature 브랜치의 원본 커밋과 main의 squash 커밋은 Git이 “다른 변경사항”으로 인식한다. 이 상태에서 feature 브랜치를 계속 사용하면 이미 반영된 내용을 다시 충돌로 처리한다.
해결 방법:
# Squash Merge 후에는 반드시 브랜치를 삭제하고 새로 생성git branch -d feature/old-branch # 로컬 삭제git push origin --delete feature/old-branch # 원격 삭제
# 추가 작업이 있다면 새 브랜치 생성git checkout -b feature/continuation main문제 4: Feature Flag 정리 안 해서 기술 부채 누적 (TBD)
섹션 제목: “문제 4: Feature Flag 정리 안 해서 기술 부채 누적 (TBD)”증상:
코드베이스에 Feature Flag 분기가 50개 이상 존재한다.어떤 Flag가 아직 사용 중이고 어떤 것이 이미 100% 릴리즈된 건지 모른다.새 기능 개발 시 기존 Flag와 조합이 꼬여서 예상치 못한 버그 발생.원인: Feature Flag는 TBD에서 미완성 코드를 숨기기 위한 임시 장치다. 기능이 100% 릴리즈되면 Flag와 분기 코드를 제거해야 하는데, 이를 방치하면 코드 경로가 기하급수적으로 늘어난다 (Flag 2개 = 4가지 경로, 10개 = 1024가지 경로).
해결 방법:
# 1. Flag 인벤토리 관리 (스프레드시트 또는 자동화)# flag_name | created_date | owner | status | removal_target_date# new-search | 2024-01-15 | @팀원A | 100% rolled out | 2024-02-15
# 2. 릴리즈 완료된 Flag의 코드 정리# ❌ 정리 전if (await featureFlagService.isEnabled('new-search', userId)) { return this.newSearchProducts();}return this.legacySearchProducts();
# ✅ 정리 후 (100% 릴리즈 확인 후)return this.newSearchProducts();# + legacySearchProducts() 메서드 삭제
# 3. CI에서 만료된 Flag 감지 (날짜 기반)# jest나 lint 규칙으로 만료일이 지난 Flag가 있으면 빌드 실패 처리📖 더 보기: Feature Flags Best Practices — Flagsmith — Feature Flag 생명주기 관리와 정리 전략 (중급)
문제 5: Git Flow에서 hotfix를 develop에 반영하지 않아 버그 재발
섹션 제목: “문제 5: Git Flow에서 hotfix를 develop에 반영하지 않아 버그 재발”증상: main에 긴급 패치를 배포했는데, 다음 release에서 같은 버그가 다시 나타남
원인: hotfix 브랜치를 main에만 병합하고 develop에 반영하지 않음. develop → release → main 흐름에서 hotfix가 덮어씌워짐
해결 방법:
# hotfix 완료 시 반드시 두 브랜치 모두 병합git checkout maingit merge --no-ff hotfix/critical-fixgit tag -a v1.0.1
git checkout develop # ← 이 단계를 절대 빠뜨리지 말 것git merge --no-ff hotfix/critical-fix
git branch -d hotfix/critical-fix예방: Git Flow 도구(git flow) 사용 시 자동으로 두 브랜치에 병합한다.
git flow hotfix start critical-fix# 수정 후git flow hotfix finish critical-fix # main + develop 자동 병합7. 체크리스트
섹션 제목: “7. 체크리스트”Git Flow 도입 체크리스트
섹션 제목: “Git Flow 도입 체크리스트”-
main과develop브랜치가 분리되어 있는가? - feature 브랜치는 항상
develop에서 파생하는가? - hotfix 브랜치 병합 후
develop에도 반영하는가? - release 브랜치에서 QA 후
main에 태그를 남기는가?
GitHub Flow 도입 체크리스트
섹션 제목: “GitHub Flow 도입 체크리스트”-
main브랜치는 항상 배포 가능한 상태인가? - 모든 변경사항이 PR을 통해 병합되는가?
- Branch Protection Rules가 설정되어 있는가?
- CI/CD가 PR 단계에서 자동 실행되는가?
- 리뷰어 지정 및 승인 프로세스가 있는가?
TBD 도입 체크리스트
섹션 제목: “TBD 도입 체크리스트”- 자동화 테스트 커버리지가 충분한가? (70% 이상 권장)
- Feature Flag 시스템이 있는가? (환경변수, 외부 서비스 등)
- 브랜치 수명이 1~2일 이내로 유지되는가?
- 팀에 코드 리뷰 문화가 확립되어 있는가?
- CI가 모든 커밋에 즉시 실행되는가?
공통 체크리스트
섹션 제목: “공통 체크리스트”- 팀 전원이 합의한 브랜치 네이밍 컨벤션이 있는가?
- 커밋 메시지 컨벤션이 있는가? (Conventional Commits 등)
- Squash / Merge Commit / Rebase 방식이 통일되어 있는가?
- Branch Protection Rules이
main(그리고develop) 브랜치에 적용되어 있는가?
8. 핵심 키워드
섹션 제목: “8. 핵심 키워드”| 키워드 | 설명 |
|---|---|
main / master | 프로덕션에 배포된 안정 코드 브랜치 |
develop | Git Flow의 통합 개발 브랜치 |
feature branch | 기능 단위 작업 브랜치 |
release branch | 배포 준비 브랜치 (QA, 버전 태깅) |
hotfix branch | 프로덕션 긴급 패치 브랜치 |
trunk | TBD에서 main을 부르는 다른 이름 |
Feature Flag | 코드 배포와 기능 노출을 분리하는 패턴 |
Pull Request (PR) | 코드 리뷰 + 브랜치 병합 요청 |
Branch Protection Rules | GitHub에서 브랜치에 적용하는 보호 규칙 |
Squash Merge | 여러 커밋을 하나로 압축 후 병합 |
Rebase | 커밋을 다른 브랜치 위에 재배치 |
Merge Commit | 두 브랜치의 이력을 하나로 합치는 커밋 |
CI/CD | 자동 빌드/테스트/배포 파이프라인 |
DORA metrics | DevOps 성과 측정 지표 (배포 빈도, 리드타임 등) |
8.5. 추천 리소스
섹션 제목: “8.5. 추천 리소스”📚 추천 리소스
섹션 제목: “📚 추천 리소스”- 📖 A successful Git branching model — nvie.com — Git Flow 원저자 Vincent Driessen의 원문. 2020년 업데이트에서 “웹앱에는 적합하지 않다”고 직접 인정한 부분도 읽어볼 것 (입문)
- 📖 Gitflow Workflow — Atlassian Git Tutorial — Git Flow 전체 명령어 흐름을 그림과 함께 설명. 실습 예제 포함 (입문)
- 📖 Trunk Based Development 공식 사이트 — TBD의 모든 것. Feature Flag, Short-Lived Feature Branches 섹션별 상세 설명 (중급)
- 📖 Trunk-Based Development vs Gitflow: Which Branching Model Actually Works? — Mergify — 2025년 기준 두 전략의 장단점 비교. 실제 팀에서 60%가 하이브리드 방식을 쓴다는 통계 포함 (입문~중급)
- 📖 Choosing the Right Git Strategy in 2025 — Abdullah — GitHub Flow → TBD 점진적 전환 전략과 2025년 팀별 선택 가이드 (입문~중급)
9. 직접 확인해보기
섹션 제목: “9. 직접 확인해보기”실습 1: Git Flow 체험
섹션 제목: “실습 1: Git Flow 체험”# git-flow CLI 도구 설치 (macOS)brew install git-flow-avh
# 새 저장소에서 git flow 초기화mkdir git-flow-practice && cd git-flow-practicegit initgit flow init# 기본값으로 Enter 연타예상 출력:
Which branch should be used for bringing forth production releases? - masterBranch name for production releases: [master] main
Which branch should be used for integration of the "next release"? - developBranch name for "next release" development: [develop]
How to name your supporting branch prefixes?Feature branches? [feature/]Bugfix branches? [bugfix/]Release branches? [release/]Hotfix branches? [hotfix/]Support branches? [support/]Version tag prefix? []Gitflow configuration stored in .git/config# feature 브랜치 시작git flow feature start my-feature예상 출력:
Switched to a new branch 'feature/my-feature'
Summary of actions:- A new branch 'feature/my-feature' was created, based on 'develop'- You are now on branch 'feature/my-feature'# 파일 작성 후 커밋echo "# My Feature" > feature.mdgit add feature.mdgit commit -m "feat: my feature 추가"
# feature 브랜치 완료git flow feature finish my-feature예상 출력:
Switched to branch 'develop'Updating 1a2b3c4..5d6e7f8Fast-forward feature.md | 1 + 1 file changed, 1 insertion(+)Deleted branch feature/my-feature (was 5d6e7f8).
Summary of actions:- The feature branch 'feature/my-feature' was merged into 'develop'- Feature branch 'feature/my-feature' has been locally deleted- You are now on branch 'develop'실습 2: 현재 브랜치 보호 규칙 확인 (GitHub CLI)
섹션 제목: “실습 2: 현재 브랜치 보호 규칙 확인 (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, enforce_admins: .enforce_admins.enabled }'예상 출력:
{ "required_reviews": 1, "status_checks": ["ci/build", "ci/test"], "enforce_admins": true}실습 3: Merge 방식별 이력 비교
섹션 제목: “실습 3: Merge 방식별 이력 비교”# 테스트 저장소 생성mkdir merge-practice && cd merge-practicegit initecho "initial" > README.mdgit add . && git commit -m "initial commit"
# Merge Commit 방식git checkout -b feature/aecho "feature a" > a.txtgit add . && git commit -m "feat: A 기능 1"echo "update" >> a.txtgit add . && git commit -m "feat: A 기능 2"
git checkout maingit merge --no-ff feature/a -m "Merge: feature/a"
git log --oneline --graph예상 출력:
* f3a1b2c Merge: feature/a|\| * 9d8e7f6 feat: A 기능 2| * 1a2b3c4 feat: A 기능 1|/* 0000001 initial commit# Squash Merge 방식git checkout -b feature/becho "feature b" > b.txtgit add . && git commit -m "feat: B 기능 1"echo "update" >> b.txtgit add . && git commit -m "feat: B 기능 2"
git checkout maingit merge --squash feature/bgit commit -m "feat: B 기능 (squash)"
git log --oneline --graph예상 출력:
* a1b2c3d feat: B 기능 (squash) ← 2개 커밋이 1개로 압축됨* f3a1b2c Merge: feature/a|\| * 9d8e7f6 feat: A 기능 2| * 1a2b3c4 feat: A 기능 1|/* 0000001 initial commit10. 한 줄 요약
섹션 제목: “10. 한 줄 요약”Git Flow는 버전 관리 명확성, GitHub Flow는 단순성과 속도, Trunk-Based Development는 최고의 배포 빈도를 추구하며, 팀 규모·배포 주기·테스트 문화에 따라 최적의 전략을 선택해야 한다.