문서 읽는 데 48분 · A5

A-5: Pull Request로 함께 고치기

목차 29
전체 8강 중 5강 · Git·GitHub
난이도 · 입문

ℹ️코드 버전 관리와 협업의 기본기 — 어떤 트랙을 가든 처음에 익혀두면 좋아요.

또 만났네요, 홍순구 튜터입니다. 지난 시간 우리는 내 저장소를 인터넷(GitHub)에 올리고, 어디서든 받아오고, push 가 거부될 땐 pull 로 받아 합치는 협업의 기본 리듬까지 손에 넣었어요. 그런데 그 마지막에 제가 한 가지 찜찜한 걸 남겨뒀죠 — 우리는 main곧바로 push 했어요.

혼자 쓰는 연습 저장소라 괜찮았어요. 그런데 동료 다섯 명이 같은 프로젝트에서 다들 main 에 직접 밀어 넣는다고 상상해보세요. 누구 코드가 맞는지 아무도 검토 안 한 채로 섞이고, 지난 시간에 본 그 거부와 충돌이 하루에도 몇 번씩 터질 거예요. 그래서 팀은 main 에 바로 올리지 않아요. 대신 "내가 이렇게 바꿨는데, 합쳐도 될까요?" 하고 제안서를 내고 동료의 검토를 받은 뒤 합치죠.

그 제안서가 바로 오늘의 주인공, Pull Request(풀 리퀘스트, 줄여서 PR — 변경을 합쳐달라는 요청) 예요. 처음엔 PR·리뷰·이슈 같은 단어가 낯설고, "내 코드를 남이 본다" 는 게 좀 부담스러울 수도 있어요. 괜찮아요. 현업 신입도 다들 처음엔 그래요. 오늘 하나씩 천천히 해보면, 끝날 즈음엔 "아, 이래서 팀이 이렇게 일하는구나" 하고 협업의 뼈대가 잡힐 거예요.

텍스트
   오늘의 여정 — main을 지키며 여럿이 함께 고치기

   ① 왜 main에 직접 밀어 넣지 않을까 — 협업의 안전장치
   ② Pull Request란 — 합쳐도 될까요? 라는 제안서
   ③ PR을 직접 올려보기 — 기능 브랜치에서 제안서까지
   ④ 코드 리뷰 — 코멘트·승인·변경 요청
   ⑤ 이슈(Issue) — 버그와 할 일을 추적하는 곳
   ⑥ PR과 이슈를 잇기 — Closes #12 한 줄
   ⑦ 합치는 세 가지 방법 — merge·squash·rebase
   ⑧ PR을 병합하고 정리하기 — 협업 한 바퀴 닫기

①에서 왜 PR 이 필요한지 깔고, ②에서 PR 이 무엇인지 개념을 잡아요. ③에서 직접 PR 을 하나 올려보고, ④에서 리뷰를 주고받아요. ⑤⑥에서 이슈로 할 일을 추적하고 PR 과 잇는 법을 익히고, ⑦에서 합치는 세 가지 방법을 비교해요. 마지막 ⑧에서 실제로 병합하고 정리하며 협업 한 바퀴를 닫습니다.

💡 오늘 수업의 핵심 — "Pull Request 는 'main 에 합쳐도 될까요?' 하고 내는 제안서다. 기능 브랜치를 push 해서 PR 을 열고, 동료의 리뷰(코멘트·승인·변경 요청)를 받은 뒤 합친다. 이슈는 버그와 할 일을 추적하는 곳이고, Closes #12 한 줄로 PR 과 이어진다."

🎯 학습 목표

  • main 에 직접 push 하지 않는지 이해하고, 기능 브랜치 → push → Pull Request 생성으로 변경을 제안합니다.
  • PR 에 코드 리뷰(코멘트·승인·변경 요청)를 주고받고, 이슈(Issue) 로 버그와 할 일을 추적하며 Closes #12 로 PR 과 잇습니다.
  • 머지 방식 3종(merge commit·squash·rebase merge)의 차이를 구분하고, PR 을 병합한 뒤 브랜치를 정리해 협업 한 바퀴를 닫습니다.

Step 1: "왜 main에 직접 밀어 넣지 않을까 — 협업의 안전장치"

오프닝에서 던진 질문부터 풀어요. 지난 시간 우리는 git pushmain 에 곧바로 올렸어요. 혼자라면 아무 문제 없죠. 그런데 팀이 되는 순간 이게 왜 위험한지, 그림으로 먼저 봐요.

텍스트
   다섯 명이 모두 main에 직접 push 하면

        개발자 A ─┐
        개발자 B ─┤
        개발자 C ─┼──  main   (검토 없이 마구 섞임)
        개발자 D ─┤
        개발자 E ─┘

    누가 무엇을 왜 바꿨는지 아무도 모르고,
     누군가 깨뜨려도 그게 누구 탓인지 찾기 어렵다.

검토 없이 모두가 main 에 직접 올리면, 그 main 이 언제 깨질지 모르는 살얼음판이 돼요. 빌드가 안 되는 코드가 섞여 들어와도 막을 길이 없고, 그 위에서 다른 사람들이 또 작업을 쌓으니 사고가 눈덩이처럼 커지죠.

그래서 팀은 main보호(protected branch) 해둬요. GitHub 저장소 설정에서 "이 브랜치엔 직접 push 금지, 반드시 PR 을 거쳐서, 최소 1명의 승인을 받은 뒤에만 합칠 수 있다" 는 규칙을 걸어두는 거예요. 그러면 아무리 급해도 누구도 main 에 함부로 밀어 넣을 수 없고, 모든 변경이 검토라는 관문을 한 번 통과하게 됩니다.

텍스트
   해결: main을 '보호'하고, 변경은 PR로만 합치게 한다

        개발자 ── 기능 브랜치 ── PR(동료 검토·승인) ── main

⚠️ 현업 가면 거의 모든 팀이 main 을 이렇게 잠가둬요. 신입 때 "어? main 에 push 가 안 되는데요?" 하고 당황하는 경우가 많은데, 그건 여러분을 막는 게 아니라 지켜주는 거예요. 직접 못 미는 게 정상이고, PR 로 올리면 됩니다.

💡 한 줄 정리

main 에 직접 push 하면 검토 없이 섞여 사고가 난다. 팀은 main 을 보호(protected branch)하고, 모든 변경을 Pull Request 로만 합쳐 동료의 검토라는 관문을 거치게 한다.

🙋 학생 질문 — "튜터님, 그럼 혼자 하는 프로젝트도 꼭 PR을 써야 하나요?"

꼭 써야 하는 건 아니에요. 혼자 쓰는 저장소라면 main 에 바로 커밋하고 push 해도 아무도 안 다쳐요.

그런데 연습 삼아 혼자서도 PR 을 써보는 걸 추천해요. 기능 브랜치를 따서 작업하고, 셀프 PR 을 올려 내 변경을 한 번 쭉 훑어보고 합치는 습관을 들이면, 나중에 팀에 들어갔을 때 그 흐름이 이미 익숙하거든요. 또 혼자 하는 프로젝트라도 "이 변경이 무슨 일을 했는지" 가 PR 단위로 기록돼서, 한참 뒤에 돌아봐도 역사가 읽기 좋아요.

그래서 오늘 과제도 혼자서 셀프 PR 을 한 바퀴 돌려보는 것부터 시작해요. 동료가 없어도 충분히 연습할 수 있어요.


Step 2: "Pull Request란 — 합쳐도 될까요? 라고 묻는 제안서"

