1. Merge Conflict란?
Git은 두 브랜치를 병합할 때 변경 사항을 자동으로 합친다.
그러나 같은 파일의 같은 라인을 두 브랜치가 서로 다르게 수정했다면, Git은 어느 쪽이 맞는지 판단할 수 없다. 이 상태가 바로 Merge Conflict이다.
참고 — Git이 자동으로 합칠 수 있는 경우(서로 다른 파일 수정, 같은 파일의 다른 라인 수정)는 컨플릿 없이 머지됩니다. 컨플릿은 정말로 충돌이 일어났을 때만 발생한다.
2. 발생 원인
Merge Conflict의 근본 원인은 두 브랜치가 서로의 변경 사항을 모르는 채로 같은 라인을 수정했을 때다. 반대로, 한 브랜치가 다른 브랜치의 커밋 이력을 포함하고 있다면 Git은 충돌 없이 머지할 수 있다.
시나리오 1 — 동일 파일, 동일 라인 수정
가장 흔한 케이스입니다. 두 브랜치가 같은 함수나 변수를 각각 다르게 수정했을 때 발생합니다.
main ──── A ──────────────────── M
\ /
feature-a ── B1 ── B2 (login.py 3번 라인 수정)
\
feature-b ── C1 ── C2 (login.py 3번 라인 수정)
↑ 충돌!
Git이 컨플릿을 판단하는 방식 — 3-way merge
feature-b가 머지될 때 Git은 단순히 두 브랜치만 비교하는 것이 아니라, 분기 시점의 공통 조상(base) 까지 포함해 세 가지를 비교한다.
① base → "Hello" (분기 시점 원본)
② develop → "Hello, Korea" (feature-a가 이미 머지된 상태)
③ feature-b → "Hello, Japan" (base에서 독립적으로 수정)
Git 입장에서는 develop과 feature-b가 모두 같은 라인을 base 기준으로 다르게 수정했기 때문에 어느 쪽이 맞는지 판단할 수 없어 컨플릿을 발생시킨다.
feature-b는 develop을 당겨오지 않은 채 작업했기 때문에 feature-a의 변경을 전혀 모르는 상태 이것이 충돌의 근본 원인이다
develop: A ── feature-a 머지 ──→ "Korea"
↑
여기서 분기
↓
feature-b: 독립적으로 작업 ──────→ "Japan" ← develop 변경을 모름, 항상 develop을 pull해주고 merge 해줘야 함
시나리오 2 — 베이스 브랜치가 많이 앞서간 경우
feature 브랜치를 오래 방치한 뒤 머지를 시도하면, 그 사이 develop에 쌓인 변경 사항과 충돌할 가능성이 높아집니다.
시나리오 3 — 파일 삭제 vs 수정
한쪽 브랜치에서 파일을 삭제하고, 다른 브랜치에서 같은 파일을 수정하면 Git이 처리 방법을 결정할 수 없어 컨플릿이 발생합니다.
-> 결국 시나리오 1,2,3의 근본적인 원인은 develop pull해주고 변경사항을 feature 브랜치에 반영을 해줘야 한다는 점이다.
3. 컨플릿 마커 읽는 법
Merge Conflict가 발생하면 Git은 해당 파일에 컨플릿 마커를 삽입합니다.
def get_greeting():
<<<<<<< HEAD
return "Hello, Korea" # 현재 브랜치 (내 변경)
=======
return "Hello, Japan" # 머지하려는 브랜치 (상대방 변경)
>>>>>>> feature/greeting
마커 의미
| <<<<<<< HEAD | 현재 체크아웃된 브랜치의 변경 내용 시작 |
| ======= | 두 변경 사항의 구분선 |
| >>>>>>> branch-name | 머지하려는 브랜치의 변경 내용 끝 |
이 마커 사이의 내용을 직접 편집해 원하는 최종 코드로 만들어야 합니다.
4. CLI로 해결하기
4-1. 컨플릿 발생 확인
$ git merge feature/greeting
Auto-merging greeting.py
CONFLICT (content): Merge conflict in greeting.py
Automatic merge failed; fix conflicts and then commit the result.
4-2. 충돌 파일 확인
$ git status
Unmerged paths:
(use "git add <file>..." to mark resolution)
both modified: greeting.py
4-3. 파일 직접 편집
에디터로 파일을 열어 마커를 제거하고 최종 코드로 정리합니다.
# 수정 전 (컨플릿 마커 포함)
<<<<<<< HEAD
return "Hello, Korea"
=======
return "Hello, Japan"
>>>>>>> feature/greeting
# 수정 후 (원하는 코드로 정리)
return "Hello, Korea" # 최종 결정
4-4. 해결 완료 후 커밋
# 해결된 파일을 스테이징
$ git add greeting.py
# 머지 커밋 생성
$ git commit -m "resolve: greeting.py merge conflict"
주의 — git add 전에 파일 내에 <<<<<<<, =======, >>>>>>> 마커가 남아있지 않은지 반드시 확인해야함!
5. GitHub UI로 해결하기
CLI가 불편하다면 GitHub에서 제공하는 웹 에디터로도 해결할 수 있습니다.
순서
- PR 페이지에서 Resolve conflicts 버튼 클릭
- 웹 에디터에서 컨플릿 마커 확인
- 원하는 코드만 남기고 마커 삭제
- 우측 상단 Mark as resolved 클릭
- Commit merge 버튼으로 커밋
한계 — GitHub UI는 단순한 텍스트 충돌만 처리할 수 있다. 복잡한 충돌이나 바이너리 파일 충돌은 로컬에서 직접 해결해야 한다.
6. 예방 팁
Merge Conflict를 완전히 피할 수는 없지만, 빈도를 크게 줄일 수 있습니다.
자주 동기화하기
feature 브랜치를 오래 방치할수록 베이스 브랜치와의 격차가 커집니다. 작업 중에도 주기적으로 베이스 브랜치를 당겨오는 습관을 들이세요.
# 작업 중 develop 최신 상태 반영
$ git fetch origin
$ git rebase origin/develop
브랜치 수명을 짧게 유지하기
브랜치가 오래 살수록 충돌 가능성이 높아집니다. 작은 단위로 나눠 PR을 자주 올리는 것이 효과적이다.
파일/모듈 담당 영역 나누기
팀원 간에 담당 파일이나 모듈을 명확히 나누면 같은 파일을 동시에 수정하는 상황 자체를 줄일 수 있다.
PR 크기를 작게 유지하기
한 번에 많은 변경을 담은 PR은 충돌 가능성도 높고 리뷰도 어렵습니다. 기능 단위로 잘게 나눠 PR을 올리는 것을 권장
7. pull vs rebase vs merge — 언제 무엇을 쓰나
브랜치 동기화 방법으로 pull, rebase, merge 세 가지가 자주 언급됩니다. 각각 용도가 다릅니다.
pull — local develop 최신화
로컬 develop 브랜치를 원격과 맞출 때 사용합니다. develop은 직접 커밋하지 않으므로 단순히 당겨오면 충분합니다.
# local develop에서
git pull origin develop
rebase — feature 브랜치에 develop 반영
feature 브랜치에서 develop의 최신 변경을 반영할 때 사용하는데, 커밋 히스토리가 선형으로 유지되어 git log가 깔끔해진다.
# feature 브랜치에서
git fetch origin
git rebase origin/develop
rebase는 "내 커밋들을 develop의 최신 커밋 위로 옮기는" 동작이다. (아, 나는 반대로 알고 있었다 ㅜ)
# rebase 전
develop: A --- B --- C
\
feature: D --- E
# rebase 후
develop: A --- B --- C
\
feature: D' --- E' ← C 위로 이동
merge — 공식 브랜치 합치기
develop → main처럼 공식 브랜치를 합치거나, PR 머지처럼 기능 단위 이력을 보존해야 할 때 사용합니다. 머지 커밋이 생기면서 두 히스토리가 합쳐진 기록이 남습니다.
# main 브랜치에서
git merge develop
# merge 결과
develop: A --- B --- C
\ \
main: D --- E ---M ← 머지 커밋
정리
상황 명령 이유
| local develop 최신화 | git pull origin develop | 단순 동기화 |
| feature에 develop 반영 | git rebase origin/develop | 히스토리 선형 유지 |
| develop → main 반영 | git merge develop | 기능 단위 이력 보존 |
| PR 머지 (GitHub) | merge | 브랜치 합류 기록 남기기 |
개인 작업 중 동기화 → rebase, 공식 브랜치 합치기 → merge 로 기억하면 편합니다.
마무리
Merge Conflict는 협업 과정에서 피할 수 없는 상황이지만, 발생 원리를 이해하고 있다면 당황하지 않고 침착하게 해결할 수 있다. 이 또한, 소통이 매우 중요하리 ....
'Git & GitHub' 카테고리의 다른 글
| Git 협업 플로우 — PR 올리기 전 필수 순서 정리 (1) | 2026.04.09 |
|---|---|
| Git — 커밋 컨벤션 · Branch 전략 (0) | 2026.04.06 |
| Git Merge vs Rebase - 언제 쓰고, 무슨 차이일까? (0) | 2026.04.06 |