오늘은 Git으로 팀 협업을 할 때 필요한 것들을 정리했다.
브랜치를 어떤 기준으로 나누는지 / 원격 저장소(GitHub)와 어떻게 주고받는지 / PR과 Fork
실습은 전부 git-practice라는 연습용 저장소에서 IntelliJ로 진행했고, 회차별로 폴더를 나눴다.
git-pratice/
├── day1-merge-practice/ # 지난 회차: 브랜치·병합·충돌
└── day2-branch-practice/ # 이번 회차
└── notice.txt
"여러 명이 동시에 작업하니까" 나누는 게 아니라, 작업의 목적 단위로 나눈다. 목적은 보통 이 셋 중 하나다.
혼자 작업해도 이 단위로 나눈다. 반대로 오타 수정, 주석 정리처럼 몇 분이면 끝나는 아주 작은 수정은 브랜치를 안 만듦. 브랜치도 전환하고 관리하고 병합하는 비용이 들기 때문에.
타입/설명이름만 봐도 이 브랜치가 왜 존재하는지 알 수 있어야 한다. 이번 실습에서 만든 브랜치들:
feature/notice-emoji # notice.txt에 이모지 추가
feature/notice-color # 같은 줄을 '파란색'으로 수정
feature/notice-bold # 같은 줄을 '굵게'로 수정
hotfix/notice-typo # 운영 중 긴급 오타 수정
feature/add-data # Fork 실습에서 서초구 맛집 데이터 추가
feature, fix, hotfix, release 등test, test2, my-branch 같은 이름 금지add-data)가 자연스럽다feature/CI-123-notice-emoji처럼 이슈 번호를 앞에 붙이면 좋다| Git Flow | GitHub Flow | |
|---|---|---|
| 브랜치 종류 | 5가지 (main, develop, feature/, release/, hotfix/*) | 2가지 (main, feature/*) |
| 배포 주기 | 정해진 주기 (월 1회 등) | 수시로 |
| 어울리는 팀 | 정식 QA 단계가 있는 팀 | 빠른 실험·배포가 중요한 소규모 팀 |
정답은 없고, 우리 프로덕트가 어떤 속도로 진행되는지에 따라 고른다. 이 연습 저장소는 개인 학습용이라 정해진 배포 주기도 QA 단계도 없어서 GitHub Flow(main + feature/*)로 진행했다.
헷갈린 부분 :
develop은 자동으로 생기는 게 아니라main처럼 사람이 직접 만드는 일반 브랜치다. Git Flow를 쓰기로 한 팀의 약속일 뿐임.
release/*나 hotfix/*가 main에 병합되는 순간 = 실제로 배포되는 순간이므로, 그 커밋에 v1.2.0, v1.2.1처럼 태그를 찍는다. 이번엔 hotfix 병합 후 v1.0.1을 찍었다.
로컬 저장소는 내 컴퓨터에만 있어서, 컴퓨터가 고장 나면 커밋 히스토리가 통째로 날아간다. 원격 저장소(GitHub)는 이 저장소의 복사본을 서버에 올려두는 것 — 백업 + 협업 + 이력 관리가 된다.
로컬과 원격은 자동으로 동기화되지 않는다. 아래 세 동작으로 직접 맞춰준다.
day2-branch-pratice/notice.txt를 수정·커밋한 뒤 push → GitHub에 반영README.md를 만들어 커밋 → IntelliJ에서 fetch → 내 README.md는 아직 없지만 Git 로그에 origin/main이 한 커밋 앞서 있는 게 보임pull = fetch + merge.README.md가 생김그래서 실무에서는 "일단 fetch로 확인하고 괜찮으면 병합"하는 신중한 흐름과 "그냥 pull로 한 번에"라는 빠른 흐름을 상황 따라 골라 쓴다.
접속 방식은 HTTPS와 SSH가 있는데, SSH 키를 등록해두면 매번 로그인 안 해도 된다.
브랜치에서 작업하고 main에 바로 merge해도 협업은 된다. 그런데 실무에서는 그 전에 다른 사람이 내 코드를 검토하는 단계를 꼭 거친다. 작성자 본인은 자기 코드의 문제를 보기 어렵기 때문.
PR(Pull Request)은 "제 브랜치의 이 변경사항을 main에 반영해도 될까요?"라고 공식적으로 요청하는 절차다.
main은 항상 검증된 코드만 유지된다PR 설명은 "무엇을"보다 "왜"에 초점을 맞춘다. 무엇이 바뀌었는지는 리뷰어가 diff를 보면 이미 안다. 왜 그렇게 했는지는 작성자만 아는, 설명 없이는 알 수 없는 유일한 정보다.
나쁜 예: "notice.txt에 버전 2 추가"
나은 예: "사용자가 현재 문서 버전을 파일에서 바로 확인할 수 있도록 버전 표기를 이어서 씀"
1. 브랜치 만들기 (로컬)
2. 코드 수정 → commit (로컬)
3. push → GitHub에 브랜치 생김
4. PR 열기 (브랜치 → main)
5. 리뷰어가 PR에 코멘트
6. 작성자가 코드 고침 → commit → push ← 또 push! merge 아님
7. PR이 자동으로 갱신됨 (6번 커밋이 PR에 붙음)
8. 다 됐으면 merge (PR의 Merge 버튼) ← 이게 마지막
9. 병합된 브랜치는 삭제
셀프 리뷰 실습에서는 내가 리뷰어(코멘트)와 작성자(수정) 1인 2역을 했다. Files changed 탭에서 줄에 코멘트를 달고, IntelliJ에서 고쳐 커밋·push하니 PR에 커밋이 자동으로 추가됐다.
내가 쓰기 권한이 없는 저장소(오픈소스 등)에 기여하고 싶을 때 쓴다.
upstream, 내 Fork는 originupstream (원본) ← 최종적으로 코드가 반영되길 원하는 곳, PR을 여기로 보냄
│ fork
▼
origin (내 Fork) ← 내가 clone하고 작업하고 push하는 곳
origin(내 Fork)에서 작업하고 push하지만, PR은 내 Fork의 feature 브랜치 → 원본의 main으로 보낸다main은 원본이랑 똑같이 깨끗하게 유지하고, 작업은 항상 새 브랜치에서 한다. 그래야 나중에 upstream에서 원본 업데이트를 편하게 받아올 수 있다File → New → Project from Version Control), upstream 등록도 Git → Manage Remotes로 된다 (이번엔 생략 가능한 부분이라 건너뜀)실습하면서 헷갈렸던 것들. 대부분 "로컬 ≠ 원격"에서 비롯된 문제였다.
IntelliJ에서 지난 회차 브랜치(merge_test, merge_test_A, merge_test_B)를 삭제했는데 GitHub에는 그대로 있었다. 로컬 브랜치 삭제와 원격 브랜치 삭제는 완전히 별개의 작업이다. GitHub 웹의 View all branches에서 직접 지우거나, 원격 브랜치를 대상으로 삭제를 push해야 한다.
v1.0.1 태그를 로컬에서 만들고 main을 push했는데 GitHub 태그 목록에 안 보였다. git push는 기본적으로 브랜치의 커밋만 올리고 태그는 따로 push해야 한다. (IntelliJ Settings → Version Control → Git → Push tags를 All로 바꾸거나 push할 때 옵션 체크)
PR 리뷰 후 코드를 고치고 나서 "이제 merge하나?" 했는데 아니었다.
main에 최종적으로 합치는 것 (맨 마지막 단계)PR은 코드를 수정하는 도구가 아니라, 특정 브랜치를 지켜보는 대화창일 뿐이다. 코드 수정은 항상 로컬에서 하고 push하면 PR이 알아서 갱신된다.
