Git 고급 활용법: 팀 협업과 브랜치 관리 모범 사례

들어가며
동료가 단체 채팅에서 저를 불렀습니다. “방금 병합한 코드가 제 로그인 기능을 덮어썼어요. 지금 사용자가 서비스에 로그인하지 못하고 있어요……”
코드를 열어 보니 화면 전체가 충돌 표시로 가득했고, 어느 것이 제 코드이고 어느 것이 동료의 코드인지 구분할 수 없었습니다. 그날 밤 저는 기본 Git 명령을 사용할 줄 아는 것과 팀 협업을 할 줄 아는 것은 전혀 다르다는 사실을 배웠습니다.
브랜치가 너무 많아 본인도 기억하지 못하거나, 충돌이 날까 두려워 코드를 병합하지 못하거나, 커밋 기록이 실타래처럼 엉킨 경험이 있다면 이 글이 도움이 될 것입니다. 워크플로 선택부터 충돌 처리, 커밋 기록 정리, 코드 리뷰 프로세스 구축까지 지난 몇 년간 팀 협업에서 쌓은 실전 경험을 공유합니다. 이 글을 읽고 나면 팀에 적합한 브랜치 전략을 선택하는 법, 병합 충돌을 깔끔하게 해결하는 법, rebase로 커밋 기록을 정리하는 기술, 효율적인 코드 리뷰 프로세스의 핵심을 익힐 수 있습니다.
1장: 팀에 맞는 Git 워크플로 선택하기
솔직히 처음 팀을 이끌었을 때는 어떤 Git 워크플로를 써야 할지 저도 몰랐습니다. Git Flow가 전문적이라는 말을 듣고 그대로 도입했지만, 5명뿐인 팀이 매일 브랜치 관리에만 30분을 쓰게 되었고 결국 모두가 지쳐 버렸습니다.
나중에야 깨달았습니다. 워크플로에는 최고가 있는 것이 아니라 가장 적합한 것이 있을 뿐입니다.
Git Flow vs GitHub Flow vs Trunk Based Development
세 워크플로의 차이를 가장 간단하게 설명해 보겠습니다.
Git Flow는 엄격한 생산 라인 공장과 같습니다. 전용 개발 브랜치(develop), 기능 브랜치(feature), 릴리스 브랜치(release), 긴급 수정 브랜치(hotfix)가 있습니다. Microsoft나 IBM 같은 대기업이 자주 사용하는 이유는 매월 한 버전 출시처럼 명확한 릴리스 주기가 있기 때문입니다.
하지만 소규모 팀에 Git Flow는 너무 무겁습니다. 예전에 저희 팀도 매번 배포할 때 develop에서 release 브랜치를 만들고, 테스트 후 master에 병합한 다음 다시 develop에 병합했습니다. 이 절차만으로도 충분히 고생스러웠습니다.
GitHub Flow는 훨씬 단순합니다. 메인 브랜치(main/master)는 하나뿐이고 모든 기능은 메인 브랜치에서 feature 브랜치를 만들어 개발한 뒤 PR로 다시 병합합니다. 빠른 반복과 지속적 전달을 중시하므로 3~10명 규모의 팀에 특히 적합합니다.
현재 저희 팀도 GitHub Flow와 CI/CD 자동화 테스트를 함께 사용합니다. 거의 매일 여러 번 배포할 수 있고, 팀원도 복잡한 브랜치 규칙을 기억할 필요가 없어 편하게 느낍니다.
Trunk Based Development(트렁크 기반 개발)는 더 극단적입니다. 모든 사람이 메인 브랜치에서 직접 개발하고 브랜치 수명은 보통 하루를 넘지 않습니다. Google이나 Facebook처럼 강력한 자동화 테스트 역량을 갖춘 회사에서야 시도할 수 있는 방식입니다.
일반적인 팀에는 TBD가 다소 과격합니다. 테스트 커버리지가 90% 이상이 아니라면 메인 브랜치를 망가뜨리기 쉽습니다.
2025년의 최신 흐름
최근 여러 자료를 살펴보니 2025년의 흐름은 혼합 모델입니다. 소규모 팀(1~10명)은 대부분 GitHub Flow와 GitHub Actions 또는 GitLab CI 자동화를 사용합니다. 중대형 팀은 상황에 따라 Git Flow의 일부와 GitHub Flow를 결합합니다.
예를 들어 지인의 회사에는 20명 규모의 팀이 있는데, ‘GitHub Flow + release 브랜치’ 혼합 방식을 사용합니다. 평소 개발은 GitHub Flow로 진행하고, 배포를 준비할 때 release 브랜치를 만들어 안정화합니다.
제가 권하는 기준은 다음과 같습니다.
- 1~5명의 소규모 팀: 고민하지 말고 GitHub Flow를 사용합니다.
- 5~15명의 중간 규모 팀: GitHub Flow와 간소화된 release 프로세스를 사용합니다.
- 15명 이상: Git Flow나 혼합 방식을 고려하되 완성도 높은 CI/CD를 함께 구축합니다.
그리고 중요한 점이 하나 더 있습니다. 과도하게 설계하지 마세요. 처음부터 지나치게 복잡한 브랜치 전략을 만들었다가 아무도 지키려 하지 않아 결국 각자 제멋대로 작업하는 상태로 돌아간 팀을 너무 많이 봤습니다.
2장: 브랜치 관리의 황금률
팀 협업에서 가장 골치 아픈 문제는 혼란스러운 브랜치 관리입니다. 예전 프로젝트의 한 저장소에는 원격 브랜치가 70개 넘게 있었고, 그중 적어도 절반은 2년 전에 버려진 브랜치였습니다. 제 브랜치를 찾는 데도 한참 걸렸습니다.
그 뒤 브랜치 관리의 ‘황금률’을 정리해 팀에 적용했고, 저장소가 훨씬 깔끔해졌습니다.
브랜치 명명 규칙
먼저 명명 규칙입니다. 현재 저희는 모든 브랜치 이름에 다음 접두사를 반드시 붙입니다.
feature/- 신규 기능 개발bugfix/- 버그 수정hotfix/- 긴급 수정release/- 릴리스 브랜치
그리고 feature/JIRA-1234-user-login처럼 작업 번호를 반드시 포함합니다.
이렇게 하면 브랜치의 용도와 담당자를 한눈에 파악할 수 있습니다. 예전에는 test라는 브랜치가 있었는데, 3개월 뒤에는 무엇을 테스트한 것인지 아무도 기억하지 못해 결국 삭제했습니다.
다음 주의 사항도 기억하세요.
- Git은 지원하지만 일부 시스템에서 문자가 깨질 수 있으므로 한국어 브랜치 이름을 사용하지 않습니다.
- 공백과
@같은 특수 문자를 사용하지 않습니다. - 단어는 밑줄
_대신 하이픈-으로 연결합니다. 이는 업계의 관례입니다.
브랜치 수명 관리
제가 본 가장 황당한 사례는 feature 브랜치를 3개월 동안 병합하지 않은 경우였습니다. 막상 병합하려니 해결할 수 없을 만큼 충돌이 많아서 결국 기능을 처음부터 다시 작성해야 했습니다.
그래서 지금은 팀에 feature 브랜치의 수명은 3일을 넘기지 않는다고 요구합니다. 기능이 크면 여러 개의 작은 feature로 나눠 단계적으로 병합합니다.
병합 후 즉시 브랜치를 삭제하는 것은 철칙입니다. ‘나중에 필요할지도 모르니’ 삭제를 아까워하는 사람이 많지만, 믿으셔도 됩니다. 그럴 일은 없습니다. Git에는 완전한 기록이 있어 언제든 병합된 코드를 찾을 수 있습니다.
브랜치 삭제 명령은 휴대전화 메모장에도 적어 두었습니다.
# 로컬 브랜치 삭제
git branch -d feature/xxx
# 원격 브랜치 삭제
git push origin --delete feature/xxx
저장소에 이미 사용하지 않는 브랜치가 많이 쌓였다면 다음 명령으로 정리할 수 있습니다.
# main에 이미 병합된 브랜치 확인
git branch --merged main
# 일괄 삭제(주의해서 사용)
git branch --merged main | grep -v "main" | xargs git branch -d
브랜치 보호 정책
또 하나 매우 중요한 점은 메인 브랜치를 보호하는 것입니다.
처음 팀을 이끌 때는 누구나 main 브랜치에 직접 push할 수 있었습니다. 한 번은 인턴이 완성되지 않은 코드를 실수로 push해 전체 테스트 환경이 망가졌습니다.
그 뒤 GitHub/GitLab에 다음과 같은 브랜치 보호 규칙을 설정했습니다.
- main 브랜치에 직접 push하지 못하게 하고 반드시 PR/MR을 거칩니다.
- 병합하려면 최소 1명의 approve가 필요합니다.
- 단위 테스트와 lint를 포함한 CI 검사를 반드시 통과해야 합니다.
설정 방법은 간단합니다. GitHub 저장소의 Settings → Branches → Branch protection rules에서 구성하면 됩니다.
이 규칙은 번거로워 보이지만 사소한 실수를 많이 막아 줍니다. 코드 리뷰 자체도 훌륭한 학습 기회이므로 팀원은 서로의 코딩 스타일과 비즈니스 로직을 배울 수 있습니다.
3장: Git Rebase vs Merge — 언제 무엇을 사용해야 할까?
rebase를 처음 사용했을 때는 정말 당황했습니다. git rebase를 실행한 뒤 수많은 commit ID가 모두 바뀐 것을 보고 코드가 사라진 줄 알아 울 뻔했습니다.
나중에야 rebase와 merge가 모두 코드를 합칠 수 있지만 내부 논리는 완전히 다르다는 것을 이해했습니다.
Merge와 Rebase의 본질적인 차이
일상적인 비유로 설명해 보겠습니다.
Merge는 두 강줄기가 합쳐지는 것과 같습니다. 두 강이 어디에서 만나는지 명확히 보이고, 합류한 뒤에는 더 넓은 강이 됩니다. Git에서 merge는 merge commit을 생성하고 커밋 기록 트리에 뚜렷한 분기를 남깁니다.
Rebase는 지류의 물을 본류 뒤에 ‘다시 붓는’ 것과 같아 처음부터 강이 하나였던 것처럼 보입니다. Rebase는 기록을 다시 쓰고 각 commit을 대상 브랜치 뒤로 ‘옮기기’ 때문에 commit ID가 바뀝니다.
기술적인 관점에서 보면 다음과 같습니다.
- Merge는 브랜치 생성과 병합 과정을 포함한 전체 기록을 보존합니다.
- Rebase는 더 깔끔한 선형 커밋 기록을 만들지만 브랜치의 맥락 정보는 사라집니다.
사용 사례와 황금률
그렇다면 언제 무엇을 써야 할까요? 제 경험상 기준은 다음과 같습니다.
Merge를 사용할 때:
- feature 브랜치를 최종적으로 main 브랜치에 병합할 때
- 팀 공유 브랜치를 병합할 때
- 전체 병합 기록을 보존하고 싶을 때
Rebase를 사용할 때:
- feature 브랜치가 main 브랜치보다 뒤처져 최신 코드를 동기화하고 싶을 때
- 아직 push하지 않은 개인 브랜치의 커밋 기록을 정리할 때
- 선형 커밋 기록을 만들고 싶을 때
하지만 반드시 기억해야 할 황금률이 있습니다.
이미 공유 브랜치에 push한 commit은 절대 rebase하지 마세요!
왜일까요? rebase는 commit ID를 바꿉니다. 다른 사람이 이미 기존 commit을 기반으로 작업했다면 rebase 후 그 사람의 브랜치가 엉키게 됩니다. 동료 한 명이 이 실수를 저지른 뒤 팀 전체가 브랜치를 다시 받아야 했고, 일주일 동안 모두에게 원망을 들었습니다.
그래서 제 원칙은 로컬에서는 자유롭게 rebase하되 push 전에는 신중히 판단한다입니다.
대화형 Rebase 실전
rebase를 이야기할 때 git rebase -i(대화형 rebase)를 빼놓을 수 없습니다. 매우 강력한 기능입니다.
예전에 한 기능을 개발하면서 10번 넘게 commit했고 메시지는 ‘fix’, ‘수정’, ‘다시 수정’처럼 전문적이지 않았습니다. main에 병합하기 전에 git rebase -i를 이용해 십여 개의 commit을 명확한 3개의 commit으로 정리했습니다.
구체적인 방법은 다음과 같습니다.
# 최근 commit 10개 정리
git rebase -i HEAD~10
실행하면 편집기가 열리고 다음과 비슷한 내용이 표시됩니다.
pick a1b2c3d 사용자 로그인 기능 추가
pick e4f5g6h 로그인 bug 수정
pick i7j8k9l 다시 수정
pick m0n1o2p 로그인 로직 최적화
...
작업은 다음과 같이 바꿀 수 있습니다.
pick- 이 commit 유지squash또는s- 이 commit을 이전 commit과 합치기reword또는r- commit 메시지 수정edit또는e- 이 commit에서 멈추고 내용 수정 허용drop또는d- 이 commit 삭제
예를 들어 위 내용을 다음처럼 바꿀 수 있습니다.
pick a1b2c3d 사용자 로그인 기능 추가
squash e4f5g6h 로그인 bug 수정
squash i7j8k9l 다시 수정
reword m0n1o2p 로그인 로직 최적화
그러면 앞의 commit 세 개가 하나로 합쳐지고 네 번째 commit에서는 메시지를 수정하라는 안내가 나옵니다.
솔직히 처음에는 복잡하게 느껴질 수 있지만 몇 번 연습하면 익숙해집니다. rebase 후의 커밋 기록은 정말 훨씬 깔끔해져서 ‘수정’, ‘다시 수정’, ‘또 수정’ 같은 민망한 commit이 더는 남지 않습니다.
2025년의 모범 사례는 로컬에서 rebase로 커밋 기록을 정리하고 메인 브랜치에 병합할 때는 merge를 사용하는 것입니다. 이렇게 하면 메인 브랜치의 명확한 기록을 유지하면서 중요한 병합 정보도 보존할 수 있습니다.
4장: 더는 충돌을 두려워하지 않기
Git 충돌에는 정말 좋지 않은 기억이 있습니다. 처음 충돌을 만났을 때 화면 가득한 <<<<<<<와 >>>>>>>를 보고 무엇을 해야 할지 전혀 몰라 결국 제 코드를 모두 삭제하고 다시 작성했습니다.
나중에는 충돌이 그렇게 두려운 것이 아니라는 사실을 알게 되었습니다. 핵심은 충돌 표시의 의미를 이해하고 예방 기법을 익히는 것입니다.
충돌 예방 모범 사례
실제로 많은 충돌은 예방할 수 있습니다. 현재 저희 팀에는 다음 사항을 요구합니다.
1. 하루에 최소 한 번 메인 브랜치 동기화
매일 작업을 시작하기 전에 다음을 실행합니다.
git checkout main
git pull origin main
git checkout feature/xxx
git merge main # 또는 git rebase main
그러면 feature 브랜치가 너무 뒤처지지 않아 병합할 때 충돌도 훨씬 줄어듭니다.
2. 작게 커밋하고 빠르게 병합
앞서 말했듯이 feature 브랜치의 수명은 3일을 넘기지 않습니다. 브랜치가 오래 존재해 메인 브랜치와 차이가 커질수록 충돌 가능성이 높아집니다.
3. 팀 협업 전에 먼저 소통
두 사람이 같은 파일을 수정해야 한다면 미리 알리는 것이 좋습니다. 현재 저희 팀은 Notion에 ‘개발 중인 기능’ 표를 만들어 누가 어떤 파일을 수정하는지 한눈에 파악합니다.
충돌 표시 이해와 수동 해결
예방해도 충돌은 발생할 수 있습니다. 그때 다음과 같은 표시를 보게 됩니다.
<<<<<<< HEAD
function login(username, password) {
// 내 코드
return api.post('/login', { username, password });
}
=======
function login(email, password) {
// 동료의 코드
return api.post('/auth/login', { email, password });
}
>>>>>>> feature/new-login
이 표시는 이해하기 어렵지 않습니다.
<<<<<<< HEAD부터=======까지는 현재 브랜치의 코드입니다.=======부터>>>>>>> feature/new-login까지는 병합하려는 브랜치의 코드입니다.
어느 부분을 남길지 결정하거나 두 코드를 결합해야 합니다. 이 예에서는 동료와 의논해 username과 email 중 무엇으로 로그인할지, API 경로는 무엇으로 바꿀지 결정해야 할 수 있습니다.
모든 충돌을 해결한 뒤 표시를 삭제하고 다음을 실행합니다.
git add .
git commit -m "로그인 기능의 병합 충돌 해결"
시각화 도구로 효율 높이기
솔직히 충돌이 많을 때 수동으로 해결하는 일은 매우 피곤합니다. 지금은 주로 VS Code의 내장 충돌 해결 도구를 사용합니다.
VS Code에서는 충돌 파일이 강조 표시되고 다음 버튼이 나타납니다.
- “Accept Current Change” - 내 코드 유지
- “Accept Incoming Change” - 상대 코드 유지
- “Accept Both Changes” - 양쪽 모두 유지
- “Compare Changes” - 비교 화면 열기
버튼만 누르면 충돌을 해결할 수 있어 수동 편집보다 훨씬 편리합니다.
충돌이 특히 복잡하면 git mergetool과 p4merge 같은 전문 도구를 사용합니다. 기준 버전, 내 버전, 상대 버전을 3열로 비교할 수 있어 더 명확합니다.
충돌 해결 후 검증
한 번은 충돌을 해결하고 commit한 뒤 곧바로 push했는데, 동료의 핵심 로직 일부를 삭제한 사실을 나중에 알았습니다. 그 때문에 프로덕션 기능에 문제가 생겼고, 그 경험 이후 충돌을 해결한 뒤에는 반드시 검증한다는 습관을 들였습니다.
이제 충돌을 해결할 때마다 다음을 수행합니다.
- 로컬 테스트를 실행해 기능이 정상인지 확인합니다.
git diff로 변경 사항을 살펴보고 코드가 잘못 삭제되지 않았는지 확인합니다.- 가능하면 동료에게도 review를 요청합니다.
몇 분밖에 걸리지 않지만 많은 프로덕션 장애를 예방할 수 있습니다.
5장: 효율적인 코드 리뷰 프로세스 구축하기
솔직히 경력 초반에는 코드 리뷰가 정말 싫었습니다. 제가 작성한 코드가 이미 훌륭한데 왜 다른 사람이 이래라저래라 해야 하는지 이해하지 못했습니다. 리뷰 의견에 따라 계속 수정하는 것도 시간 낭비처럼 느껴졌습니다.
나중에 직접 다른 사람의 코드를 리뷰하면서 코드 리뷰의 가치는 단순히 ‘bug 찾기’에 그치지 않는다는 것을 이해했습니다.
코드 리뷰의 진정한 가치
지금은 코드 리뷰에 적어도 세 가지 큰 가치가 있다고 생각합니다.
1. 지식 공유 - 팀원이 서로 무엇을 하는지 알게 해 정보 고립을 방지합니다. 저도 다른 사람의 PR을 리뷰하다가 특정 기능을 이렇게 구현할 수 있다는 사실을 여러 번 배웠습니다.
2. 품질 보증 - 눈이 한 쌍 더 있으면 자신이 놓친 문제를 발견할 수 있습니다. 한 번은 재귀 함수를 작성하고 테스트에도 문제가 없었지만, 리뷰한 동료가 경계 조건을 처리하지 않았다는 사실을 발견해 스택 오버플로를 막을 수 있었습니다.
3. 기술 성장 - 좋은 코드를 읽는 것은 최고의 학습법입니다. 제가 아는 코딩 기법 중 상당수는 senior 엔지니어의 PR을 리뷰하며 배운 것입니다.
PR 모범 사례
효율적인 코드 리뷰를 위해서는 PR 자체를 잘 작성해야 합니다. 현재 저희 팀은 다음 규칙을 사용합니다.
PR 크기를 적절하게 유지
PR 하나는 200~400줄이 가장 좋고 최대 500줄을 넘기지 않습니다. 너무 큰 PR은 아무도 꼼꼼히 보고 싶어 하지 않아 리뷰가 형식적으로 끝나기 쉽습니다.
기능이 크다면 여러 개의 작은 PR로 나눠 단계적으로 병합합니다. 예를 들어 사용자 시스템은 다음처럼 나눌 수 있습니다.
- PR1: 데이터베이스 테이블 설계와 기본 model
- PR2: 사용자 등록 API
- PR3: 사용자 로그인 API
- PR4: 사용자 정보 수정 API
PR 설명을 명확히 작성
저희 팀에는 다음과 같은 PR 설명 템플릿이 있습니다.
## 배경
이 변경이 필요한 이유를 간단히 설명
## 변경 내용
- xxx 기능 추가
- xxx bug 수정
- xxx 모듈 리팩터링
## 테스트 현황
- [ ] 단위 테스트 통과
- [ ] 로컬 기능 테스트 통과
- [ ] 테스트 환경에서 검증 완료
## 주의 사항
주의해야 할 설정 변경, 데이터베이스 마이그레이션 등
이렇게 작성하면 리뷰어가 무엇을 왜 변경했는지 한눈에 이해할 수 있습니다.
Commit message 규칙 준수
현재 저희는 Conventional Commits 규칙을 참고해 commit 메시지에 다음과 같은 유형 접두사를 반드시 붙입니다.
feat: 사용자 로그인 기능 추가fix: 비밀번호 검증 bug 수정refactor: 사용자 service 계층 리팩터링docs: API 문서 업데이트
그러면 나중에 changelog를 자동 생성할 수 있고 기록을 되짚을 때도 쉽게 찾을 수 있습니다.
리뷰어와 작성자의 책임
코드 리뷰는 양방향 과정이며 리뷰어와 작성자 모두에게 책임이 있습니다.
리뷰어가 해야 할 일:
- PR에 24시간 안에 응답합니다. PR을 며칠씩 방치하지 않습니다.
- 흠잡기가 아니라 건설적인 의견을 냅니다. ‘이렇게 쓰면 안 됩니다’보다 ‘이유가 …이므로 xxx로 바꾸는 것을 제안합니다’가 낫습니다.
- 코드 스타일은 lint에 맡기고 코드 로직, 가독성, 잠재적 bug에 집중합니다.
작성자가 해야 할 일:
- 침묵하지 말고 댓글에 제때 응답합니다.
- 설계 의도를 설명합니다. 리뷰어가 이해하지 못했다면 코드가 충분히 명확하지 않을 수 있습니다.
- 처음에는 불합리해 보여도 왜 그런 제안을 했는지 생각하며 겸허히 받아들입니다.
처음 리뷰어 역할을 맡았을 때는 ‘여기가 틀렸으니 수정하세요’ 같은 댓글을 자주 썼습니다. 작성자는 제가 무엇을 원하는지 전혀 알 수 없었습니다. 이후에는 ‘데이터가 많을 때 이 반복문에 성능 문제가 생길 수 있습니다. xxx 또는 xxx로 바꾸는 것을 제안하는데 어떻게 생각하시나요?’처럼 작성했고, 소통이 훨씬 잘되었습니다.
2025년의 새로운 흐름: AI 보조 코드 리뷰
2025년에는 AI 보조 코드 리뷰 도구가 많이 등장했습니다. GitHub는 10월에 Code Quality 기능을 출시해 코드의 유지 보수성과 보안 문제를 자동으로 감지할 수 있게 했습니다.
직접 사용해 보니 다음처럼 쉽게 놓칠 수 있는 문제를 실제로 발견했습니다.
- 처리하지 않은 예외
- 잠재적인 메모리 누수
- 지나치게 복잡한 함수(순환 복잡도가 너무 높음)
하지만 현재 AI가 사람의 리뷰를 대체할 수는 없습니다. 기술적인 문제는 찾을 수 있지만 설계 의도와 비즈니스 로직의 타당성은 여전히 사람이 판단해야 합니다.
그래서 지금은 AI 도구가 먼저 훑어 명백한 문제를 찾고, 사람이 설계와 로직을 깊이 리뷰합니다. 이 방법으로 효율이 많이 높아졌습니다.
6장: 흔한 실수와 문제 예방 가이드
마지막으로 저와 팀이 겪은 시행착오를 공유하겠습니다. 여러분은 같은 문제를 피할 수 있기를 바랍니다.
강제 push의 위험
한 번은 제 feature 브랜치가 메인 브랜치보다 많이 뒤처져 rebase했는데, 로컬과 원격의 기록이 달라 push할 수 없었습니다.
당시에는 어떻게 해야 할지 몰라 검색하다가 git push -f 명령을 발견했습니다. ‘강제 push’라는 말이 강력하게 느껴져 그대로 사용했습니다.
그 결과 동료가 방금 push한 코드를 모두 덮어썼습니다. 다행히 백업이 있었지만, 그렇지 않았다면 동료의 하루 작업이 사라질 뻔했습니다.
나중에야 git push -f(또는 git push --force)가 가장 위험한 명령 중 하나라는 것을 알게 되었습니다. 원격에 새 코드가 무엇이 있든 로컬 기록으로 원격 기록을 강제로 덮어씁니다.
사용할 수 있는 경우:
- 개인 feature 브랜치이며 다른 사람이 사용하지 않는다고 확신할 때
절대 사용하면 안 되는 경우:
- main/master 같은 공유 브랜치
- 다른 사람이 사용하는 모든 브랜치
강제 push가 꼭 필요하다면 git push --force-with-lease가 더 안전합니다. 원격 브랜치에 새 commit이 있는지 검사하고, 있다면 push를 거부합니다.
그 밖의 자주 발생하는 실수
강제 push 외에도 다음과 같은 흔한 실수가 있습니다.
1. main 브랜치에서 직접 개발
절대 그렇게 하지 마세요. 글자 하나를 바꾸는 간단한 수정도 브랜치를 만들고 PR로 병합해야 합니다. 그래야 리뷰 기록이 남고 문제가 생겨도 쉽게 되돌릴 수 있습니다.
2. 지나치게 성의 없는 Commit 메시지
‘fix’, ‘수정’, ‘111’, ‘asdf’…… 별난 commit 메시지를 너무 많이 봤습니다. 나중에 코드를 추적할 때는 전혀 도움이 되지 않습니다.
모든 commit에 무엇을 왜 변경했는지 명확히 적는 습관을 들이세요.
3. 병합된 브랜치를 제때 삭제하지 않음
브랜치는 병합 후 즉시 삭제해야 하며 그대로 쌓아 두지 않습니다. GitHub와 GitLab에는 PR 병합 시 브랜치를 자동 삭제하는 옵션이 있으므로 선택하세요.
4. git add .을 무작정 사용
git add .은 원하지 않는 파일을 포함해 모든 변경 사항을 스테이징 영역에 추가합니다. 비밀번호가 포함된 .env 파일을 실수로 커밋한 사람도 봤습니다.
더 나은 방법은 git add -p입니다. 각 변경 사항을 add할지 하나씩 물어보므로 조금 느리지만 더 안전합니다.
팀 협업 규칙 체크리스트
마지막으로 참고할 수 있는 팀 Git 규칙 체크리스트를 정리했습니다.
브랜치 관리
- 통일된 브랜치 명명 규칙 사용(feature/, bugfix/ 등)
- 브랜치 이름에 작업 번호 포함
- feature 브랜치 수명을 3일 이하로 유지
- 병합 후 즉시 브랜치 삭제
- main 브랜치 보호 규칙 설정
코드 커밋
- 유형 접두사를 붙여 규칙에 맞는 Commit 메시지 작성
- 각 commit에는 한 가지 작업만 포함
- 커밋 전 lint와 테스트 실행
- 민감 정보(.env, 키 등)를 커밋하지 않음
코드 리뷰
- PR 크기를 200~400줄로 적절하게 유지
- 배경, 변경 내용, 테스트를 포함한 명확한 PR 설명
- 최소 1명의 approve 후 병합
- CI 검사 통과 후 병합
- PR에 24시간 안에 응답
충돌 처리
- 매일 한 번 메인 브랜치 동기화
- 충돌 해결 후 테스트 실행
- 확실하지 않은 충돌은 즉시 동료와 소통
금지 사항
- main 브랜치에서 직접 개발하지 않기
- 공유 브랜치에
git push -f를 사용하지 않기 - 공유 브랜치에 push한 commit을 rebase하지 않기
- 테스트하지 않은 코드를 커밋하지 않기
결론
새벽 2시에 프로덕션 장애를 처리했던 그날 밤을 떠올리면, 당시 이런 기술을 알고 있었기를 진심으로 바랍니다. 하지만 다행히 시행착오는 모두 경험이 되었습니다.
사실 Git 자체는 어렵지 않습니다. 어려운 것은 팀 안에서 합의에 이르는 일입니다. 아무리 좋은 워크플로와 규칙도 팀원이 지키지 않으면 소용이 없습니다.
그래서 제가 권하는 방법은 단순하게 시작해 단계적으로 개선하는 것입니다. 처음부터 지나치게 복잡한 규칙을 만들지 말고 팀의 실제 상황에 맞춰 조금씩 조정하세요.
예를 들면 다음과 같습니다.
- 첫째 주: 브랜치 명명 규칙 통일
- 둘째 주: 브랜치 보호 규칙 설정
- 셋째 주: PR 리뷰 프로세스 구축
- 넷째 주: commit message 규칙 도입
서두르지 말고 팀이 적응할 시간을 주세요.
가장 중요한 것은 소통입니다. 문제가 생겼을 때 혼자 끙끙대지 말고 동료와 많이 의논하세요. 많은 Git 충돌은 본질적으로 기술 문제가 아니라 소통 문제입니다.
Git 역량을 더 높이고 싶다면 다음 자료를 권합니다.
- Atlassian Git Tutorial - 가장 포괄적인 Git 튜토리얼
- Pro Git 전자책 - Git 공식 제작, 무료 온라인 열람
- GitKraken 또는 SourceTree - 초보자에게 더 친숙한 시각화 Git 클라이언트
다음 글에서는 cherry-pick, stash, submodule 같은 Git 고급 기술을 다룰 수도 있습니다. 관심이 있다면 계속 지켜봐 주세요.
마지막으로 여러분과 팀의 Git 협업이 갈수록 원활해지고, 다시는 한밤중에 일어나 merge 충돌을 처리하지 않기를 바랍니다!
Git 팀 협업 전체 프로세스
워크플로 선택부터 코드 리뷰 프로세스 구축까지 브랜치 관리, 충돌 해결, 모범 사례를 아우르는 전체 단계
Estimated time: PT1H
-
1
Step 1: 팀에 적합한 Git 워크플로 선택
팀 규모에 따라 워크플로를 선택합니다. -
2
Step 2: 브랜치 관리 규칙 수립
브랜치 명명 규칙: 통일된 접두사(feature/신규 기능, bugfix/버그 수정, hotfix/긴급 수정, release/릴리스 브랜치)를 사용하고 작업 번호(예: feature/JIRA-1234-user-login)를 반드시 포함합니다. 밑줄 _ 대신 하이픈 -을 사용하고 한국어와 특수 문자는 사용하지 않습니다. 브랜치 수명 관리: feature 브랜치는 3일을 넘기지 않고 병합 후 즉시 삭제합니다(git branch -d feature/xxx, git push origin —delete feature/xxx). 병합된 브랜치는 git branch —merged main | grep -v “main” | xargs git branch -d로 일괄 삭제합니다. 브랜치 보호: GitHub/GitLab에서 main 브랜치의 직접 push를 금지하고 PR을 거치게 하며, 최소 1명의 approve와 CI 검사 통과를 요구합니다. -
3
Step 3: Rebase와 Merge 사용 사례 이해
Merge 사용 사례: feature 브랜치를 최종적으로 main에 병합할 때, 팀 공유 브랜치를 병합할 때, 전체 병합 기록을 보존하고 싶을 때입니다. Rebase 사용 사례: feature 브랜치가 main보다 뒤처져 최신 코드를 동기화할 때, 아직 push하지 않은 개인 브랜치 기록을 정리할 때, 선형 커밋 기록을 원할 때입니다. 황금률: 이미 공유 브랜치에 push한 commit은 절대 rebase하지 않습니다. commit ID가 바뀌어 다른 사람의 브랜치가 엉키기 때문입니다. 로컬에서는 자유롭게 rebase하되 push 전에는 신중히 판단합니다. 대화형 Rebase: git rebase -i HEAD10으로 최근 commit 10개를 정리하고 pick/squash/reword/edit/drop을 사용합니다. 2025년 모범 사례는 로컬에서 rebase로 기록을 정리하고 메인 브랜치에는 merge로 병합하는 것입니다. -
4
Step 4: Git 충돌 예방과 해결
충돌 예방: 하루에 최소 한 번 메인 브랜치를 동기화하고(git checkout main && git pull origin main && git checkout feature/xxx && git merge main), 작게 커밋해 빠르게 병합하며(feature 브랜치 3일 이하), 협업 전에 소통합니다. 충돌 표시 이해: <<<<<<< HEAD부터 =======까지는 내 코드이고, =======부터 >>>>>>>까지는 병합할 코드입니다. 시각화 도구: VS Code 내장 충돌 해결 도구(Accept Current Change/Accept Incoming Change/Accept Both Changes/Compare Changes), git mergetool과 p4merge의 3열 비교를 사용합니다. 해결 후 검증: 로컬 테스트로 기능을 확인하고 git diff로 코드가 잘못 삭제되지 않았는지 살펴보며 동료에게도 review를 요청합니다. -
5
Step 5: 효율적인 코드 리뷰 프로세스 구축
PR 모범 사례: 200400줄, 최대 500줄로 유지하고 기능이 크면 여러 개의 작은 PR로 나눠 병합합니다. PR 설명 템플릿에는 배경/변경 내용/테스트 현황/주의 사항을 포함합니다. Commit message에는 Conventional Commits 규칙에 따른 유형 접두사(feat/fix/refactor/docs)를 붙입니다. 리뷰어와 작성자의 책임: 리뷰어는 24시간 안에 PR에 응답하고 코드 로직, 가독성, 잠재적 bug에 관한 건설적인 의견을 냅니다. 작성자는 제때 응답하고 설계 의도를 설명하며 제안을 겸허히 받아들입니다. 2025년의 흐름은 AI 보조 코드 리뷰입니다. GitHub Code Quality로 유지 보수성과 보안 문제를 자동 감지하고, AI가 명백한 문제를 찾은 뒤 사람이 설계와 로직을 깊이 리뷰합니다. -
6
Step 6: 흔한 실수를 피하고 팀 규칙 수립
금지 사항: main 브랜치에서 직접 개발하지 않고, 공유 브랜치에 git push -f를 사용하지 않으며(git push —force-with-lease가 더 안전함), 공유 브랜치에 push한 commit을 rebase하지 않고, 테스트하지 않은 코드를 커밋하지 않습니다. 그 밖의 흔한 실수: 성의 없는 commit 메시지(Conventional Commits 규칙 사용), 병합된 브랜치를 제때 삭제하지 않는 것, git add .을 무작정 사용하는 것(git add -p가 더 안전함)입니다. 팀 협업 규칙 체크리스트: 브랜치 관리(명명 규칙 통일, 작업 번호 포함, 3일 이하의 수명, 병합 후 즉시 삭제, 브랜치 보호), 코드 커밋(규칙에 맞는 commit 메시지, commit당 한 가지 작업, 커밋 전 lint와 테스트 실행, 민감 정보 제외), 코드 리뷰(적절한 PR 크기, 명확한 설명, 최소 1명의 approve, CI 검사 통과, 24시간 안에 응답), 충돌 처리(매일 메인 브랜치 동기화, 해결 후 테스트, 불확실하면 즉시 소통)를 지킵니다.
FAQ
소규모 팀은 어떤 Git 워크플로를 선택해야 하나요?
• 단순하고 빠름: 메인 브랜치는 하나뿐이며 모든 기능은 메인 브랜치에서 feature 브랜치를 만들어 개발합니다.
• 빠른 반복: CI/CD와 결합하면 하루에도 여러 번 배포할 수 있습니다.
• 낮은 학습 비용: 팀원이 쉽게 이해하고 지킬 수 있습니다.
Git Flow를 권장하지 않는 이유:
• 복잡한 절차: develop/feature/release/hotfix 등 여러 브랜치를 유지해야 합니다.
• 대기업에 적합: 명확한 릴리스 주기가 있는 대규모 팀에 필요합니다.
• 소규모 팀에는 지나치게 번거로워 매일 브랜치 관리에만 많은 시간이 듭니다.
2025년의 흐름은 혼합 모델입니다. 소규모 팀은 GitHub Flow를 사용하고 중대형 팀은 Git Flow의 일부 기능을 결합합니다. 핵심은 과도하게 설계하지 않고 팀 규모에 맞는 방식을 선택하는 것입니다.
브랜치 관리의 황금률은 무엇이며, 브랜치 혼란은 어떻게 피하나요?
• 통일된 접두사를 사용합니다(feature/ 신규 기능, bugfix/ 버그 수정, hotfix/ 긴급 수정, release/ 릴리스 브랜치).
• 작업 번호를 반드시 포함합니다(예: feature/JIRA-1234-user-login).
• 단어는 밑줄 _ 대신 하이픈 -으로 연결합니다(업계 관례입니다).
• 일부 시스템에서 문자가 깨질 수 있으므로 한국어 브랜치 이름을 사용하지 않습니다.
• 공백과 @ 같은 특수 문자를 사용하지 않습니다.
브랜치 수명 관리:
• feature 브랜치의 수명은 3일을 넘기지 않습니다. 기능이 크다면 여러 개의 작은 feature로 나눠 단계적으로 병합합니다.
• 병합 후 즉시 브랜치를 삭제합니다.
- git branch -d feature/xxx로 로컬 브랜치 삭제
- git push origin --delete feature/xxx로 원격 브랜치 삭제
• 병합된 브랜치 일괄 삭제: git branch --merged main | grep -v "main" | xargs git branch -d
브랜치 보호 정책:
• GitHub/GitLab에서 main 브랜치에 직접 push하지 못하게 하고 PR/MR을 거치도록 설정합니다.
• 병합하려면 최소 1명의 approve가 필요하게 합니다.
• 단위 테스트와 lint를 포함한 CI 검사를 반드시 통과하게 합니다.
• GitHub 저장소의 Settings → Branches → Branch protection rules에서 설정할 수 있습니다.
Rebase와 Merge는 무엇이 다르며 언제 각각 사용해야 하나요?
Merge:
• 두 강줄기가 합쳐지는 것과 같아서 어디에서 만나는지 명확히 보이고, 합류 뒤에는 더 넓은 강이 됩니다.
• Git에서 merge는 merge commit을 만들며 커밋 기록 트리에 뚜렷한 분기가 남습니다.
• 브랜치 생성과 병합 과정을 포함한 전체 기록을 보존합니다.
Rebase:
• 지류의 물을 본류 뒤에 다시 붓는 것과 같아 처음부터 강이 하나였던 것처럼 보입니다.
• Rebase는 기록을 다시 쓰고 각 commit을 대상 브랜치 뒤로 옮기므로 commit ID가 바뀝니다.
• 더 깔끔한 선형 커밋 기록을 만들지만 브랜치의 맥락 정보는 사라집니다.
사용 사례:
• Merge: feature 브랜치를 최종적으로 main에 병합할 때, 팀 공유 브랜치를 병합할 때, 전체 병합 기록을 보존하고 싶을 때
• Rebase: feature 브랜치가 main보다 뒤처져 최신 코드를 동기화할 때, 아직 push하지 않은 개인 브랜치 기록을 정리할 때, 선형 커밋 기록을 원할 때
황금률:
• 이미 공유 브랜치에 push한 commit은 절대 rebase하지 않습니다. rebase는 commit ID를 바꾸므로 다른 사람이 기존 commit을 바탕으로 작업했다면 그 사람의 브랜치가 엉키게 됩니다.
원칙: 로컬에서는 자유롭게 rebase하되 push 전에는 신중히 판단합니다.
2025년 모범 사례는 로컬에서 rebase로 기록을 정리하고 메인 브랜치에는 merge로 병합하는 것입니다.
Git 충돌을 어떻게 예방하고 해결하나요?
1) 하루에 최소 한 번 메인 브랜치를 동기화합니다.
• 작업 시작 전: git checkout main && git pull origin main && git checkout feature/xxx && git merge main 또는 git rebase main
2) 작게 커밋하고 빠르게 병합합니다.
• feature 브랜치의 수명은 3일을 넘기지 않습니다. 브랜치가 오래 존재해 메인 브랜치와 차이가 커질수록 충돌 가능성이 높아집니다.
3) 협업 전에 먼저 소통합니다.
• 두 사람이 같은 파일을 수정해야 한다면 미리 알리는 것이 좋습니다.
• Notion에 ‘개발 중인 기능’ 표를 만들어 누가 어떤 파일을 수정하는지 한눈에 파악합니다.
충돌 표시 이해:
• <<<<<<< HEAD부터 =======까지는 현재 브랜치의 코드입니다.
• =======부터 >>>>>>>까지는 병합하려는 브랜치의 코드입니다.
• 어느 부분을 남길지 결정하거나 두 코드를 결합해야 합니다.
• 모든 충돌을 해결한 뒤 표시를 삭제하고 git add . && git commit을 실행합니다.
시각화 도구 사용:
• VS Code 내장 충돌 해결 도구(Accept Current Change는 내 코드 유지, Accept Incoming Change는 상대 코드 유지, Accept Both Changes는 양쪽 모두 유지, Compare Changes는 비교 화면 열기)
• git mergetool과 p4merge를 이용한 3열 비교(기준 버전, 내 버전, 상대 버전)
해결 후 검증:
• 로컬 테스트를 실행해 기능이 정상인지 확인합니다.
• git diff로 변경 사항을 살펴보고 코드가 잘못 삭제되지 않았는지 확인합니다.
• 가능하면 동료에게도 review를 요청합니다.
효율적인 코드 리뷰 프로세스는 어떻게 구축하며 PR 모범 사례는 무엇인가요?
• 지식 공유: 팀원이 서로 무엇을 하는지 알게 해 정보 고립을 방지합니다.
• 품질 보증: 눈이 한 쌍 더 있으면 자신이 놓친 문제를 발견할 수 있습니다.
• 기술 성장: 좋은 코드를 읽는 것은 가장 훌륭한 학습 방법입니다.
PR 모범 사례:
1) PR 크기를 적절하게 유지합니다.
• 200~400줄이 가장 좋고 최대 500줄을 넘기지 않습니다.
• 너무 큰 PR은 아무도 꼼꼼히 보려 하지 않으므로 기능이 크다면 여러 개의 작은 PR로 나눠 병합합니다.
2) PR 설명을 명확히 작성합니다.
• 배경/변경 내용/테스트 현황/주의 사항이 포함된 템플릿을 사용합니다.
• 리뷰어가 무엇을 왜 변경했는지 한눈에 이해할 수 있습니다.
3) Commit message 규칙을 지킵니다.
• Conventional Commits 규칙에 따라 commit 메시지에 유형 접두사를 붙입니다.
- feat/신규 기능 추가
- fix/버그 수정
- refactor/리팩터링
- docs/문서 업데이트
• 나중에 changelog를 자동 생성하고 기록을 되짚을 때 쉽게 찾을 수 있습니다.
리뷰어와 작성자의 책임:
리뷰어:
• PR에 24시간 안에 응답합니다.
• 흠잡기가 아니라 건설적인 의견을 냅니다. ‘이렇게 쓰면 안 됩니다’보다 ‘이유가 ...이므로 xxx로 바꾸는 것을 제안합니다’가 낫습니다.
• 코드 스타일은 lint에 맡기고 코드 로직, 가독성, 잠재적 bug에 집중합니다.
작성자:
• 침묵하지 말고 댓글에 제때 응답합니다.
• 설계 의도를 설명합니다. 리뷰어가 이해하지 못했다면 코드가 충분히 명확하지 않을 수 있습니다.
• 처음에는 불합리해 보여도 왜 그런 제안을 했는지 생각하며 겸허히 받아들입니다.
Git 팀 협업에서 흔히 저지르는 실수와 예방법은 무엇인가요?
1) 강제 push의 위험:
• git push -f 또는 git push --force는 가장 위험한 명령 중 하나입니다. 원격에 새 코드가 있더라도 로컬 기록으로 원격 기록을 강제로 덮어씁니다.
• main/master 같은 공유 브랜치와 다른 사람이 사용하는 모든 브랜치에서는 절대 사용하면 안 됩니다.
• 강제 push가 꼭 필요하다면 git push --force-with-lease가 더 안전합니다. 원격 브랜치에 새 commit이 있는지 확인하고 있다면 push를 거부합니다.
2) main 브랜치에서 직접 개발:
• 절대 그렇게 하지 않습니다. 글자 하나를 바꾸는 간단한 수정도 브랜치를 만들고 PR로 병합해야 합니다.
• 그래야 리뷰 기록이 남고 문제가 생겨도 쉽게 되돌릴 수 있습니다.
3) 지나치게 성의 없는 Commit 메시지:
• ‘fix’, ‘수정’, ‘111’, ‘asdf’ 같은 commit 메시지는 나중에 코드를 추적할 때 전혀 도움이 되지 않습니다.
• 모든 commit에 무엇을 왜 변경했는지 명확히 적는 습관을 들입니다.
4) 병합된 브랜치를 제때 삭제하지 않음:
• 브랜치는 병합 후 즉시 삭제해야 합니다.
• GitHub와 GitLab에는 PR 병합 시 브랜치를 자동 삭제하는 옵션이 있으므로 선택합니다.
5) git add .을 무작정 사용:
• 원하지 않는 파일을 포함해 모든 변경 사항을 스테이징 영역에 추가합니다.
• 비밀번호가 든 .env 파일을 실수로 커밋하는 사람도 있습니다.
• git add -p를 사용하면 변경 사항을 하나씩 add할지 묻기 때문에 조금 느려도 더 안전합니다.
팀 협업 규칙 체크리스트:
브랜치 관리:
• 명명 규칙 통일, 작업 번호 포함, 3일 이하의 수명, 병합 후 즉시 삭제, 브랜치 보호 설정
코드 커밋:
• 규칙에 맞는 commit 메시지, commit당 한 가지 작업, 커밋 전 lint와 테스트 실행, 민감 정보 제외
코드 리뷰:
• 적절한 PR 크기, 명확한 설명, 최소 1명의 approve, CI 검사 통과, 24시간 안에 응답
충돌 처리:
• 매일 메인 브랜치 동기화, 해결 후 테스트 실행, 확실하지 않으면 즉시 소통
금지 사항:
• main 브랜치에서 직접 개발하지 않기
• 공유 브랜치에 git push -f를 사용하지 않기
• 공유 브랜치에 push한 commit을 rebase하지 않기
• 테스트하지 않은 코드를 커밋하지 않기
3분 읽기 · 게시일: 2025년 11월 24일 · 수정일: 2026년 9월 4일



댓글
GitHub로 로그인하여 댓글을 남기세요