이제 PR 이 정확히 무엇인지 잡아요. 한 줄로 말하면, Pull Request 는 "내 기능 브랜치를 main 에 합쳐도 될까요?" 하고 동료에게 내는 제안서예요. 그냥 합쳐버리는 게 아니라, 합치기 전에 한 번 멈춰서 "이거 봐주세요" 하고 검토를 요청하는 거죠.

PR 이 열리면 GitHub 가 한 페이지에 세 가지를 모아서 보여줘요. ① 무엇이 바뀌었는지(변경된 코드 비교, diff), ② 그것에 대한 대화(코멘트), ③ 합쳐도 좋다는 승인 여부예요. 이 한 페이지에서 동료가 변경을 읽고, 질문하고, "좋아요" 하거나 "여기 고쳐주세요" 하는 모든 일이 벌어집니다.

텍스트
   Pull Request의 한살이 — 브랜치에서 시작해 병합으로 끝난다

   1. 기능 브랜치에서 작업하고 커밋한다
   2. 브랜치를 origin에 push 한다
   3. PR을 연다 — "이 브랜치를 main에 합쳐도 될까요?"
   4. 동료가 리뷰한다 — 코멘트·승인·변경 요청
   5. 승인되면 main에 병합(merge)한다
   6. 다 쓴 기능 브랜치를 삭제한다

이 여섯 단계가 오늘 우리가 통째로 익힐 흐름이에요. 1·2번은 이미 배운 것들이에요 — 브랜치는 A-2, push 는 A-4 에서 했죠. 3번부터 6번까지가 오늘의 새 내용이고요. 그러니까 PR 은 완전히 새로운 기술이라기보다, 지금까지 배운 브랜치와 push 위에 "검토" 라는 관문을 하나 얹은 거라고 보면 돼요.

💡 한 줄 정리

PR 은 한 브랜치를 다른 브랜치(보통 main)에 합쳐달라는 제안서다. 변경된 코드 비교, 동료와의 대화, 승인이 한 페이지에 모인다. 브랜치 → 리뷰 → 병합이 전체 흐름이다.

🙋 학생 질문 — "튜터님, 올리는 건데 왜 이름이 '풀(pull, 당기기)' 리퀘스트예요?"

이름 때문에 헷갈리는 분 많아요. 분명 내가 변경을 올리는(push) 건데 왜 "당기기" 요청이냐는 거죠.

이건 합치는 쪽(저장소 주인)의 입장에서 붙은 이름이에요. 내가 "제 변경을 당겨가서(pull) 합쳐주세요" 하고 주인에게 요청하는 거라, 받는 쪽 기준으로 Pull Request 인 거예요. 내 시점이 아니라 상대 시점의 이름이라 처음엔 어색하죠.

참고로 GitLab 같은 다른 서비스에선 같은 걸 Merge Request(머지 리퀘스트) 라고 불러요. "합치기 요청" 이라 뜻이 더 직관적이죠. 둘은 이름만 다르고 하는 일은 똑같아요. GitHub 를 기준으로 배우니 우리는 PR 로 부를게요.


Step 3: "PR을 직접 올려보기 — 기능 브랜치에서 제안서까지"

말로만 들으면 안 와닿으니, 직접 PR 을 하나 올려봐요. 지난 시간에 GitHub 에 올려둔 연습 저장소가 있다는 전제로 갈게요. 순서는 간단해요. ① 기능 브랜치를 따서 작업하고, ② 그 브랜치를 push 하고, ③ GitHub 에서 PR 을 연다. 앞의 두 단계는 복습이에요.

Bash
# 1. 기능 브랜치를 만들어 이동 (A-2에서 배운 switch)
git switch -c feature/add-profile

# 2. 파일을 고치고 커밋
echo "이름: 홍길동" >> profile.txt
git add profile.txt
git commit -m "프로필에 이름 한 줄 추가"

# 3. 기능 브랜치를 원격에 올리기 (A-4에서 배운 push)
git push -u origin feature/add-profile
# remote: Create a pull request for 'feature/add-profile' on GitHub by visiting:
# remote:      https://github.com/USERNAME/my-diary/pull/new/feature/add-profile

push 결과의 마지막 줄을 보세요. GitHub 가 친절하게 "이 브랜치로 PR 을 만들려면 이 주소로 가세요" 하고 링크를 줘요. 새 브랜치를 처음 올리면 이 안내가 떠요. 그 주소를 누르거나, 아니면 그냥 GitHub 저장소 페이지를 새로고침하면 위쪽에 "Compare & pull request" 라는 초록 버튼이 보여요. 그걸 누르면 PR 만드는 화면으로 넘어가요.

PR 화면에서 두 가지만 확인하면 돼요. 하나는 방향이에요. base: main ← compare: feature/add-profile 처럼 돼 있는데, "feature/add-profile 의 변경을 main 으로 합친다" 는 뜻이에요.

또 하나는 제목과 설명이에요. 무엇을 왜 바꿨는지 적어주면, 리뷰어가 코드를 보기 전에 맥락을 먼저 잡을 수 있어요. 다 적었으면 "Create pull request" 를 누르면 PR 이 열립니다.

혹시 아직 작업 중이라 리뷰는 이르다면, "Create pull request" 옆 화살표에서 "Create draft pull request" 를 고를 수 있어요. "흐름은 미리 공유하되 아직 보지는 마세요" 라고 표시하는 거예요. 다 되면 "Ready for review" 로 바꾸면 됩니다. 신입이 "미완성인데 올려도 되나" 망설일 때 딱 쓰는 기능이에요.

터미널을 떠나기 싫다면, GitHub CLI(gh)로 한 줄에 PR 을 열 수도 있어요.

Bash
# 터미널에서 바로 PR 만들기 (GitHub CLI)
gh pr create --base main --head feature/add-profile \
  --title "프로필에 이름 추가" \
  --body "프로필 파일에 이름 한 줄을 추가했습니다."

🌟 GUI 로는 — GitHub Desktop 에서 브랜치를 push 하면 "Create Pull Request" 버튼이 떠요. 누르면 브라우저의 PR 작성 화면으로 바로 연결돼요. VS Code 라면 GitHub Pull Requests 확장으로 편집기 안에서 PR 을 만들 수 있고요.

💡 한 줄 정리

기능 브랜치를 git push 로 올리면 GitHub 가 PR 생성 링크를 알려준다. 웹에서 방향(base: main ← compare: 내 브랜치)을 확인하고 제목·설명을 적어 PR 을 연다. gh pr create 로 터미널에서 바로 열 수도 있다.

🙋 학생 질문 — "튜터님, PR 제목이랑 설명엔 뭘 적어야 해요?"

좋은 커밋 메시지를 쓰듯이 적으면 돼요. 제목엔 "무엇을 했는지" 를 한 줄로, 설명엔 "왜 그렇게 했는지, 어떻게 확인하면 되는지" 를 적어요.

리뷰어 입장에서 생각하면 쉬워요. 동료가 내 PR 을 처음 열었을 때, 코드만 덩그러니 있으면 "이게 뭘 하려는 거지?" 부터 헤매요. 그런데 설명에 "로그인 화면 정렬이 깨져서 고쳤고, 모바일·데스크탑에서 확인했습니다" 같은 한두 줄이 있으면, 리뷰어가 곧장 코드의 의도를 알고 봐줄 수 있죠.

