포스트

Git 스터디 노트 (3) Pull Request: 브랜치, 리뷰, 병합 전략

혼자 쓰는 Git에서 팀 협업으로 넘어가는 지점인 Pull Request를 정리합니다. main에 직접 push하지 않는 이유, 브랜치에서 PR을 여는 흐름, fork와 branch 방식의 차이, 코드 리뷰, 그리고 merge/squash/rebase 세 가지 병합 전략을 다룹니다.

Git 스터디 노트 (3) Pull Request: 브랜치, 리뷰, 병합 전략

2편까지가 혼자 쓰는 Git이었다면, 이번 글은 여러 사람이 한 저장소에서 함께 일하는 방식입니다. GitHub의 Pull Request(PR)를 중심으로 다룹니다.

왜 main에 바로 push하지 않는가

혼자 작업할 때는 main 브랜치에 바로 커밋하고 push해도 아무 문제가 없습니다. 하지만 사람이 둘만 돼도 이 방식이 깨집니다.

  • 리뷰 없이 들어간다. 실수나 나쁜 설계가 아무 검토 없이 곧장 공유 브랜치에 반영됩니다.
  • 깨진 코드가 남을 공유된다. 내가 push한 미완성 코드를 동료가 바로 pull로 받아 갑니다.
  • 이력이 엉킨다. 여러 명이 같은 브랜치에 동시에 push하면 충돌과 강제 병합이 반복됩니다.

Pull Request는 이 문제를 “합치기 전에 멈추는 단계”로 해결합니다. 각자 자기 브랜치에서 작업하고, main에 합치기 전에 제안 → 검토 → 승인을 거칩니다. PR은 Git 기능이 아니라 GitHub 같은 호스팅 서비스가 제공하는 협업 기능입니다. 내부적으로는 2편에서 다룬 브랜치와 merge 위에 리뷰와 논의를 얹은 것입니다.

PR의 기본 흐름

한 기능을 작업해서 main에 넣기까지의 표준 흐름은 다음과 같습니다.

1
2
3
git switch -c feat/early-stop    # 1. main에서 작업 브랜치를 딴다
# ... 코드 수정, 의미 단위로 커밋 ...
git push -u origin feat/early-stop   # 2. 브랜치를 원격에 올린다

push하면 GitHub이 “이 브랜치로 PR을 만들겠냐”고 안내합니다. 웹 화면에서 만들거나, GitHub CLI로 바로 열 수 있습니다.

1
gh pr create --title "학습 루프에 조기 종료 추가" --body "검증 손실이 5에폭 개선 없으면 중단"

이후 흐름은 이렇게 이어집니다.

  1. 리뷰: 동료가 diff를 읽고 코멘트를 남기거나 승인(approve)합니다.
  2. 수정: 지적된 부분을 같은 브랜치에서 고쳐 다시 push합니다. PR은 브랜치를 따라가므로 자동으로 갱신됩니다.
  3. 병합: 승인되면 main에 합치고, 작업 브랜치는 삭제합니다.

핵심은 PR = 브랜치 하나를 합치자는 제안이라는 점입니다. 새 커밋을 push하면 그 브랜치를 가리키던 PR에 자동으로 반영되기 때문에, “PR을 다시 만든다”는 일은 거의 없습니다.

fork 방식과 branch 방식

같은 PR이라도 권한에 따라 두 가지 구도가 있습니다.

  • branch 방식 (팀 내부): 저장소에 쓰기 권한이 있는 팀원들이 하나의 저장소 안에서 각자 브랜치를 만들어 PR을 엽니다. origin 하나만 있으면 됩니다.
  • fork 방식 (오픈소스 기여): 남의 저장소에 쓰기 권한이 없을 때, 저장소를 통째로 내 계정으로 복제(fork)한 뒤 내 fork에서 작업하고, 원본을 향해 PR을 엽니다.

fork 방식에서는 원격이 둘이 됩니다.

1
2
3
4
git clone https://github.com/내계정/프로젝트   # 내 fork를 받는다
git remote add upstream https://github.com/원본/프로젝트   # 원본을 upstream으로 등록
git fetch upstream                              # 원본의 최신 변경을 받아
git merge upstream/main                         # 내 브랜치를 최신으로 유지

여기서 origin은 내 fork, upstream은 원본입니다. 오픈소스에 기여할 때 “왜 내 PR이 오래된 코드 기준이냐”는 문제의 대부분은 upstream을 정기적으로 fetch하지 않아서 생깁니다.

좋은 PR을 만드는 법

리뷰받는 쪽에서 지킬 것들입니다. 이건 도구가 아니라 배려의 문제이고, 리뷰 속도를 그대로 결정합니다.

  • 작게 만든다. 500줄짜리 PR은 아무도 제대로 리뷰하지 못합니다. 리뷰가 가능한 크기(대략 한 화면에서 몇 화면)로 쪼갭니다. 큰 기능은 여러 PR로 나눕니다.
  • 하나의 PR = 하나의 목적. 1편의 “커밋 하나 = 의미 하나”가 PR 단위로 올라온 것입니다. “기능 추가 + 리팩토링 + 포맷팅”을 한 PR에 섞으면 리뷰어가 진짜 변경을 찾기 어렵습니다.
  • 본문에 “왜”를 쓴다. 무엇이 바뀌었는지는 diff가 보여줍니다. 본문에는 배경, 접근 방식, 리뷰어가 특히 봐줬으면 하는 부분을 적습니다.
  • 올리기 전에 내 diff를 스스로 읽는다. 디버그 출력, 주석 처리한 코드, 실수로 넣은 파일이 diff에서 대부분 눈에 띕니다.
  • 관련 이슈를 연결한다. 본문에 Closes #42라고 적으면 PR이 병합될 때 그 이슈가 자동으로 닫힙니다.

