Git 실무 워크플로우 #5 PR 운영 — 리뷰 단위·커밋 메시지·draft PR

7 분 소요

첫 PR을 만들어 머지하는 흐름은 Git 기초 #6 GitHub와 첫 Pull Request에서 다뤘습니다. PR을 만들 줄 아는 것과 팀에서 PR이 잘 돌아가게 만드는 것은 다른 문제입니다. 리뷰가 이틀씩 밀리는 팀, 500줄짜리 PR에 “LGTM” 한 줄만 달리는 팀, 커밋 메시지가 전부 “수정"인 팀의 공통점은 도구가 아니라 운영에 있습니다. 이번 글은 PR을 운영의 관점에서 다룹니다.

총 7편으로 구성됩니다.

  • #1 브랜치 전략 — GitHub Flow와 trunk-based
  • #2 rebase vs merge — 결정 기준과 금기
  • #3 interactive rebase — squash·fixup으로 커밋 정리
  • #4 충돌 해결 — 충돌의 구조와 mergetool·rerere
  • #5 PR 운영 — 리뷰 단위·커밋 메시지·draft PR ← 이번 글
  • #6 stash·cherry-pick·bisect — 일상 도구 셋
  • #7 모노레포와 Git — sparse-checkout·서브모듈·LFS

이번 글은 리뷰 단위를 설계하는 기준부터 커밋 메시지 컨벤션, draft PR과 셀프 리뷰, 그리고 팀 차원의 장치인 PR 템플릿과 branch protection까지 순서대로 정리합니다.

작은 PR — 리뷰 단위를 설계한다 #

PR 운영에서 가장 효과가 큰 원칙 하나를 꼽으면 PR을 작게 유지하는 것입니다. 기준은 두 가지입니다.

  • 한 가지 목적 — PR 하나는 한 문장으로 설명할 수 있어야 합니다. “로그인 버그 수정 + 버튼 색 변경 + 의존성 업데이트"처럼 목적이 나열되기 시작하면 이미 쪼갤 시점을 지난 것입니다.
  • 리뷰 가능한 크기 — 변경이 수백 줄을 넘어가면 리뷰의 질이 급격히 떨어집니다. 리뷰어가 diff를 끝까지 읽는 것을 포기하고 승인 버튼을 누르는 순간, 리뷰는 형식 절차가 됩니다.

작은 PR은 리뷰가 빨리 끝나고, 코멘트가 구체적으로 달리고, 문제가 생겼을 때 revert 범위도 좁습니다. 반대로 큰 PR은 리뷰 대기가 길어지고, 그 사이 main이 전진해 충돌이 쌓이고, 결국 “일단 머지하고 나중에 고치자"로 흘러갑니다.

큰 작업을 쪼개는 법 #

작게 유지하라는 원칙은 알아도, 작업 자체가 큰 경우가 있습니다. 이때는 PR을 시간 순서가 아니라 의존 순서로 쪼갭니다.

  • 선행 리팩터링을 분리합니다. 기능을 넣기 전에 필요한 구조 변경(함수 분리, 이름 변경, 파일 이동)을 동작 변화 없는 별도 PR로 먼저 보냅니다. 리뷰어는 “동작이 같은가"만 확인하면 되므로 큰 diff라도 리뷰가 빠릅니다. 그 위에 올라가는 기능 PR은 순수하게 새 동작만 담게 됩니다.
  • 기능을 세로로 자릅니다. 화면 전체를 한 번에 만드는 대신 데이터 모델 PR, API PR, UI PR처럼 쌓아 올릴 수 있는 단위로 나눕니다.
  • 스택을 쌓는 방법도 있습니다. 앞 PR의 브랜치에서 다음 브랜치를 이어서 만들어 PR을 연쇄시키는 방식입니다. 앞 PR이 머지되면 다음 PR의 base를 main으로 바꿔 순서대로 흘려보냅니다. 도구 없이도 가능하지만 스택이 깊어지면 관리 부담이 커지므로, 두세 단 정도까지가 현실적입니다.

