현대 소프트웨어 개발에서 버전 관리 시스템인 Git은 선택이 아닌 필수다. 하지만 단순히 코드를 저장하는 것을 넘어, 여러 개발자가 하나의 프로젝트에서 효율적으로 협업하기 위해서는 약속된 규칙, 즉 **브랜치 전략(Branching Strategy)**이 필요하다.
본 글에서는 다양한 전략 중 가장 직관적이고 널리 쓰이는 GitHub Flow를 중심으로, 오픈소스 기여나 팀 프로젝트에서 바로 적용할 수 있는 협업 프로세스를 상세히 기술한다.
1. Git과 브랜치 전략의 필요성
Git은 파일의 변경 사항을 추적하고 여러 사용자가 동시에 작업할 수 있게 돕는 분산 버전 관리 시스템이다. 개별 개발자는 자신만의 독립된 작업 공간인 **브랜치(Branch)**를 만들어 기존 코드에 영향을 주지 않고 기능을 구현할 수 있다.
하지만 브랜치를 생성하고 병합하는 과정에 규칙이 없다면, 코드가 뒤섞이거나 충돌(Conflict)이 빈번하게 발생하여 관리 비용이 급증한다. 이를 방지하기 위해 팀의 규모와 프로젝트 성격에 맞는 '브랜치 전략'을 선택해야 한다.
대표적인 브랜치 전략
- Git Flow:
master,develop,feature,release,hotfix등 엄격한 브랜치 구조를 가진다. 대규모 프로젝트나 정기적인 배포 주기가 있는 경우 적합하다. - GitHub Flow:
master브랜치와feature브랜치만 사용하는 단순한 구조다. 수시로 배포가 일어나는 웹 서비스나 지속적 통합(CI/CD)이 활발한 팀에 최적화되어 있다. - GitLab Flow: GitHub Flow의 단순함에 배포 환경(Production, Pre-production)에 따른 브랜치를 추가하여 보완한 방식이다. 지속적 배포를 지향하면서도 환경별 관리가 필요한 상황에 유리하다.
2. GitHub Flow: 단순함 속에 담긴 강력함
GitHub Flow는 "master 브랜치는 언제든 배포 가능한 상태여야 한다" 는 대전제 아래 다음과 같은 단계로 진행된다.
- 브랜치 생성:
master에서 새로운 기능(feature)을 위한 브랜치를 만든다. - 커밋: 작업을 진행하며 수시로 커밋을 남긴다.
- Pull Request(PR) 생성: 작업이 완료되면 코드 리뷰를 요청한다.
- 리뷰 및 토론: 팀원들과 코드를 검토하고 피드백을 주고받는다.
- 테스트 및 머지: 승인된 코드를
master에 병합(Merge)하고 즉시 배포한다.
아래 그림은 이러한 GitHub Flow의 전체적인 흐름을 직관적으로 보여준다.
3. 실전 협업: Fork와 Clone
협업하고자 하는 원본 저장소(Upstream)에 직접 쓰기 권한이 없는 경우, 자신의 계정으로 저장소를 복제하는 Fork 과정이 선행되어야 한다.
3.1. Fork와 로컬 복제(Clone)
- GitHub 웹사이트에서 대상 저장소의 오른쪽 상단 Fork 버튼을 클릭한다.
- 자신의 계정으로 복제된 저장소의 URL을 복사하여 터미널에 다음 명령어를 입력한다.
3.2. Upstream 원격 저장소 등록
Fork한 저장소는 원본 저장소와 연결이 끊어진 상태다. 원본의 최신 코드를 지속적으로 반영하기 위해 upstream이라는 이름으로 원본 저장소를 등록한다.
위 명령어 입력 시, 아래와 같이 origin(내 저장소)과 upstream(원본 저장소)이 모두 정상적으로 등록된 것을 확인할 수 있다.
merge와 rebase는 단순히 코드를 합치는 수단을 넘어, 프로젝트의 히스토리를 어떻게 관리할 것인가에 대한 철학의 차이를 보여준다. 특히 원본 저장소(upstream)와 내 로컬 작업 공간(origin/local)의 상태가 서로 다를 때(Diverged), 두 방식이 이력을 처리하는 메커니즘을 상세히 설명한다.
4. 로컬 master 브랜치 최신화
내가 기능을 개발하는 동안에도 원본 저장소(upstream)의 master에는 다른 개발자들의 코드가 계속 머지된다. 내 로컬의 master와 원격 저장소(origin)를 원본과 동일하게 유지하는 것은 협업의 기본이다.
4.1. 정석적인 방법: Checkout 후 최신화
터미널에서 원본의 변경 사항을 직접 가져와 내 로컬 master에 병합하는 방식이다.
- 동작 원리 (Fast-Forward): 일반적인 경우, 로컬
master에서 직접 커밋을 생성하지는 않는다. 따라서 내 로컬master는upstream/master보다 뒤처져만 있는 상태다. 이때 머지를 수행하면 Git은 별도의 머지 커밋을 만들지 않고 포인터만 최신으로 이동시키는 Fast-Forward(FF) 방식을 사용한다.
4.2. Merge vs Rebase: master 최신화에서는 무엇이 다를까?
결론부터 말하자면, 로컬 master에서 별도의 작업을 하지 않았다면 두 방식의 결과는 완전히 같다. 앞서 언급한 'Fast-Forward'가 발생하기 때문이다. 하지만 로컬 master에 실수로 직접 커밋을 남긴 경우, merge는 머지 커밋을 생성하고 rebase는 해당 커밋을 최신 이력 뒤로 재배치한다는 차이가 있다.
4.3. Checkout 없이 master 최신화하기 (추천)
매번 브랜치를 전환하지 않고도 현재 작업 브랜치를 유지한 채 master를 최신화할 수 있는 효율적인 방법이다.
이 방법은 로컬 master에 저장하지 않은 변경 사항이 있을 경우 이를 덮어씌우지만, master에서 직접 작업하지 않는 원칙을 지킨다면 가장 빠르고 깔끔한 방법이다.
4.4. 원격 저장소(origin)와 동기화
로컬 master를 최신화한 후에는 내 원격 저장소(origin)에도 동일한 변경 사항을 반영해야 한다.
5. 작업 브랜치(feature)에 최신 변경 사항 반영
이제 동기화한 master의 최신 코드를 내가 작업 중인 feature-branch에 합칠 차례다. 여기서 merge와 rebase 중 프로젝트의 관리 철학에 맞는 방식을 선택한다.
5.1. 방식 1: Merge (과거를 보존하는 병합)
- 장점: 작업의 선후 관계와 머지된 시점의 기록이 명확히 남는다.
- 결과: 두 줄기가 합쳐진 'Merge Commit' 이 생성되며, 히스토리가 갈라졌다가 합쳐지는 모습이 시각적으로 남는다.
5.2. 방식 2: Rebase (기반을 재설정하는 재배치, 권장)
- 장점: 내 작업의 시작점(Base)을 최신
master의 끝으로 옮긴다. 히스토리가 일직선(Linear History)으로 유지되어 가독성이 압도적으로 좋아진다. - 결과: 내 커밋들이 마치 방금
master에서 따온 브랜치에서 작업한 것처럼 재배치된다.
6. PR 생성부터 머지까지
작업이 완료되었다면 이제 원본 저장소에 내 코드를 반영해달라고 요청한다. Pull Request(PR) 전 반드시 master를 최신화하고 내 브랜치에 반영하는 과정을 거쳐야 충돌 없는 깔끔한 PR이 가능하다.
6.1. Push 및 PR 생성
push -f가 필요할 수 있다.이후 GitHub에서 'Compare & pull request' 버튼을 클릭하여 작업 내용을 상세히 적고 PR을 생성한다.
6.2. 리뷰 및 수정
팀원들이 개선 사항을 남기면, 로컬에서 코드를 수정하고 다시 커밋한 뒤 푸시한다. PR은 자동으로 업데이트된다.
6.3. 머지(Merge)와 브랜치 삭제
모든 리뷰가 통과되면 메인테이너가 Merge pull request를 눌러 master에 반영한다. 반영 후에는 로컬과 원격의 작업 브랜치를 정리한다.
7. Git을 통한 협업의 효과
GitHub Flow 전략을 준수하면 다음과 같은 이점을 얻을 수 있다.
- 코드 품질 향상: PR 과정을 통해 동료의 검토를 거치므로 버그를 사전에 방지할 수 있다.
- 지식 공유: 팀원들이 서로의 코드를 읽으며 기술적인 맥락과 노하우를 공유하게 된다.
- 안정적인 서비스 운영: 언제든 배포 가능한
master상태를 유지함으로써 개발 속도와 안정성을 동시에 잡을 수 있다.
결국 Git 협업의 핵심은 기술적인 명령어 숙달보다 **"동료와 어떻게 소통하며 코드를 쌓아갈 것인가"**에 대한 약속을 지키는 것에 있다. 이 가이드가 여러분의 성공적인 협업에 도움이 되기를 바란다.