처음엔 거창하게 안 써도 돼요. "무엇을, 왜" 두 가지만 한 문장씩 있어도 충분히 좋은 PR 이에요. 빈칸으로 올리는 것만 피하면 됩니다.


Step 4: "코드 리뷰 — 코멘트·승인·변경 요청"

PR 을 열었으면, 이제 리뷰 차례예요. 코드 리뷰는 동료(또는 나 자신)가 변경을 읽고 의견을 다는 과정이에요. PR 페이지의 "Files changed" 탭으로 가면 바뀐 코드가 줄 단위로 보이는데, 특정 줄 옆의 + 를 누르면 바로 그 줄에 코멘트를 달 수 있어요. "여기 변수 이름이 헷갈려요", "이 부분 좋네요" 처럼 콕 집어 대화하는 거죠.

코멘트를 다 달고 리뷰를 제출할 땐 세 가지 중 하나를 골라요. 이게 리뷰의 핵심이에요.

텍스트
   리뷰를 제출할 때 고르는 세 가지

   [Comment]          그냥 의견만 — 합쳐도 될지 판단은 미룸
   [Approve]          좋아요, 합쳐도 됩니다 — 승인
   [Request changes]  이건 고쳐주세요 — 고치기 전엔 합치지 말자

Approve(승인) 가 모이면 PR 은 "합쳐도 좋다" 는 신호를 받은 거예요. 보호된 main 이라면 보통 이 승인이 1개 이상 있어야 병합 버튼이 열려요. 반대로 Request changes(변경 요청) 가 달리면 "지금 이대로는 합치지 말자, 먼저 고치자" 는 뜻이라, 작성자가 고쳐서 다시 push 하면 PR 이 자동으로 갱신돼요. 같은 PR 위에서 고치고 → 다시 보고 → 승인하는 대화가 오가는 거죠.

Bash
# 터미널에서 리뷰 제출하기 (GitHub CLI, 42 = PR 번호)
gh pr review 42 --approve                       # 승인
gh pr review 42 --comment --body "여기 좋네요"   # 의견만
gh pr review 42 --request-changes --body "이 부분 고쳐주세요"

🌟 GUI 로는 — PR 페이지 우측의 "Reviewers" 에서 리뷰어를 지정하고, "Files changed" → "Review changes" 버튼에서 위 세 가지(Comment·Approve·Request changes)를 골라 제출해요. 혼자 연습할 땐 셀프 리뷰로 자기 코드를 한 번 훑어볼 수 있어요.

💡 한 줄 정리

코드 리뷰는 PR 의 'Files changed' 에서 라인별 코멘트를 달고, 리뷰를 제출할 때 Comment·Approve·Request changes 중 하나를 고른다. 승인이 모이면 합칠 수 있고, 변경 요청이 달리면 고친 뒤 다시 본다.

🙋 학생 질문 — "튜터님, 리뷰에서 지적받으면 제 코드가 별로라는 뜻인가요? 좀 무서워요."

이거 정말 많이 묻는 질문이고, 신입 때 누구나 식은땀 흘리는 부분이에요. 먼저 안심부터 시켜드릴게요 — 리뷰는 사람을 평가하는 게 아니라 코드를 함께 더 낫게 만드는 대화예요.

생각해보면, 아무리 베테랑 개발자도 혼자 짠 코드엔 본인이 못 보는 빈틈이 있어요. 다른 눈이 한 번 봐주면 그 빈틈이 합쳐지기 전에 잡히죠. 그래서 좋은 팀일수록 리뷰가 활발해요. 변경 요청이 달리는 건 "네 실력이 부족하다" 가 아니라 "이 코드가 더 좋아질 여지가 있다" 는 뜻이에요.

오히려 리뷰를 거치며 동료의 시각을 배우는 게 신입이 가장 빨리 크는 길이에요. 지적을 공격으로 받지 말고 "공짜 과외" 라고 생각해보세요. 그리고 리뷰어도 사람을 깎는 말이 아니라 코드를 짚는 말을 하도록, 서로 예의를 지키는 게 좋은 팀 문화고요.


Step 5: "이슈(Issue) — 버그와 할 일을 추적하는 곳"

PR 이 "코드 변경" 을 다루는 곳이라면, 이슈(Issue) 는 "해야 할 일과 버그" 를 다루는 곳이에요. 한마디로 저장소에 붙은 할 일 목록이자 버그 신고함이에요. "로그인 버튼이 모바일에서 안 눌려요" 같은 버그도, "프로필에 소개글 칸을 추가하자" 같은 할 일도 전부 이슈로 적어둬요.

