← posts/b.log()

blog92@web:~$ cat posts/github-05-pull-request-and-branch-strategy.md

GIT3 min read

GitHub 완전 정복 5편 — Pull Request, 머지 전략, force push, 브랜치 전략

PR 표준 흐름과 좋은 PR의 조건·리뷰 문화, merge/squash/rebase 세 머지 버튼의 결과 차이, --force와 --force-with-lease의 차이, GitHub Flow·Git Flow·Trunk-Based 선택 기준, 그리고 Issues와 프로젝트 관리를 다룹니다.

이전 편: 4편 — 브랜치의 실체와 merge·rebase·충돌 해결

3.7 Pull Request — GitHub 협업의 핵심

PR은 Git 기능이 아니라 GitHub 기능입니다. "내 브랜치를 당신 브랜치에 합쳐달라"는 공식 요청이자, 코드 리뷰와 CI가 붙는 지점입니다.

표준 흐름:

bash
# 1. 최신 main 기준으로 시작
git switch main
git fetch origin
git merge origin/main
 
# 2. 브랜치 생성
git switch -c feature/login
 
# 3. 작업 & 커밋
git add .
git commit -m "feat(auth): 로그인 폼 추가"
 
# 4. 푸시
git push -u origin feature/login
 
# 5. GitHub에서 PR 생성 (또는 gh CLI)
gh pr create --base main --head feature/login \
  --title "feat: 로그인 기능" --body "Closes #42"
 
# 6. 리뷰 반영
git add . && git commit -m "fix: 리뷰 반영"
git push
 
# 7. 머지 후 정리
git switch main
git fetch origin --prune
git merge origin/main
git branch -d feature/login

좋은 PR의 조건

  • 작게 (변경 400줄 이하가 리뷰 품질의 분기점이라고 알려져 있습니다)
  • 하나의 PR은 하나의 목적만
  • 제목에 Conventional Commits 타입 사용
  • 본문에 왜 이렇게 했는지, 어떻게 테스트했는지
  • UI 변경이면 스크린샷/GIF 첨부
  • Closes #42 / Fixes #42 로 이슈 자동 종료 연결

PR 템플릿 — .github/PULL_REQUEST_TEMPLATE.md

markdown
## 변경 사항
<!-- 무엇을 왜 바꿨는지 -->
 
## 관련 이슈
Closes #
 
## 테스트 방법
1.
2.
 
## 체크리스트
- [ ] 로컬 테스트 통과
- [ ] 문서 업데이트
- [ ] Breaking change 없음

리뷰 문화

상태의미
Comment의견만, 승인/거부 아님
Approve머지해도 좋음
Request changes수정 필요, 머지 차단

리뷰 코멘트에 nit:(사소한 지적), question:, blocking: 같은 접두사를 붙이면 중요도가 전달돼서 소통 비용이 줄어듭니다.

3.8 GitHub 머지 전략 3가지

PR 머지 버튼의 세 가지 옵션은 결과가 완전히 다릅니다.

방식결과적합한 상황
Create a merge commit브랜치 커밋 전부 + 병합 커밋히스토리를 있는 그대로 남기고 싶을 때
Squash and merge커밋 전부를 하나로 압축대부분의 팀에 권장. main이 깨끗해짐
Rebase and merge커밋들을 main 위에 일직선으로커밋 하나하나가 의미 있고 일직선을 원할 때

실무에서는 Squash and merge가 기본값인 경우가 많습니다. 작업 중 "wip", "오타 수정", "리뷰 반영" 같은 커밋이 main 히스토리에 남지 않고, PR 하나 = 커밋 하나가 되어 revert도 간단해지기 때문입니다.

3.9 push --force와 --force-with-lease

bash
git push --force              # ⚫ 원격을 내 로컬로 무조건 덮어씀
git push --force-with-lease   # 🔴 내가 마지막으로 본 상태와 다르면 거부 ★ (Git 1.8.5+, 사실상 모든 버전에서 사용 가능)

--force는 그 사이 동료가 push한 커밋을 경고 없이 삭제합니다. 원격에서 사라진 커밋은 동료의 로컬에는 남아있을 수 있지만, 그걸 다시 찾아 붙이는 건 매우 번거롭습니다.

