3.7 Pull Request — GitHub 협업의 핵심
PR은 Git 기능이 아니라 GitHub 기능입니다. "내 브랜치를 당신 브랜치에 합쳐달라"는 공식 요청이자, 코드 리뷰와 CI가 붙는 지점입니다.
표준 흐름:
# 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
## 변경 사항
<!-- 무엇을 왜 바꿨는지 -->
## 관련 이슈
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
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로, 원격 끝점이 내 로컬 히스토리에 실제로 통합됐는지까지 검사합니다.
더 엄격하게 하려면:
git push --force-with-lease --force-if-includes
# 내 로컬이 원격의 최신 커밋을 실제로 알고 있을 때만 허용 (Git 2.30+)아예 실수를 막으려면 alias로 덮어씁니다.
git config --global alias.pushf "push --force-with-lease --force-if-includes"3.10 브랜치 전략
GitHub Flow — 단순, 지속 배포에 적합
main ─────────────────────────────▶ (항상 배포 가능)
└─ feature ─┘ PR + 리뷰 + CI → merge → 즉시 배포규칙: main은 항상 배포 가능 / 모든 작업은 브랜치에서 / PR로 리뷰 / 머지하면 바로 배포.
웹 서비스, SaaS, 스타트업에 적합합니다.
Git Flow — 복잡, 버전 릴리스 제품에 적합
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와 프로젝트 관리
<!-- 이슈 본문에서 -->
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
name: 버그 리포트
description: 버그를 신고합니다
labels: ["bug"]
body:
- type: textarea
id: what-happened
attributes:
label: 무슨 일이 일어났나요?
validations:
required: true
- type: input
id: version
attributes:
label: 버전더 깊이
- 브랜치 전략을 "통합 비용"의 관점에서 다시 보려면 DevOps 패턴 3강 — 코드 흐름과 통합을 함께 읽어 보세요.