이슈를 열면 자동으로 번호가 붙어요(#12 처럼). 그리고 두 가지 정리 도구가 있어요. 라벨(label) 은 이슈에 색깔 꼬리표를 다는 거예요 — bug, enhancement(기능 개선), docs(문서) 처럼요. 마일스톤(milestone) 은 "이번 v1.1 출시 전까지 끝낼 것들" 처럼 여러 이슈를 한 목표로 묶는 거예요.

텍스트
   이슈는 저장소에 붙은 '할 일 / 버그' 목록이다

   #12  로그인 버튼이 모바일에서 안 눌림      라벨: bug          담당: 나
   #13  프로필에 소개글 칸 추가              라벨: enhancement  마일스톤: v1.1
   #14  README에 설치법 작성                라벨: docs

이슈가 메모장과 다른 점은, 저장소에 묶여 모두가 보고 함께 추적한다는 거예요. 누구나 댓글로 논의할 수 있고, 담당자를 정할 수 있고, 곧 볼 것처럼 PR·커밋과 연결돼요(다음 Step). 그래서 "이 버그 누가 보고 있더라?", "이번 주 뭘 끝내기로 했더라?" 가 흩어지지 않고 한곳에 쌓여요.

특히 담당자(Assignee) 를 정해두면 그 일에 확실한 주인이 생겨요. 팀에서 일이 공중에 떠서 아무도 안 잡는 상황을 막아주죠 — 신입 때 담당자가 비어 있어 일이 흐지부지되는 경우가 의외로 많거든요.

Bash
# 터미널에서 이슈 만들기 (GitHub CLI)
gh issue create --title "로그인 버튼이 모바일에서 안 눌림" \
  --body "아이폰 사파리에서 로그인 버튼 터치가 안 됩니다." \
  --label bug

🌟 GUI 로는 — GitHub 저장소의 "Issues" 탭 → "New issue" 로 만들어요. 오른쪽 사이드바에서 라벨·담당자(Assignees)·마일스톤을 지정하고요. 팀이 쓰는 "Projects" 보드에 이슈를 카드처럼 올려 칸반으로 관리하기도 해요.

💡 한 줄 정리

이슈(Issue)는 버그와 할 일을 추적하는 티켓이다. 번호(#12)가 붙고, 라벨로 분류하고, 마일스톤으로 목표 버전까지 묶는다. 메모장과 달리 저장소에 묶여 모두가 함께 추적한다.

🙋 학생 질문 — "튜터님, 이슈랑 그냥 메모장에 할 일 적는 거랑 뭐가 달라요?"

핵심 차이는 "저장소에 묶여 있고, 코드와 이어진다" 는 거예요.

내 메모장에 적은 할 일은 나만 봐요. 동료는 내가 무슨 버그를 잡고 있는지 알 수 없죠. 반면 이슈는 저장소에 공개돼서 팀 전체가 보고, 댓글로 같이 논의하고, "이건 내가 할게요" 하고 담당을 가져갈 수 있어요. 흩어진 카톡·메모가 아니라 한곳에 모이는 거예요.

게다가 이슈는 코드와 직접 연결돼요. 다음 Step 에서 배울 Closes #12 한 줄이면, 그 버그를 고친 PR 과 이슈가 자동으로 이어져요. 나중에 "이 코드 왜 이렇게 고쳤지?" 하고 거슬러 올라가면 → PR → 이슈 → 원래 버그 상황까지 한 번에 추적돼요. 메모장으론 절대 못 하는 일이죠.


Step 6: "PR과 이슈를 잇기 — Closes #12 한 줄"

이제 PR 과 이슈를 하나로 잇는 마법 같은 한 줄을 배워요. PR 설명이나 커밋 메시지에 Closes #12 라고 써두면, 그 PR 이 병합되는 순간 12번 이슈가 자동으로 닫혀요. 버그를 고친 코드가 합쳐지면, 그 버그 신고도 알아서 "해결됨" 처리되는 거죠.

Bash
# 커밋 메시지에 한 줄 — 이 PR이 합쳐지면 #12 이슈가 자동으로 닫힌다
git commit -m "로그인 버튼 모바일 터치 영역 수정

Closes #12"

키워드는 세 가지가 다 똑같이 동작해요 — Closes #12, Fixes #12, Resolves #12. 셋 중 아무거나 써도 PR 병합 시 해당 이슈가 닫혀요. 보통 버그엔 Fixes, 일반 할 일엔 Closes 를 즐겨 쓰지만 규칙은 아니에요. 반대로 그냥 #12 라고만 쓰면 이슈로 가는 링크만 걸리고 자동으로 닫히진 않아요. "닫지는 말고 참고만 연결" 하고 싶을 때 그렇게 써요.

이 한 줄이 왜 중요할까요? 시간이 지나면 진가가 드러나요. 6개월 뒤 누군가 이상한 코드를 발견하고 "이거 왜 이렇게 짰지?" 하고 거슬러 올라간다고 해봐요. 커밋 → 그 커밋이 속한 PR → PR 이 닫은 이슈 → 이슈에 적힌 원래 버그 상황과 논의까지, 맥락이 한 줄기로 쭉 이어져요. Closes #12 한 줄을 안 적었다면 이 연결고리가 끊겨, 그 코드의 사연을 영영 모르게 되죠.

텍스트
   버그  이슈  PR  커밋이 한 줄기로 이어진다

   이슈 #12 (버그 신고)  ── Closes #12 ──  PR #42 (수정)  ──  커밋들
                              병합되면 #12 자동으로 닫힘

🌟 GUI 로는 — PR 을 만드는 화면의 설명(Description) 칸에 Closes #12 를 직접 적어도 돼요. 커밋 메시지가 아니라 PR 본문에 넣어도, 병합되는 순간 그 이슈가 똑같이 자동으로 닫혀요.

💡 한 줄 정리

PR 본문이나 커밋 메시지에 Closes #12(또는 Fixes·Resolves)를 쓰면, PR 이 병합될 때 그 이슈가 자동으로 닫힌다. 버그 → 이슈 → PR → 커밋이 한 줄기로 이어져 나중에 맥락을 통째로 추적할 수 있다.

🙋 학생 질문 — "튜터님, 그냥 #12라고만 써도 이슈가 닫히나요?"

아니요, 그게 중요한 차이예요. #12 라고만 쓰면 이슈로 가는 링크만 걸리고, 자동으로 닫히진 않아요.

이슈를 자동으로 닫으려면 반드시 Closes·Fixes·Resolves 같은 키워드를 앞에 붙여야 해요. Closes #12 처럼요. GitHub 가 이 키워드를 보고 "아, 이 PR 이 합쳐지면 12번 이슈를 닫으라는 거구나" 하고 알아듣는 거죠.

그래서 둘을 골라 쓰면 돼요. 이 PR 로 그 이슈가 끝나는 거면 Closes #12(닫기), 그냥 "관련 있으니 참고로 연결만" 하고 싶으면 #12(링크만). 닫고 싶었는데 키워드를 빠뜨려서 이슈가 안 닫히는 실수가 흔하니, "닫을 땐 Closes 를 꼭 붙인다" 를 기억하세요.


Step 7: "합치는 세 가지 방법 — merge·squash·rebase"

리뷰가 끝나 합칠 때가 되면, GitHub 는 합치는 방법을 세 가지 중에 고르게 해줘요. 셋 다 "feature 를 main 에 합친다" 는 결과는 같지만, 합친 뒤 역사의 모양이 달라져요. 기능 브랜치에 커밋 D, E 두 개가 있다고 치고 하나씩 봐요.

텍스트
   ① Merge commit — 있는 그대로, 병합 커밋 M을 남긴다

        A───B───C───────M    main
             \         /
              D───E───┘

   갈래(브랜치가 있었다는 사실)가 그대로 보존된다.
텍스트
   ② Squash and merge — D·E를 하나로 합쳐 main에 얹는다

        A───B───C───DE    main

   feature의 여러 커밋이 깔끔한 한 개(DE)로 압축된다.
텍스트
   ③ Rebase and merge — D·E를 main 끝에 한 줄로 펴서 얹는다

        A───B───C───D'───E'    main

   병합 커밋 없이 선형(한 줄) 역사가 된다.

세 방법을 언제 쓰는지 표로 정리하면 이래요.

방식 역사 모양 언제 쓰나
Merge commit 갈래 보존, 병합 커밋 M 생김 있었던 일을 그대로 남기고 싶을 때(감사 추적·오픈소스)
Squash and merge 여러 커밋 → 한 개로 압축 자잘한 커밋을 깔끔히 정리 — 입문·일반 팀이 가장 많이 쓰는 무난한 기본
Rebase and merge 선형(한 줄), 병합 커밋 없음 갈래 없이 한 줄 역사를 원할 때

여기서 한 가지 짚어둘게요. 이건 GitHub 가 버튼 한 번으로 대신 해주는 합치기예요. 우리가 직접 손으로 치는 git rebase 와는 달라요. 손으로 하는 rebase 는 강력한 만큼 조심할 게 많은데(특히 "공개된 브랜치는 rebase 금지" 라는 황금률), 그건 한참 뒤 A-7 에서 따로 제대로 다뤄요. 지금은 "GitHub 가 합칠 때 세 가지 모양 중 고를 수 있다" 정도만 알면 충분해요.

💡 한 줄 정리

GitHub 은 PR 을 합칠 때 세 가지를 고를 수 있다. Merge commit 은 갈래를 남기고(M), Squash 는 여러 커밋을 한 개로 압축하고, Rebase merge 는 한 줄로 펴 병합 커밋 없이 얹는다. 입문 단계에선 Squash 가 무난하다.

🙋 학생 질문 — "튜터님, 셋 중에 뭘 골라야 할지 모르겠어요."

처음엔 다 비슷해 보여서 고르기 어렵죠. 결론부터 말하면, 팀에 들어가면 그 팀이 정해둔 규칙을 따르면 돼요. 보통 저장소 설정에서 한 가지만 허용해두는 경우도 많아서, 고민할 필요조차 없을 때가 많아요.

혼자 연습하거나 아직 규칙이 없다면 Squash and merge 를 기본으로 쓰면 무난해요. 작업하다 보면 "오타 수정", "다시 수정", "진짜 수정" 같은 자잘한 커밋이 쌓이기 마련인데, Squash 는 그걸 깔끔한 한 개로 압축해서 main 의 역사를 읽기 좋게 만들어주거든요.

그리고 아까 말했듯이, 이건 GitHub 가 대신 해주는 거라 직접 git rebase 를 칠 때처럼 위험하지 않아요. 마음 편히 골라도 돼요. 손으로 하는 rebase 의 주의사항과 황금률은 A-7 에서 제대로 배울 거예요.


Step 8: "PR을 병합하고 깔끔히 정리하기"

마지막이에요. 리뷰가 승인됐으니 실제로 합치고 뒷정리까지 해서 협업 한 바퀴를 닫아요. GitHub PR 페이지의 "Merge pull request" 버튼을 누르고(앞에서 고른 방식으로), "Confirm merge" 하면 main 에 합쳐져요. 합친 직후 GitHub 가 "Delete branch" 버튼을 보여주는데, 다 쓴 기능 브랜치는 여기서 지워주면 돼요.

그런데 여기서 끝이 아니에요. 방금 합쳐진 건 원격(GitHub)의 main 이에요. 내 컴퓨터의 로컬 main 은 아직 그 변경을 몰라요. 그래서 로컬을 최신으로 맞춰줘야 해요.

Bash
# (터미널에서 합치고 원격 브랜치까지 지우기 — GitHub CLI)
gh pr merge 42 --squash --delete-branch

# 병합 뒤 로컬을 최신으로 맞추기
git switch main          # main으로 돌아와서 (A-2)
git pull                 # 합쳐진 변경 받아오기 (A-4)

# 쓸모를 다한 로컬 기능 브랜치 삭제 (A-2)
git branch -d feature/add-profile

이 뒷정리가 왜 필요한지 보세요. 원격 main 엔 내 변경이 합쳐졌지만, 로컬 main 은 PR 을 올리던 그 시점에 멈춰 있어요. git switch main 으로 돌아와 git pull 하면, 그제야 로컬 main 도 합쳐진 최신 상태가 돼요. 그리고 기능 브랜치는 이미 main 에 흡수됐으니 역할이 끝났죠. 원격은 "Delete branch" 로, 로컬은 git branch -d 로 지워서 깔끔하게 마무리해요.

🌟 GUI 로는 — PR 페이지에서 "Merge pull request" → "Confirm merge" → "Delete branch" 버튼을 차례로 누르면 병합과 원격 브랜치 삭제가 끝나요. 로컬 동기화(switch main + pull)와 로컬 브랜치 삭제는 GitHub Desktop·VS Code 의 브랜치 메뉴에서도 할 수 있어요.

이걸로 한 바퀴가 완성됐어요. 기능 브랜치 → 커밋 → push → PR → 리뷰 → 병합 → 정리. 이 한 바퀴가 여러분이 앞으로 어떤 팀, 어떤 프로젝트에 가도 매일 돌리게 될 협업의 기본 리듬이에요.

💡 한 줄 정리

리뷰가 승인되면 'Merge pull request' 로 합치고, 다 쓴 기능 브랜치는 삭제한다. 로컬에선 git switch main && git pull 로 합쳐진 결과를 받고 git branch -d 로 정리한다. 이걸로 브랜치 → PR → 리뷰 → 병합 → 정리 한 바퀴가 끝난다.

🙋 학생 질문 — "튜터님, 합친 브랜치는 왜 굳이 지워요? 남겨두면 안 돼요?"

남겨둬도 동작에는 아무 문제 없어요. 다만 쌓이면 저장소가 지저분해져서 지우는 걸 권해요.

기능 브랜치는 "이 변경을 작업하는 임시 작업대" 예요. PR 이 병합되면 그 변경은 이미 main 에 안전하게 들어갔으니, 작업대는 역할을 다한 거죠. 그걸 안 지우면 feature/add-profile, fix/login, feature/old-stuff 처럼 끝난 브랜치가 수십 개씩 쌓여서, 정작 지금 살아 있는 브랜치가 뭔지 찾기 힘들어져요.

혹시 "지웠다가 나중에 필요하면 어쩌지?" 걱정된다면 안심하세요. 합쳐진 내용은 이미 main 에 있으니 사라지는 게 아니고, GitHub 는 삭제한 브랜치도 한동안 "Restore branch" 로 되살릴 수 있어요. 그러니 끝난 브랜치는 가볍게 지우는 습관이 깔끔한 저장소를 만들어요.

여러 개가 한꺼번에 쌓였을 땐 git branch --merged이미 main 에 합쳐진(=지워도 안전한) 브랜치만 추려 볼 수 있어요. 거기 뜬 것만 골라 지우면 깔끔하죠. 그리고 원격에서 지워진 브랜치의 흔적(origin/...)이 내 쪽에 남아 있으면 git fetch --prune 으로 한 번에 청소해요.


마무리

오늘 우리는 git 을 "혼자 쓰는 도구" 에서 "여럿이 안전하게 함께 쓰는 도구" 로 한 단계 끌어올렸어요. 왜 main 에 직접 밀어 넣지 않는지 이해하고, 기능 브랜치로 Pull Request 를 올리고, 동료의 리뷰를 받고, 이슈로 할 일을 추적하고 Closes #12 로 이어 붙이고, 세 가지 방식 중 골라 병합한 뒤 깔끔히 정리하는 한 바퀴까지 돌려봤죠. 이제 여러분은 "내 코드를 검토받고 안전하게 합치는" 협업의 뼈대를 갖췄어요.

오늘 배운 핵심 세 가지

  • 💡 하나 — PR 은 '합쳐도 될까요?' 라고 묻는 제안서다. main 은 보호하고, 모든 변경은 기능 브랜치 → push → PR → 리뷰 → 병합의 관문을 거친다. 직접 push 가 막히는 건 사고가 아니라 안전장치다.
  • 💡 둘 — 리뷰와 이슈가 협업의 두 축이다. 리뷰는 Comment·Approve·Request changes 로 코드를 함께 다듬고, 이슈는 버그·할 일을 추적한다. Closes #12 한 줄이 이슈와 PR 을 한 줄기로 잇는다.
  • 💡 셋 — 합치는 방식은 세 가지, 정리까지가 한 바퀴다. Merge commit·Squash·Rebase merge 중 골라 병합하고(입문엔 Squash 무난), 병합 뒤엔 git switch main && git pull 로 로컬을 맞추고 다 쓴 브랜치를 지운다.

다음 시간 예고

오늘 우리가 돌린 한 바퀴 — main 을 보호하고, 기능 브랜치를 따서, PR 로 검토받고 합치는 이 흐름 — 사실 여기엔 이름이 있어요. 가장 널리 쓰이는 이 협업 방식을 GitHub Flow 라고 불러요. 다음 시간엔 이 GitHub Flow 를 비롯해, 팀이 git 으로 협업하는 여러 약속(브랜치 전략)을 정리해요.

그리고 한 가지 더. 지금까지는 내 저장소에서만 PR 을 올렸는데, 세상엔 내가 권한 없는 남의 공개 저장소(오픈소스) 가 가득해요. 거기에 기여하려면 어떻게 PR 을 올릴까요? 그때 등장하는 게 fork(포크)와 upstream(업스트림) 이에요. A-4 에서 잠깐 예고했던 그 upstream 이라는 두 번째 원격을 다음 시간에 드디어 만나요. 오픈소스에 내 첫 PR 을 올리는 것까지 같이 해봅니다.


과제

오늘 배운 건 눈으로만 보면 절대 안 익어요. 꼭 여러분 GitHub 계정과 연습 저장소에서 직접 PR 을 올리고 합쳐보세요. 동료가 없어도 셀프 PR 로 충분히 한 바퀴를 돌려볼 수 있어요. 직접 손으로 병합 버튼을 눌러봐야 "아, 이래서 팀이 이렇게 일하는구나" 가 몸에 새겨져요.

[기초] 셀프 PR로 협업 한 바퀴 돌려보기

지난 시간 GitHub 에 올려둔 연습 저장소에서, 기능 브랜치 하나로 PR 을 만들어 끝까지 합쳐보세요.

git switch -c feature/연습 으로 브랜치를 따고, 파일을 한 줄 고쳐 커밋한 뒤 git push -u origin feature/연습 으로 올리세요. GitHub 에서 "Compare & pull request" 로 PR 을 열고(제목·설명을 한 줄씩 채워서), 셀프로 "Merge pull request" 까지 눌러 합치세요.

마지막으로 git switch main && git pull 로 로컬을 맞추고, 다 쓴 브랜치를 git branch -d 로 지우세요. 그리고 "PR 을 거치는 것과 main 에 바로 push 하는 것이 무엇이 다른지" 를 한 문장으로 적어보세요.

[응용] 이슈를 만들고 PR로 자동으로 닫기

연습 저장소에 이슈를 하나 만들고, 그 이슈를 고치는 PR 로 자동으로 닫아보세요.

먼저 "Issues" 탭에서 할 일을 하나 적어 이슈를 만들고 번호(#N)를 확인하세요(라벨도 하나 달아보세요). 그다음 기능 브랜치에서 그 할 일을 처리하고, 커밋 메시지나 PR 설명에 Closes #N 을 적어 PR 을 올리세요.

PR 을 병합한 뒤 "Issues" 탭으로 가서 그 이슈가 자동으로 닫혔는지 확인하세요. 마지막으로, 만약 Closes 키워드 없이 그냥 #N 이라고만 썼다면 무슨 차이가 났을지 한 문장으로 적어보세요.

[심화] 팀 상황을 상상하며 변경 요청을 주고받기

이런 팀 상황을 떠올려보세요. "동료가 올린 PR 을 내가 리뷰하는데, 한 군데 고쳤으면 하는 부분이 보인다." 이 상황을 직접 만들어볼게요.

PR 을 하나 올린 뒤(짝이 있으면 서로의 저장소에, 혼자면 셀프 리뷰로), "Files changed" 에서 특정 줄에 코멘트를 달고 "Request changes" 로 리뷰를 제출하세요. 그다음 작성자 입장에서 그 부분을 고쳐 다시 커밋·push 하고, PR 이 자동으로 갱신되는 걸 확인하세요.

마지막으로 "Approve" 로 승인한 뒤 병합하세요. 그리고 "변경 요청 → 수정 → 승인 → 병합" 으로 이어지는 이 대화가, 그냥 바로 합치는 것에 비해 팀에 어떤 안전을 주는지 한두 문장으로 적어보세요.


생각해볼 주제

1. 셀프 머지가 가능한데도, 왜 굳이 남에게 검토를 맡길까

혼자 하는 프로젝트라면 PR 을 셀프로 합쳐도 아무 문제 없어요. 그런데 팀에선 거의 항상 동료의 리뷰와 승인을 거치죠. 내 코드는 내가 제일 잘 아는데, 왜 굳이 남이 검토하게 둘까요? "버그를 한 번 더 거른다" 를 넘어, 리뷰가 팀에 주는 또 다른 가치(지식 공유, 코드 일관성, 책임 분산)는 무엇일지 생각해보세요. 반대로 리뷰가 지나치면 생기는 비용(속도 저하)도 함께 떠올리면 균형이 보일 거예요.

2. 역사를 '깔끔하게' vs '있는 그대로' — Squash와 Merge commit 중 무엇이 옳을까

Squash 는 자잘한 커밋을 한 개로 압축해 main 의 역사를 깔끔하게 만들어요. 반면 Merge commit 은 브랜치가 있었다는 사실과 모든 커밋을 그대로 남기죠. 어느 쪽이 "더 옳은" 걸까요? 사실 정답은 없고 트레이드오프예요. 깔끔한 역사가 주는 이득과, 있는 그대로의 역사가 주는 이득(예: 나중에 문제 추적, 누가 언제 무엇을)을 견줘보세요. 어떤 팀·프로젝트에서 어느 쪽이 더 어울릴지 상상해보면 좋아요.

3. Closes #12 한 줄은 6개월 뒤 누구를 구할까

Closes #12 를 안 적어도 PR 은 잘 합쳐져요. 당장은 아무 차이가 없죠. 그런데 이 한 줄이 만드는 "이슈 ↔ PR ↔ 커밋" 의 연결은 시간이 지나야 진가가 드러나요. 6개월·1년 뒤, 누군가 이상한 코드를 발견하고 그 사연을 추적해야 할 때 이 연결이 무엇을 해줄지 생각해보세요. 당장 이득이 없는 일을 습관으로 들이는 게 왜 협업에서 중요한지, 좋은 커밋 메시지를 쓰는 일(A-1)과도 엮어 풀어보면 한 줄기로 이어질 거예요.

✅ 예시 답안정답 보기

이 문서는 A-5 「Pull Request로 함께 고치기」의 과제와 생각해볼 주제에 대한 예시답안입니다. 정답을 외우는 용도가 아니라, 직접 PR 을 올리고 합쳐본 결과를 어떻게 정리하고 설명하는지 흐름을 참고하는 용도로 보세요. 특히 PR 은 직접 올리고, 리뷰하고, 합쳐봐야 감이 잡힙니다. 답안을 보기 전에 꼭 먼저 여러분 연습 저장소에서 셀프 PR 을 한 바퀴 돌려보세요.


과제 예시답안

🎯 [과제 1 예시답안] 셀프 PR로 협업 한 바퀴 돌려보기

채점 포인트

항목 배점 기준
브랜치 + push 25% 기능 브랜치를 따 커밋하고 git push -u origin <브랜치> 로 올렸는가
PR 생성 25% 제목·설명을 채워 PR 을 열었는가(빈칸 아님)
병합 + 정리 30% "Merge pull request" 로 합치고 로컬 동기화 + 브랜치 삭제까지 했는가
차이 설명 20% PR 을 거치는 것과 main 직접 push 의 차이를 적었는가

풀이 예시

기능 브랜치를 따서 한 줄 고치고 올려요.

Bash
git switch -c feature/연습
echo "소개: 안녕하세요" >> profile.txt
git add profile.txt
git commit -m "프로필에 소개 한 줄 추가"

git push -u origin feature/연습
# remote: Create a pull request for 'feature/연습' on GitHub by visiting:
# remote:      https://github.com/USERNAME/my-diary/pull/new/feature/연습

GitHub 저장소 페이지를 새로고침하면 "Compare & pull request" 버튼이 떠요. 눌러서 방향(base: main ← compare: feature/연습)을 확인하고, 제목과 설명을 한 줄씩 채운 뒤 "Create pull request" 로 PR 을 엽니다. 셀프 연습이니 그대로 "Merge pull request" → "Confirm merge" 로 합치고, "Delete branch" 로 원격 브랜치를 지워요. 마지막으로 로컬을 맞춰요.

Bash
git switch main
git pull
# Updating a1b2c3d..e4f5a6b
# Fast-forward
#  profile.txt | 1 +

git branch -d feature/연습
# Deleted branch feature/연습 (was a1b2c3d).

차이에 대해: main 에 직접 push 하면 검토라는 관문 없이 곧장 섞여요. PR 을 거치면 합치기 전에 변경을 한 번 멈춰 들여다보고(셀프든 동료든) 합치게 되죠. 혼자일 땐 차이가 작아 보이지만, 그 "한 번 멈춰 보는" 습관이 팀에선 사고를 막는 안전장치가 돼요.

💡 튜터의 한마디 — 이 과제의 핵심은 병합 버튼을 직접 눌러보는 거예요. 머리로만 알던 흐름을 손으로 한 바퀴 돌리면, "PR 이 뭔지" 가 비로소 몸에 붙어요. 셀프 PR 이라 리뷰가 좀 허전하게 느껴졌다면 정상이에요 — 다음 과제와 심화에서 이슈 연결과 리뷰까지 채우면 진짜 협업의 모양이 완성돼요. 그리고 병합 뒤 pull 을 빠뜨리면 로컬 main 이 뒤처진다는 것, 이번에 손으로 느꼈을 거예요. 그 감각을 기억해두세요.

🎯 [과제 2 예시답안] 이슈를 만들고 PR로 자동으로 닫기

채점 포인트

항목 배점 기준
이슈 생성 25% "Issues" 탭에서 이슈를 만들고 번호·라벨을 확인했는가
Closes 연결 35% 커밋·PR 에 Closes #N 을 적어 PR 을 올렸는가
자동 닫힘 확인 25% 병합 후 이슈가 자동으로 닫힌 걸 확인했는가
키워드 차이 설명 15% Closes #N#N 만 쓸 때의 차이를 적었는가

풀이 예시

먼저 "Issues" 탭 → "New issue" 로 할 일을 하나 만들어요. 예를 들어 제목 "프로필에 소개글 칸 추가", 라벨 enhancement 로요. 만들고 나면 번호가 붙어요(예: #3). 이제 그 일을 기능 브랜치에서 처리하며 커밋 메시지에 Closes #3 을 적어요.

Bash
git switch -c feature/intro
echo "소개: 한 줄 소개입니다" >> profile.txt
git add profile.txt
git commit -m "프로필에 소개글 칸 추가

Closes #3"

git push -u origin feature/intro

GitHub 에서 PR 을 열면, PR 화면에 "이 PR 이 #3 이슈를 닫습니다(This will close #3)" 같은 안내가 보여요. 리뷰·승인 뒤 "Merge pull request" 로 합치고 "Issues" 탭으로 가보면, 3번 이슈가 자동으로 Closed 상태가 돼 있어요. 그 이슈를 열어보면 "어느 PR/커밋이 닫았는지" 까지 링크로 연결돼 있고요.

키워드 차이에 대해: Closes #3 처럼 키워드를 붙이면 PR 병합 시 이슈가 자동으로 닫혀요. 반면 그냥 #3 이라고만 썼다면, 이슈로 가는 링크만 걸리고 자동으로 닫히진 않아요. "이 PR 로 그 일이 끝난다" 면 Closes, "관련 있으니 참고만 연결" 이면 #3 으로 골라 씁니다.

💡 튜터의 한마디Closes #N 한 줄로 이슈가 스르륵 닫히는 걸 처음 보면 작은 감동이 있어요. 별것 아닌 것 같지만, 이게 쌓이면 "이 코드는 어떤 문제를 풀려고 들어왔는지" 가 저장소에 통째로 기록돼요. 닫고 싶었는데 키워드를 빠뜨려 이슈가 안 닫히는 실수가 흔하니, "닫을 땐 Closes 를 꼭 붙인다" 만 기억하면 돼요.

🎯 [과제 3 예시답안] 팀 상황을 상상하며 변경 요청을 주고받기

채점 포인트

항목 배점 기준
라인 코멘트 + 변경 요청 35% 특정 줄에 코멘트를 달고 "Request changes" 로 제출했는가
수정 후 PR 갱신 30% 작성자 입장에서 고쳐 다시 push 하고 PR 이 갱신된 걸 확인했는가
승인 + 병합 20% "Approve" 로 승인하고 병합했는가
안전 설명 15% 변경 요청 대화가 주는 안전을 적었는가

풀이 예시

PR 을 하나 올린 뒤(짝이 있으면 서로의 저장소에, 혼자면 셀프 리뷰로), 리뷰어 입장이 돼요. "Files changed" 탭에서 고쳤으면 하는 줄 옆의 + 를 눌러 코멘트를 달아요. 예: "이 부분 변수 이름이 헷갈려요. name 보다 profileName 이 어떨까요?" 그리고 "Review changes" → "Request changes" 로 제출해요.

이제 작성자 입장으로 돌아와 그 부분을 고쳐요.

Bash
# (작성자) 코멘트대로 수정 후 다시 올리기
git switch feature/intro
# ... 파일 수정 ...
git add profile.txt
git commit -m "리뷰 반영: 변수 이름을 profileName으로"
git push
# 같은 PR이 자동으로 갱신된다 (새 PR을 또 만들 필요 없음)

PR 페이지를 새로고침하면 방금 커밋이 PR 에 그대로 반영돼 있어요. 같은 PR 위에서 대화가 이어지는 거죠. 리뷰어는 수정된 걸 확인하고 "Approve" 로 승인한 뒤 "Merge pull request" 로 합쳐요.

이때 반영을 확인한 코멘트는 리뷰어가 "Resolve conversation" 으로 닫아 "이건 처리됐다" 는 신호를 남겨요. 또 리뷰어가 "이렇게 바꾸면 어떨까요" 하고 직접 코드를 제안(Suggestion)하면, 작성자는 버튼 한 번으로 그 제안을 커밋에 반영할 수 있고요.

안전에 대해: 그냥 바로 합쳤다면 "헷갈리는 변수 이름" 이 그대로 main 에 들어갔을 거예요. 변경 요청 → 수정 → 승인 대화를 거치면, 합쳐지기 전에 한 번 걸러져요. 게다가 그 과정이 PR 에 기록으로 남아, 나중에 "왜 이렇게 고쳤지?" 의 답까지 함께 보존돼요.

💡 튜터의 한마디 — "Request changes" 를 직접 눌러보면 알 거예요. 이게 누굴 혼내는 버튼이 아니라, "같이 더 낫게 만들자" 는 대화의 시작이라는 걸요. 신입 때는 리뷰받는 게 무섭지만, 한 번 이 흐름을 손으로 돌려보면 "아, 이래서 팀이 리뷰를 하는구나" 가 와닿아요. 같은 PR 위에서 고치고 다시 보는 이 리듬이, 현업 협업의 심장이에요.


생각해볼 주제 예시답안

🤔 [생각해볼 주제 1] 셀프 머지가 가능한데도, 왜 굳이 남에게 검토를 맡길까

문제 상황 요약

혼자라면 PR 을 셀프로 합쳐도 되는데, 팀에선 거의 항상 동료의 리뷰와 승인을 거친다. 내 코드는 내가 제일 잘 아는데 왜 굳이 남이 검토하게 둘까. "버그를 한 번 더 거른다" 를 넘어 리뷰가 주는 또 다른 가치와, 지나친 리뷰의 비용까지 견줘보는 주제다.

튜터의 가이드 및 해설

핵심은 리뷰가 "버그 잡기" 보다 훨씬 넓은 일을 한다는 점이다.

물론 첫 번째 가치는 버그를 거르는 거다. 내가 못 본 빈틈을 다른 눈이 잡아준다. 그런데 리뷰의 진짜 힘은 그 너머에 있다. 첫째, 지식이 공유된다. 동료가 내 코드를 읽으면, 그 기능이 어떻게 돌아가는지 아는 사람이 둘이 된다. 내가 휴가를 가도, 퇴사를 해도 그 코드를 아는 사람이 남는다. 팀의 버스 지수(한 사람만 아는 위험)가 낮아지는 것이다.

둘째, 코드가 일관돼진다. 사람마다 짜는 스타일이 다른데, 리뷰를 거치면 "우리 팀은 이렇게 쓴다" 는 결이 자연스럽게 맞춰진다. 셋째, 책임이 분산된다. 합쳐진 코드는 작성자 혼자의 것이 아니라 "리뷰하고 승인한 우리 모두" 의 것이 된다. 문제가 생겨도 한 사람을 탓하는 게 아니라 함께 고치는 분위기가 된다. 특히 신입에겐 리뷰가 가장 빠른 성장 통로다 — 베테랑의 시각을 코드 위에서 직접 배우니까.

물론 비용도 있다. 리뷰가 지나치게 깐깐하거나 느리면, 작은 변경 하나 합치는 데 며칠씩 걸려 팀 전체가 답답해진다. 그래서 성숙한 팀은 균형을 잡는다 — 사소한 변경은 가볍게, 중요한 변경은 꼼꼼히. 리뷰는 목적이 아니라 더 나은 코드를 위한 수단이라는 걸 잊지 않는 것이다.

💡 핵심을 한마디로

리뷰는 버그를 거르는 걸 넘어, 지식을 공유하고(버스 지수↓), 코드를 일관되게 하고, 책임을 함께 나눈다. 신입에겐 가장 빠른 과외다. 다만 지나치면 속도라는 비용이 드니, "사소한 건 가볍게, 중요한 건 꼼꼼히" 의 균형이 좋은 팀을 만든다.

🤔 [생각해볼 주제 2] 역사를 '깔끔하게' vs '있는 그대로' — Squash와 Merge commit 중 무엇이 옳을까

문제 상황 요약

Squash 는 자잘한 커밋을 한 개로 압축해 역사를 깔끔하게 만들고, Merge commit 은 브랜치가 있었다는 사실과 모든 커밋을 그대로 남긴다. 어느 쪽이 더 옳을까. 정답이 없는 트레이드오프임을 전제로, 두 방식이 각각 주는 이득을 견줘보는 주제다.

튜터의 가이드 및 해설

핵심은 "역사를 무엇에 쓸 거냐" 에 따라 답이 갈린다는 점이다.

Squash 의 이득은 읽기 쉬움이다. 기능 하나가 main 에 커밋 한 개로 딱 떨어지니, git log 를 훑으면 "이 프로젝트가 어떤 기능들로 쌓여왔는지" 가 한눈에 들어온다. 작업 도중의 "오타 수정", "다시 수정", "아 진짜 마지막" 같은 노이즈가 사라진다. 대신 잃는 게 있다. 그 기능을 만들며 거친 중간 과정이 통째로 뭉개진다. 나중에 "이 기능 안에서 정확히 어느 단계에서 이 버그가 들어왔지?" 를 따지기 어려워진다.

Merge commit 의 이득은 정직함이다. 있었던 일을 그대로 남기니, 어떤 브랜치에서 어떤 커밋들이 어떤 순서로 작업됐는지 완전히 보존된다. 문제를 잘게 추적해야 할 때(예: git bisect 로 버그가 들어온 커밋 찾기) 유리하다. 대신 갈래가 많아지면 역사가 복잡한 그물처럼 얽혀 한눈에 읽기 어려워진다.

그래서 정답은 "프로젝트의 성격" 이 정한다. 빠르게 기능을 쌓는 일반 웹 서비스 팀은 읽기 쉬운 역사가 더 값져서 Squash 를 즐겨 쓴다. 반면 라이브러리·오픈소스처럼 "누가 언제 무엇을 왜" 의 완전한 추적이 중요한 곳은 Merge commit 으로 사실을 남긴다. 한쪽이 옳고 다른 쪽이 틀린 게 아니라, 무엇을 잃고 무엇을 얻을지 골라잡는 트레이드오프다.

💡 핵심을 한마디로

Squash 는 읽기 쉬운 역사를 얻는 대신 중간 과정을 잃고, Merge commit 은 완전한 추적을 얻는 대신 복잡함을 떠안는다. 정답은 "역사를 무엇에 쓸 거냐" 가 정한다 — 빠른 웹 서비스는 Squash, 추적이 중요한 라이브러리·오픈소스는 Merge commit 이 어울린다.

🤔 [생각해볼 주제 3] Closes #12 한 줄은 6개월 뒤 누구를 구할까

문제 상황 요약

Closes #12 를 안 적어도 PR 은 잘 합쳐지고, 당장은 아무 차이가 없다. 그런데 이 한 줄이 만드는 "이슈 ↔ PR ↔ 커밋" 의 연결은 시간이 지나야 진가가 드러난다. 당장 이득 없는 일을 습관으로 들이는 게 왜 협업에서 중요한지, 좋은 커밋 메시지와도 엮어 풀어보는 주제다.

튜터의 가이드 및 해설

핵심은 "미래의 누군가가 맥락을 거슬러 올라갈 길을 깔아두는 것" 이라는 점이다.

상상해보자. 6개월 뒤, 팀에 새로 들어온 동료(혹은 그때쯤 다 잊어버린 나 자신)가 이상한 코드 한 줄을 발견한다. "이거 왜 이렇게 짰지? 지워도 되나?" 무심코 지웠다간 옛날에 어렵게 고친 버그가 되살아날 수도 있다. 이때 Closes #12 의 연결이 있다면, 그 코드의 커밋 → 그 커밋이 속한 PR → PR 이 닫은 이슈 → 이슈에 적힌 "이런 버그가 있어서 이렇게 고쳤다" 는 사연까지, 한 줄기로 거슬러 올라갈 수 있다. 코드의 "왜" 가 통째로 복원되는 것이다.

연결이 끊겨 있다면 어떻게 될까. 코드는 있는데 그 사연이 어디에도 없다. 그 코드를 처음 짠 사람을 수소문하거나, 최악의 경우 똑같은 버그를 다시 겪고 나서야 이유를 깨닫는다. Closes #12 한 줄을 빠뜨린 대가를, 미래의 누군가가 시간으로 치르는 것이다.

이건 좋은 커밋 메시지를 쓰는 일(A-1)과 정확히 같은 철학이다. 좋은 커밋 메시지도, Closes #12 도, 당장 나에게는 아무 이득이 없다. 둘 다 미래의 독자를 위한 배려다. 협업이 단단한 팀과 그렇지 않은 팀의 차이는, 바로 이 "당장 이득 없지만 나중에 누군가를 구하는" 작은 습관을 모두가 들이느냐에서 갈린다. 코드는 한 번 짜고 끝이 아니라 오래 읽히기 때문이다.

💡 핵심을 한마디로

Closes #12 는 당장 나에겐 이득이 없지만, 6개월 뒤 코드의 "왜" 를 거슬러 올라갈 길을 깔아둔다. 좋은 커밋 메시지와 같은 철학 — 미래의 독자를 위한 배려다. 협업이 단단한 팀은 "당장 이득 없지만 나중에 누군가를 구하는" 작은 습관을 모두가 들인다.

전체 목록 Git·GitHub