Git

중급 04. Conflict 충돌 해결 실무 가이드

동료의 PR이 먼저 머지되어 내 브랜치에서 충돌이 났을 때, 충돌 마커를 직접 수정하고 안전하게 동기화하는 실무 해결 시나리오

· 3min read · 3 views
중급 04. Conflict 충돌 해결 실무 가이드

핵심 요약

동료 PR 선행 머지로 발생한 충돌 마커(<<<<<<<, =======, >>>>>>>) 해석 및 코드 병합, rebase --continue 진행과 rebase --abort 비상 탈출구 실무 가이드

Git 협업 중 가장 흔하게 겪는 상황은 "내가 작업하는 동안 동료의 작업이 먼저 머지되어 발생하는 충돌(Conflict)"입니다. 이 문서는 프롬프트 상의 브랜치 상태(branch-name) $를 명시하여 어느 브랜치에서 작업이 일어나는지 명확히 추적하며 해결하는 전체 과정을 다룹니다.


1. 실무 발생 시나리오

개발자 A(나)와 동료 B는 같은 develop 브랜치에서 각각 새 브랜치를 따서 작업을 시작했습니다.

  • 나 (개발자 A): feature/login 브랜치에서 로그인 API 작성 (routes/api.php 수정)
  • 동료 B: feature/profile 브랜치에서 회원 정보 API 작성 (routes/api.php 수정)

동료 B의 작업이 먼저 끝나서 develop 브랜치에 PR 및 Merge가 완료되었습니다. 이후 내가 작업한 feature/login을 develop에 PR 올리려고 하자 GitHub 화면에 충돌 경고가 나타납니다:

⚠️ "This branch has conflicts that must be resolved"
(동일한 파일의 같은 라인을 서로 다르게 수정했기 때문에 Git이 자동으로 합치지 못함)

[ 커밋 흐름 시각화 ]
develop:        --- C1 --- C2 (동료 B가 머지한 커밋: 회원정보 API 추가)
                              \
feature/login:                 --- C3 (내가 작업한 커밋: 로그인 API 추가) 💥 CONFLICT!

2. 충돌 해결을 위한 로컬 동기화

Step 1. 원격 최신 변경사항 가져오기

# 로컬 작업 브랜치로 이동 및 확인
(develop) $ git switch feature/login
Switched to branch 'feature/login'

# 최신 develop 브랜치 이력 가져오기
(feature/login) $ git fetch origin develop

Step 2. Rebase 시작 (또는 Merge)

실무에서는 커밋 히스토리를 깔끔한 1자 선형으로 유지하기 위해 Rebase 방식을 주로 권장합니다.

(feature/login) $ git rebase origin/develop

Auto-merging routes/api.php
CONFLICT (content): Merge conflict in routes/api.php
error: could not apply 3a1f9b2... feat: 로그인 API 라우트 추가
hint: Resolve all conflicts manually, mark them as resolved with
hint: "git add/rm <conflicted_files>", then run "git rebase --continue".

명령 프롬프트가 (feature/login|REBASE 1/1) 상태로 바뀌며 리베이스가 일시 중단됩니다.


3. 실제 충돌 파일(routes/api.php) 확인 및 수정

충돌이 발생한 파일 routes/api.php를 열어보면 Git이 아래와 같이 충돌 마커를 삽입해 두었습니다.

발생한 충돌 코드 상태 (Before)

<?php

use Illuminate\Support\Facades\Route;
use App\Http\Controllers\AuthController;
use App\Http\Controllers\ProfileController;

Route::middleware('auth:sanctum')->group(function () {
<<<<<<< HEAD (현재 베이스: origin/develop - 동료 B의 코드)
    Route::get('/user/profile', [ProfileController::class, 'show']);
    Route::put('/user/profile', [ProfileController::class, 'update']);
=======
    Route::post('/auth/login', [AuthController::class, 'login']);
    Route::post('/auth/logout', [AuthController::class, 'logout']);
>>>>>>> 3a1f9b2 (내 브랜치 커밋: feature/login)
});