--force-with-lease는 "내가 알고 있는 원격 상태 = 실제 원격 상태"일 때만 덮어씁니다. 그 사이 누가 push했다면 거부합니다. force가 꼭 필요하다면 무조건 이쪽을 쓰세요.

다만 --force-with-lease에도 구멍이 하나 있습니다. 이 옵션은 "내 원격 추적 브랜치(origin/main)가 실제 원격과 같은가"만 봅니다. 그런데 에디터의 자동 fetch나 cron이 뒤에서 git fetch를 돌리면 그 참조가 조용히 갱신되고, Git은 내가 동료의 커밋을 "본 것"으로 착각합니다. 이걸 막는 것이 --force-if-includes로, 원격 끝점이 내 로컬 히스토리에 실제로 통합됐는지까지 검사합니다.

더 엄격하게 하려면:

bash
git push --force-with-lease --force-if-includes
# 내 로컬이 원격의 최신 커밋을 실제로 알고 있을 때만 허용 (Git 2.30+)

아예 실수를 막으려면 alias로 덮어씁니다.

bash
git config --global alias.pushf "push --force-with-lease --force-if-includes"

3.10 브랜치 전략

GitHub Flow — 단순, 지속 배포에 적합

text
main ─────────────────────────────▶ (항상 배포 가능)
   └─ feature ─┘ PR + 리뷰 + CI → merge → 즉시 배포

규칙: main은 항상 배포 가능 / 모든 작업은 브랜치에서 / PR로 리뷰 / 머지하면 바로 배포. 웹 서비스, SaaS, 스타트업에 적합합니다.

Git Flow — 복잡, 버전 릴리스 제품에 적합

text
main    ────●───────────────●────── (릴리스 태그만)
             ╲             ╱
release       ╲──────────●
               ╲        ╱
develop ──●─────●──────●──────●───── (개발 통합)
           ╲   ╱        ╲    ╱
feature     ●─●          ●──●
hotfix  ────────────●────────  (main에서 분기 → main + develop)

브랜치가 5종류라 무겁습니다. 여러 버전을 동시에 유지보수하는 패키지/설치형 소프트웨어, 또는 정해진 배포 주기가 있는 SI·엔터프라이즈 환경에 맞습니다.

Trunk-Based Development — 초단기 브랜치

main 하나에 모두가 하루 이내의 짧은 브랜치로 계속 합칩니다. 미완성 기능은 Feature Flag로 숨깁니다. 배포 자동화와 테스트 커버리지가 충분한 팀에서만 성립합니다.

선택 기준: 배포 주기가 짧고 버전 개념이 없다 → GitHub Flow. 버전을 여러 개 동시 유지보수한다 → Git Flow. CI/CD가 매우 성숙하다 → Trunk-Based. 팀 규모보다 배포 방식이 결정 요인입니다.

3.11 Issues와 프로젝트 관리

markdown
<!-- 이슈 본문에서 -->
Closes #42        <!-- PR 머지 시 이슈 자동 종료 -->
Fixes #42
Resolves #42
Related to #42    <!-- 연결만, 종료 안 함 -->
 
@username         <!-- 멘션 -->
#42               <!-- 이슈/PR 참조 -->
user/repo#42      <!-- 다른 저장소 참조 -->
  • Labels — bug, enhancement, good first issue, priority: high
  • Milestones — 릴리스 단위 묶음, 진행률 표시
  • Projects (v2) — 칸반 보드, 커스텀 필드, 이슈/PR 자동 이동
  • Issue Templates — .github/ISSUE_TEMPLATE/bug_report.yml
yaml
name: 버그 리포트
description: 버그를 신고합니다
labels: ["bug"]
body:
  - type: textarea
    id: what-happened
    attributes:
      label: 무슨 일이 일어났나요?
    validations:
      required: true
  - type: input
    id: version
    attributes:
      label: 버전

더 깊이

COMMENTS (…)

댓글을 불러오는 중이에요.

NEW COMMENT0 / 1000
⌘↵ 전송

blog92@web:~$ cd ..