[GitHub 100일 챌린지] Day 48 - Merge Conflict 해결
[GitHub 100일 챌린지] Day 48 - Merge Conflict 해결
100일 챌린지 Day 48 - 병합 충돌(Merge Conflict)을 이해하고 해결하는 방법을 배웁니다.
배울 내용
- Merge Conflict란
- Merge Conflict 해결 방법
- Conflict 예방 전략
Topic1. Merge Conflict란
두 브랜치가 같은 파일의 같은 부분을 수정했을 때 발생합니다.
Merge Conflict의 원인
충돌 발생 시나리오:
gitGraph
commit id: "C1: config.js 생성"
commit id: "C2"
branch feature-a
checkout feature-a
commit id: "C3: API_URL 수정"
checkout main
branch feature-b
checkout feature-b
commit id: "C4: API_URL 다르게 수정"
checkout main
merge feature-a
merge feature-b tag: "❌ CONFLICT!"
왜 충돌이 일어나나요?:
1
2
3
4
5
6
7
8
9
10
11
12
초기 파일 (C1):
const API_URL = "http://localhost:3000"
feature-a (C3):
const API_URL = "http://api.production.com"
feature-b (C4):
const API_URL = "http://api.staging.com"
Git의 고민:
"어떤 버전을 사용해야 할까?"
→ 사용자가 직접 선택해야 함!
Conflict 발생 조건
3가지 필수 조건:
1
2
3
4
5
6
7
8
9
10
11
12
1. 같은 파일
2. 같은 위치 (같은 줄)
3. 다른 내용
예시:
✅ 충돌 O:
- main: line 10 → "version 1"
- feature: line 10 → "version 2"
❌ 충돌 X:
- main: line 10 수정
- feature: line 50 수정 (다른 위치)
해보기: Conflict 발생시키기
시나리오: 의도적으로 충돌 만들기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# 프로젝트 초기화
mkdir conflict-demo
cd conflict-demo
git init
# 초기 파일 생성
cat > config.js << 'EOF'
const config = {
apiUrl: "http://localhost:3000",
timeout: 5000,
debug: false
};
EOF
git add config.js
git commit -m "Initial config"
# === 브랜치 A: production 설정 ===
git checkout -b feature/production-config
cat > config.js << 'EOF'
const config = {
apiUrl: "https://api.production.com", // 변경!
timeout: 5000,
debug: false
};
EOF
git add config.js
git commit -m "feat: Add production API URL"
# === 브랜치 B: staging 설정 ===
git checkout main
git checkout -b feature/staging-config
cat > config.js << 'EOF'
const config = {
apiUrl: "https://api.staging.com", // 다르게 변경!
timeout: 5000,
debug: false
};
EOF
git add config.js
git commit -m "feat: Add staging API URL"
# === 충돌 발생시키기 ===
git checkout main
git merge feature/production-config
# → 성공 (Fast-Forward)
git merge feature/staging-config
# 출력:
Auto-merging config.js
CONFLICT (content): Merge conflict in config.js
Automatic merge failed; fix conflicts and then commit the result.
# ❌ 충돌 발생!
결과
충돌 발생 메시지 분석:
1
2
3
4
5
6
7
8
9
10
11
Auto-merging config.js
→ Git이 자동 병합 시도
CONFLICT (content): Merge conflict in config.js
→ config.js에서 내용 충돌 발생
Automatic merge failed
→ 자동 해결 실패
fix conflicts and then commit
→ 수동으로 해결 후 커밋 필요
충돌 상태 확인:
1
2
3
4
5
6
7
8
9
10
11
12
git status
# 출력:
On branch main
You have unmerged paths.
(fix conflicts and run "git commit")
Unmerged paths:
(use "git add <file>..." to mark resolution)
both modified: config.js
# "both modified" → 양쪽 모두 수정했음
Topic2. Merge Conflict 해결 방법
충돌을 수동으로 해결하는 과정입니다.
Conflict Marker 이해하기
충돌 발생한 파일 확인:
1
2
3
4
5
6
7
8
9
10
11
12
cat config.js
# 출력:
const config = {
<<<<<<< HEAD
apiUrl: "https://api.production.com",
=======
apiUrl: "https://api.staging.com",
>>>>>>> feature/staging-config
timeout: 5000,
debug: false
};
Conflict Marker 의미:
1
2
3
4
5
<<<<<<< HEAD
현재 브랜치 (main)의 내용
=======
병합하려는 브랜치 (feature/staging-config)의 내용
>>>>>>> feature/staging-config
각 마커의 역할:
1
2
3
4
5
6
7
8
9
10
11
12
<<<<<<< HEAD
→ 충돌 시작
→ HEAD (현재 브랜치) 내용 시작
=======
→ 구분선
→ 위: 현재 브랜치
→ 아래: 병합 브랜치
>>>>>>> feature/staging-config
→ 충돌 끝
→ 병합 브랜치 이름
충돌 해결 3단계
1단계: 충돌 파일 열기:
1
2
3
4
# 에디터로 충돌 파일 열기
code config.js
# 또는
vim config.js
2단계: 원하는 버전 선택:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 옵션 1: HEAD 버전 선택 (production)
const config = {
apiUrl: "https://api.production.com",
timeout: 5000,
debug: false
};
// 옵션 2: 병합 브랜치 버전 선택 (staging)
const config = {
apiUrl: "https://api.staging.com",
timeout: 5000,
debug: false
};
// 옵션 3: 둘 다 사용 (환경 변수로 분기)
const config = {
apiUrl: process.env.NODE_ENV === 'production'
? "https://api.production.com"
: "https://api.staging.com",
timeout: 5000,
debug: false
};
// ⚠️ Conflict Marker 제거 필수!
// <<<<<<< HEAD
// =======
// >>>>>>> 이런 마커들 모두 삭제
3단계: 해결 완료 표시:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 충돌 해결한 파일 stage
git add config.js
# 상태 확인
git status
# 출력:
On branch main
All conflicts fixed but you are still merging.
(use "git commit" to conclude merge)
Changes to be committed:
modified: config.js
# 병합 커밋 생성
git commit
# 기본 메시지 사용 또는:
git commit -m "Merge: Resolve conflict in config.js
- Choose environment-based API URL selection
- Support both production and staging environments"
해보기: 실전 충돌 해결
시나리오: 팀 협업 중 충돌 해결
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# === 초기 설정 ===
mkdir team-conflict
cd team-conflict
git init
# 공통 함수 파일
cat > utils.js << 'EOF'
function formatDate(date) {
return date.toString();
}
function formatCurrency(amount) {
return `$${amount}`;
}
EOF
git add utils.js
git commit -m "Add utility functions"
# === 개발자 A: 날짜 포맷 개선 ===
git checkout -b feature/date-format
cat > utils.js << 'EOF'
function formatDate(date) {
// 개선된 날짜 포맷
const options = { year: 'numeric', month: 'long', day: 'numeric' };
return date.toLocaleDateString('ko-KR', options);
}
function formatCurrency(amount) {
return `$${amount}`;
}
EOF
git add utils.js
git commit -m "feat: Improve date formatting"
# === 개발자 B: 날짜 포맷 다르게 개선 ===
git checkout main
git checkout -b feature/date-iso
cat > utils.js << 'EOF'
function formatDate(date) {
// ISO 형식으로 변경
return date.toISOString().split('T')[0];
}
function formatCurrency(amount) {
return `$${amount}`;
}
EOF
git add utils.js
git commit -m "feat: Use ISO date format"
# === 충돌 발생 ===
git checkout main
git merge feature/date-format
# → 성공
git merge feature/date-iso
# → 충돌!
# === 충돌 확인 ===
cat utils.js
# 출력:
function formatDate(date) {
<<<<<<< HEAD
// 개선된 날짜 포맷
const options = { year: 'numeric', month: 'long', day: 'numeric' };
return date.toLocaleDateString('ko-KR', options);
=======
// ISO 형식으로 변경
return date.toISOString().split('T')[0];
>>>>>>> feature/date-iso
}
function formatCurrency(amount) {
return `$${amount}`;
}
# === 해결: 두 가지 모두 지원 ===
cat > utils.js << 'EOF'
function formatDate(date, format = 'localized') {
if (format === 'iso') {
// ISO 형식
return date.toISOString().split('T')[0];
}
// 기본: 로컬라이즈된 형식
const options = { year: 'numeric', month: 'long', day: 'numeric' };
return date.toLocaleDateString('ko-KR', options);
}
function formatCurrency(amount) {
return `$${amount}`;
}
EOF
# === 해결 완료 ===
git add utils.js
git commit -m "Merge: Support both date formats
- Add format parameter to formatDate function
- Default to localized format
- Support ISO format via parameter
- Resolves conflict between feature/date-format and feature/date-iso"
# 히스토리 확인
git log --oneline --graph
# 출력:
* M2 (HEAD -> main) Merge: Support both date formats
|\
| * I1 (feature/date-iso) feat: Use ISO date format
* | M1 Merge branch 'feature/date-format'
|\ \
| |/
| * D1 (feature/date-format) feat: Improve date formatting
|/
* U1 Add utility functions
충돌 해결 도구
명령줄 도구:
1
2
3
4
5
6
7
8
# Git 내장 mergetool
git mergetool
# 선택한 도구로 충돌 파일 열림
# (vimdiff, meld, kdiff3 등)
# 충돌 해결 후 임시 파일 정리
git clean -f
비주얼 도구:
1
2
3
4
5
6
7
8
9
10
11
VS Code:
- 충돌 파일 자동 감지
- "Accept Current Change" 버튼
- "Accept Incoming Change" 버튼
- "Accept Both Changes" 버튼
- 직접 편집 가능
GitKraken, SourceTree:
- 그래픽 인터페이스
- 양쪽 변경사항 비교
- 클릭으로 선택
결과
충돌 해결 체크리스트:
1
2
3
4
5
6
✅ 충돌 파일 모두 확인
✅ Conflict Marker 모두 제거
✅ 원하는 최종 결과 확인
✅ 코드가 정상 작동하는지 테스트
✅ git add로 해결 표시
✅ git commit으로 병합 완료
Topic3. Conflict 예방 전략
충돌을 최소화하는 개발 전략입니다.
작업 분리 전략
파일 단위 분리:
1
2
3
4
5
6
7
8
9
좋은 예:
개발자 A → auth.js 작업
개발자 B → profile.js 작업
→ 충돌 가능성 ↓
나쁜 예:
개발자 A → utils.js 전체 수정
개발자 B → utils.js 전체 수정
→ 충돌 가능성 ↑
기능 단위 분리:
1
2
3
4
5
6
7
8
좋은 예:
feature/user-login → 로그인 기능만
feature/user-signup → 회원가입 기능만
→ 명확한 경계
나쁜 예:
feature/user-system → 모든 사용자 기능
→ 경계 모호, 충돌 많음
자주 동기화하기
정기적인 Pull:
1
2
3
4
5
6
7
8
9
10
# 매일 아침 main 최신 상태로
git checkout main
git pull origin main
# feature 브랜치에 최신 변경사항 반영
git checkout feature/my-work
git merge main
# 또는 rebase (히스토리 깔끔)
git rebase main
작은 단위로 자주 병합:
1
2
3
4
5
6
7
8
9
나쁜 예:
2주 동안 feature 브랜치에서 작업
→ main과 너무 멀어짐
→ 병합 시 충돌 다수
좋은 예:
2-3일마다 작은 기능 단위로 병합
→ main과 가까운 상태 유지
→ 충돌 최소화
소통과 협업
작업 공유:
1
2
3
4
5
팀 채널에 공유:
"오늘 utils.js의 formatDate 함수 수정 중입니다!"
→ 다른 개발자가 같은 부분 작업 피함
→ 충돌 사전 방지
코드 리뷰 활용:
1
2
3
4
5
6
Pull Request 리뷰 시:
1. 변경 범위 확인
2. 다른 PR과 충돌 여부 체크
3. 순서 조정 가능
→ 병합 전 충돌 예측
해보기: 충돌 예방 워크플로우
시나리오: 팀 프로젝트 충돌 예방
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# === 월요일 오전: 작업 시작 ===
# 1. main 최신화
git checkout main
git pull origin main
# 2. 새 feature 브랜치 생성
git checkout -b feature/payment-gateway
# 3. 팀에 공유
# Slack: "payment.js 작업 시작합니다!"
# === 월요일 저녁: 중간 커밋 ===
git add payment.js
git commit -m "WIP: Add payment gateway skeleton"
git push origin feature/payment-gateway
# === 화요일 오전: main 동기화 ===
git checkout main
git pull origin main
# main에 새 변경사항 반영
git checkout feature/payment-gateway
git merge main
# 충돌 있으면 여기서 해결 (작은 충돌)
# 충돌 없으면 계속 작업
# === 화요일 저녁: 기능 완성 ===
git add .
git commit -m "feat: Complete payment gateway integration"
git push origin feature/payment-gateway
# === 수요일: Pull Request & 병합 ===
gh pr create --title "Add payment gateway" --body "..."
# 리뷰 후 병합
gh pr merge --squash
# 다른 브랜치로 전환 전 정리
git checkout main
git pull origin main
git branch -d feature/payment-gateway
충돌 최소화 규칙
팀 규칙 예시:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
1. 브랜치 수명 규칙
- Feature 브랜치: 최대 3일
- 3일 넘으면 중간 병합 고려
2. 파일 작업 공지
- 공통 파일 수정 시 팀에 공지
- 동시 작업 조율
3. 정기 동기화
- 매일 오전 main pull
- 매일 오후 main merge
4. 작은 PR 원칙
- 한 PR에 너무 많은 변경 X
- 100~300줄 정도가 적당
5. 충돌 해결 담당
- 나중에 병합하는 사람이 해결
- 먼저 병합한 사람에게 질문 가능
결과
충돌 예방 효과:
1
2
3
4
5
6
7
8
9
Before (충돌 예방 전):
- 주 평균 10건 충돌
- 충돌 해결에 하루 2시간 소요
- 스트레스 높음
After (충돌 예방 후):
- 주 평균 2건 충돌
- 충돌 해결에 하루 30분 소요
- 원활한 협업
핵심 원칙:
1
2
3
4
5
✅ 작업 분리: 파일/기능 단위
✅ 자주 동기화: 매일 pull & merge
✅ 빠른 병합: 2-3일 내 PR
✅ 적극 소통: 작업 공유
✅ 작은 커밋: 리뷰 가능한 크기
정리
오늘 배운 내용:
1. Merge Conflict:
- 같은 파일, 같은 위치, 다른 내용
- Git이 자동 해결 불가
- 수동 해결 필요
2. 충돌 해결 방법:
1
2
3
4
5
6
7
8
9
10
11
# 1. 충돌 확인
git status
# 2. 파일 편집 (Conflict Marker 제거)
vim file.js
# 3. 해결 표시
git add file.js
# 4. 병합 완료
git commit
3. Conflict Marker:
1
2
3
4
5
<<<<<<< HEAD
현재 브랜치 내용
=======
병합 브랜치 내용
>>>>>>> branch-name
4. 충돌 예방:
- 작업 분리 (파일/기능)
- 자주 동기화 (매일 pull)
- 빠른 병합 (2-3일 내)
- 팀 소통 (작업 공유)
다음 단계: Day 49에서 Rebase를 배웁니다.
완료 체크:
- Merge Conflict의 원인을 이해했다
- 충돌을 해결할 수 있다
- Conflict Marker를 읽을 수 있다
- 충돌 예방 전략을 알고 있다
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.