커밋 메시지 — Conventional Commits #

커밋 메시지의 기본 원칙(제목 한 줄, 한 커밋에 한 변경)은 기초 시리즈에서 다뤘습니다. 팀에서는 여기에 형식을 하나 더 얹는 경우가 많습니다. 가장 널리 쓰이는 형식이 Conventional Commits입니다.

Conventional Commits 형식
type(scope): subject

feat(auth): 소셜 로그인 버튼 추가
fix(cart): 수량 0일 때 합계 오류 수정
docs(readme): 로컬 실행 절차 갱신

type이 변경의 성격을 선언하고, 괄호 안의 scope가 영향 범위를 좁혀 줍니다. 대표 type은 다음과 같습니다.

type용도
feat사용자에게 보이는 새 기능
fix버그 수정
refactor동작 변화 없는 구조 개선
docs문서만 변경
test테스트 추가, 수정
chore빌드, 설정, 의존성 등 부수 작업

이 형식의 가치는 읽기 좋다는 것에서 끝나지 않습니다. 형식이 기계로 파싱 가능하므로 체인지로그 자동 생성버전 자동 결정(feat는 minor, fix는 patch)에 그대로 연결됩니다. 커밋 메시지가 릴리스 자동화의 입력이 되는 것입니다.

제목이 “무엇을"을 담는다면 본문에는 “왜"를 적습니다. 코드로 드러나지 않는 배경, 검토했다가 버린 대안, 부작용에 대한 경고는 본문에 적어 둡니다. 반년 뒤 git log를 뒤지는 사람에게 가장 값진 정보는 대부분 본문에 있습니다.

draft PR — 아직 리뷰받을 단계가 아닐 때 #

PR을 여는 시점이 꼭 작업이 끝난 뒤일 필요는 없습니다. GitHub의 draft PR은 “아직 리뷰하지 말라"는 상태 표시가 붙은 PR입니다. 두 가지 용도로 유용합니다.

  • 방향 공유 — 구현 초반에 구조를 잡은 시점에서 draft로 열어 두면, 팀이 diff를 보며 방향에 대한 이견을 일찍 낼 수 있습니다. 다 만든 뒤에 “구조가 잘못됐다"는 코멘트를 받는 것보다 수정 비용이 훨씬 적습니다.
  • CI 선확인 — draft 상태에서도 CI는 돌아가므로, 리뷰어의 시간을 쓰기 전에 테스트와 린트를 먼저 통과시켜 둘 수 있습니다.

Ready for review로 전환하는 시점은 명확합니다. 스스로 머지해도 된다고 판단하는 상태가 되었을 때입니다. “일단 열어 두고 리뷰받으며 마저 만들자"는 접근은 리뷰어의 코멘트와 내 수정이 계속 어긋나게 만들므로 피하는 편이 좋습니다.

셀프 리뷰 — 리뷰어보다 먼저 diff를 읽는다 #

Ready for review를 누르기 전에 마지막 습관이 하나 있습니다. 자기 PR의 Files changed 탭을 처음부터 끝까지 읽는 것입니다. 에디터에서 코드를 쓸 때와 diff로 볼 때는 눈에 들어오는 것이 다릅니다. 셀프 리뷰에서 걸러지는 단골은 정해져 있습니다.

  • 지우지 않은 디버그 출력과 주석 처리된 코드
  • 의도하지 않게 딸려 들어간 파일(로컬 설정, 실험 스크립트)
  • 이 PR의 목적과 무관한 자잘한 수정

리뷰어가 이런 것을 지적하게 만들면 정작 중요한 설계 리뷰에 쓸 에너지가 소모됩니다. 기계적인 문제는 셀프 리뷰와 CI가 거르고, 사람은 판단이 필요한 문제에 집중하게 만드는 것이 좋은 분업입니다.

리뷰 코멘트 — 문화의 최소 합의 #