충돌 마커 해석하는 법

  • <<<<<<< HEAD : 현재 기준이 되는 브랜치(동료 B의 코드가 반영된 origin/develop)의 내용입니다.
  • ======= : 두 변경사항을 가르는 경계선입니다.
  • >>>>>>> 3a1f9b2 : 내가 작업한 브랜치의 해당 커밋 내용입니다.

올바른 수정 (After)

이 시나리오에서는 동료 B의 회원정보 라우트와 내 로그인 라우트가 둘 다 모두 필요한 코드입니다. 따라서 마커 기호들을 모두 지우고 두 코드를 깔끔하게 공존시킵니다.

<?php

use Illuminate\Support\Facades\Route;
use App\Http\Controllers\AuthController;
use App\Http\Controllers\ProfileController;

Route::middleware('auth:sanctum')->group(function () {
    // 동료 B의 코드와 내 코드를 모두 반영하고 마커는 완전 삭제
    Route::post('/auth/login', [AuthController::class, 'login']);
    Route::post('/auth/logout', [AuthController::class, 'logout']);
    Route::get('/user/profile', [ProfileController::class, 'show']);
    Route::put('/user/profile', [ProfileController::class, 'update']);
});

⚠️ 주의: <<<<<<<, =======, >>>>>>> 마커 라인을 한 줄이라도 남겨두면 문법 에러(Syntax Error)가 발생하여 배포 후 서비스 장애로 이어집니다. 반드시 완전히 삭제해야 합니다.


4. 충돌 해결 완료 및 프로세스 이어가기

Step 3. 상태 확인 및 충돌 파일 스테이징

(feature/login|REBASE 1/1) $ git status
interactive rebase in progress; onto origin/develop
Unmerged paths:
  (use "git add <file>..." to mark resolution)
	both modified:   routes/api.php

(feature/login|REBASE 1/1) $ git add routes/api.php

Step 4. Rebase 이어가기

🚨 절대 git commit을 실행하지 마세요!
Rebase 진행 중에는 새 커밋을 만드는 것이 아니라, 충돌을 해결했음을 알리고 다음 커밋을 순차 적용해야 하므로 --continue를 사용합니다.

(feature/login|REBASE 1/1) $ git rebase --continue
Applying: feat: 로그인 API 라우트 추가
Successfully rebased and updated refs/heads/feature/login.

# 리베이스 완료 후 다시 일반 브랜치 프롬프트로 복귀
(feature/login) $ 

5. 원격 브랜치 반영 (안전한 푸시)

Rebase를 수행하면 로컬 커밋들의 해시 ID가 새로 갱신되므로 일반 git push는 거절(Rejected)됩니다. 이때는 반드시 --force-with-lease 옵션을 사용하여 푸시합니다.

(feature/login) $ git push --force-with-lease origin feature/login
To https://github.com/my-org/project.git
 + 3a1f9b2...8e2d4a1 feature/login -> feature/login (forced update)

푸시 후 GitHub/GitLab의 PR 화면을 새로고침하면 충돌 경고가 사라지고 초록색 "Able to merge" 상태로 변경됩니다.


6. 실무 멘붕 방지: 비상 탈출구

충돌을 해결하다가 코드가 너무 꼬여서 원래 작업 상태로 되돌리고 싶다면 언제든지 아래 명령어로 Rebase를 취소할 수 있습니다.

(feature/login|REBASE 1/1) $ git rebase --abort
# Rebase 작업 즉시 중단 및 충돌 이전 상태로 원상복구
(feature/login) $ 

핵심 요약 체크리스트

  1. 동료 PR이 먼저 머지되었다면 내 브랜치(feature/login)에서 git fetch 후 git rebase origin/develop을 실행한다.
  2. 프롬프트가 (feature/login|REBASE)로 바뀌면 충돌 마커(<<<, ===, >>>)를 지우고 코드를 병합한다.
  3. 수정 후 git add <파일>을 하고, commit이 아닌 git rebase --continue를 실행한다.
  4. 작업 도중 꼬이면 당황하지 말고 git rebase --abort로 원상 복구한다.
  5. 원격 반영 시에는 팀원의 작업을 보호하는 git push --force-with-lease를 사용한다.

Related Docs

Comments (0)

No comments yet. Be the first to comment!

Leave a Comment

로그인 후 댓글을 남길 수 있습니다.