코드 리뷰: 두 관점

리뷰는 문지기가 아니라 함께 코드를 좋게 만드는 대화입니다. 두 관점을 알아두면 덜 소모적입니다.

리뷰어는 diff에 줄 단위로 코멘트를 남기고, 마지막에 세 가지 중 하나로 응답합니다. 승인(approve), 변경 요청(request changes), 단순 코멘트입니다. 좋은 리뷰 코멘트는 “이건 틀렸다”가 아니라 “여기서 입력이 비면 어떻게 되나요”처럼 질문이나 근거를 담습니다.

작성자는 모든 코멘트에 반응합니다. 고칠 것은 고쳐 다시 push하고, 반영하지 않을 것은 이유를 답글로 남깁니다. 코멘트를 조용히 무시하지 않는 것이 리뷰 문화의 기본입니다.

리뷰는 사람이 아니라 코드를 대상으로 합니다. “너는 왜”가 아니라 “이 코드는 이런 경우에”로 말하고, 지적을 받는 쪽도 코드에 대한 것으로 받아들이면 리뷰가 훨씬 가벼워집니다.

병합 전략: merge, squash, rebase

승인된 PR을 main에 합칠 때 GitHub은 세 가지 버튼을 줍니다. 셋의 차이는 main의 이력이 어떤 모양으로 남는가입니다.

전략main에 남는 모양언제
Merge commit브랜치의 커밋들이 그대로 + 병합 커밋 하나작업 과정을 이력에 보존하고 싶을 때
Squash and merge브랜치의 모든 커밋을 눌러 담은 커밋 하나PR = 커밋 하나로 깔끔하게. 가장 흔함
Rebase and merge브랜치의 커밋들을 병합 커밋 없이 일렬로완전히 선형인 이력을 원할 때

실무에서 가장 널리 쓰이는 것은 Squash and merge입니다. 작업 중의 “wip”, “오타 수정” 같은 지저분한 커밋이 main에는 의미 있는 커밋 하나로 정리되기 때문입니다. 이렇게 하면 1편에서 말한 “커밋 하나 = 의미 하나”가 main 이력 전체에서 지켜집니다.

merge와 rebase의 개념 차이만 짚자면, merge는 두 갈래를 병합 커밋으로 잇고, rebase는 내 커밋들을 상대 브랜치 끝에 옮겨 붙여 갈래가 없던 것처럼 만듭니다. rebase는 이력을 다시 쓰는 작업이라, 2편의 reset과 같은 원칙이 적용됩니다. 공유된 브랜치는 rebase하지 않습니다.

브랜치 전략과 main 보호

팀이 브랜치를 어떻게 운영할지 미리 약속해두는 것을 브랜치 전략이라고 합니다. 소규모 팀에서 가장 단순하고 충분한 것은 이 형태입니다.

  • main항상 배포 가능한 상태로 유지합니다.
  • 모든 작업은 main에서 딴 짧은 수명의 브랜치(feat/..., fix/...)에서 하고, PR로만 합칩니다.
  • 브랜치는 합치면 바로 삭제합니다. 오래 사는 브랜치일수록 충돌이 커집니다.

이 약속을 사람의 선의에만 맡기지 않고 GitHub이 강제하게 할 수 있습니다. 저장소 설정의 branch protection rule로 “main에는 직접 push 금지, PR과 최소 1인의 승인 필수, CI 통과 필수”를 걸어두는 것입니다. 마지막의 “CI 통과 필수”가 4편에서 다룰 자동화와 PR이 만나는 지점입니다.

정리

개념한 줄 요약
PR의 존재 이유합치기 전에 멈춰 제안, 검토, 승인을 거친다
기본 흐름브랜치 → push → PR → 리뷰 → 병합 → 브랜치 삭제
fork vs branch권한 없으면 fork + upstream, 팀 내부면 origin 하나
좋은 PR작게, 목적 하나, 본문엔 “왜”, 올리기 전 스스로 읽기
병합 전략보통 Squash and merge. 공유 브랜치는 rebase 금지
main 보호직접 push 금지, PR과 승인, CI 통과를 규칙으로 강제

PR은 결국 1편의 브랜치와 2편의 merge 위에 사람의 검토를 얹은 것입니다. 도구는 단순하고, 어려운 부분은 대부분 커밋과 PR을 얼마나 읽기 좋게 만드느냐에 있습니다.

다음 글

브랜치 보호 규칙의 “CI 통과 필수”를 실제로 채우려면 자동화가 필요합니다. 4편에서 push와 PR마다 테스트와 린트를 기계가 돌리는 CI, 그리고 병합되면 자동으로 배포되는 CD를 GitHub Actions로 다룹니다.

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.