리뷰 문화는 팀마다 다르지만, 마찰을 줄이는 최소한의 합의는 공통적입니다.

  • 의도를 함께 적습니다. “이렇게 바꾸세요"보다 “동시 요청이 겹치면 이 카운터가 어긋날 수 있어서, 원자적 연산으로 바꾸는 것을 제안합니다"가 받아들이기 쉽습니다.
  • suggested changes를 활용합니다. GitHub 코멘트의 제안 기능을 쓰면 작성자가 버튼 한 번으로 수정을 커밋할 수 있습니다. 오타나 한 줄 수정을 왕복 없이 끝내는 도구입니다.
  • 승인 기준을 합의해 둡니다. “모든 코멘트 해결 후 승인"인지 “사소한 코멘트는 승인 후 반영"인지 팀 차원에서 정해 두면, PR마다 기준이 흔들리지 않습니다.

팀의 장치 — PR 템플릿과 branch protection #

개인의 습관에만 기대지 않고 저장소 차원에서 강제할 수 있는 장치가 두 가지 있습니다.

PR 템플릿은 저장소에 .github/pull_request_template.md 파일을 두면 모든 PR 설명란에 자동으로 채워지는 틀입니다. 변경 요약, 변경 이유, 테스트 방법 정도의 항목만 있어도 설명이 비어 있는 PR이 사라집니다.

.github/pull_request_template.md 예시
## 무엇을 바꿨나

## 왜 바꿨나

## 어떻게 확인했나
- [ ] 테스트 추가 또는 갱신
- [ ] 로컬에서 동작 확인

branch protection은 main 브랜치에 대한 규칙입니다. 저장소 설정에서 다음을 강제할 수 있습니다.

  • PR 없이 main에 직접 push 금지
  • 승인 리뷰 최소 1개 이상
  • 지정한 CI 체크(테스트, 린트) 통과 전 머지 버튼 비활성화

규칙이 사람의 기억력을 대신하게 만들면, “급해서 그냥 push했다"는 사고 자체가 불가능해집니다.

머지 방식 정책과 커밋 정리의 관계 #

기초 #6에서 본 머지 버튼 세 가지(merge commit, squash, rebase)는 팀 정책과 커밋 정리 부담이 맞물리는 지점입니다.

Squash and merge를 팀 정책으로 쓰면 PR 안의 커밋들이 머지 시점에 하나로 합쳐지므로, “리뷰 반영”, “오타 수정” 같은 중간 커밋을 이전 글에서 다룬 interactive rebase로 정리할 부담이 크게 줄어듭니다. 대신 PR 하나가 커밋 하나가 되므로, PR이 크면 히스토리의 해상도도 함께 낮아집니다. 작은 PR 원칙과 squash 정책은 세트로 움직일 때 히스토리가 가장 읽기 좋아집니다.

반대로 커밋 단위를 히스토리에 그대로 남기는 팀(merge commit 또는 rebase and merge)이라면, PR을 열기 전에 커밋을 의미 단위로 정리하는 습관이 그대로 히스토리 품질이 됩니다.

마무리 #

이번 글의 핵심은 네 가지입니다.

  • PR은 한 가지 목적과 리뷰 가능한 크기로 유지하고, 큰 작업은 의존 순서로 쪼갭니다.
  • Conventional Commits는 읽기 좋은 형식을 넘어 체인지로그와 릴리스 자동화의 입력이 됩니다.
  • draft PR로 방향과 CI를 먼저 확인하고, Ready 전에 셀프 리뷰로 기계적인 문제를 걸러 둡니다.
  • PR 템플릿과 branch protection으로 습관을 저장소 차원의 규칙으로 바꿉니다.

다음 글인 “Git 실무 워크플로우 #6 stash·cherry-pick·bisect — 일상 도구 셋"에서는 작업 중 급한 전환, 특정 커밋만 골라 옮기기, 버그 유입 커밋 찾기처럼 필요한 순간에 없으면 곤란한 세 가지 도구를 다루겠습니다.

X