68. 병합 충돌 (Merge Conflict) 및 해결 방안

⚠️ 이 문서는 수십 명의 개발자가 Git을 이용해 동시에 코드를 수정하다가, **공교롭게도 "두 명 이상의 개발자가 완전히 똑같은 파일의 똑같은 라인(줄)을 동시에 수정하고 원본에 합치려 할 때" 깃(Git)이 기계적으로 누구의 코드를 살려야 할지 몰라 백기를 들고 뻗어버리는 '병합 충돌(Merge Conflict)' 현상과, 이를 해결하는 우아한 두 가지 철학(Merge와 Rebase)**을 다룹니다.

핵심 인사이트 (3줄 요약)

  1. 본질: Git은 텍스트의 차이(Diff)를 계산해 코드를 합친다. A가 10번 줄을 '사과'로 바꾸고 B가 10번 줄을 '바나나'로 바꿔서 제출하면, 멍청한 기계인 Git은 "누구 말이 맞는지 네가 직접 사람의 눈으로 보고 지워라"라며 양쪽 코드를 화면에 다 띄워놓고 멈춰버리는 안전장치다.
  2. 가치: 이 충돌이 두렵다고 코드 병합을 미루면 지옥(Merge Hell)이 펼쳐진다. 충돌을 자주, 그리고 작게 마주치며 해결하는 것(CI, 지속적 통합)이 현대 애자일 소프트웨어 개발 속도를 유지하는 비결이다.
  3. 기술 체계: 충돌을 풀고 두 가지의 족보를 하나로 합쳐 다이아몬드 모양의 흉터를 남기는 정통파 'Merge(머지)' 방식과, 내가 짠 코드의 뿌리를 슬쩍 최신 원본 코드 끝으로 옮겨 달아 일직선의 깔끔한 역사를 조작해 내는 'Rebase(리베이스)' 방식이 대립한다.

Ⅰ. 충돌(Conflict)은 왜 발생하는가? (타이밍의 엇갈림)

코드는 순서대로 섞여야 하는데, 인간들은 동시에 일한다.

  1. 상황극 (The Disaster):
    • 월요일 아침, 철수와 영희가 main 브랜치(공용 코드)를 각자의 노트북으로 복사(Clone)해 갔다. 당시 10번 줄에는 print("Hello")라고 적혀있었다.
    • 철수는 화요일에 10번 줄을 print("Hello Chulsoo")로 고치고 중앙 서버에 먼저 Push 해버렸다.
    • 영희는 수요일에 10번 줄을 print("Hello Younghee")로 고치고 Push를 시도한다.
  2. Git의 패닉 상태 (Conflict):
    • 중앙 서버의 Git은 영희의 요청을 막아버린다. "잠깐! 네가 예전에 가져간 원본 10번 줄은 이미 철수가 바꿔놨어. 네가 지금 이걸 그냥 덮어쓰면 철수의 며칠 치 노력이 영원히 삭제되니까, 네가 철수 코드를 먼저 다운(Pull)받아서 알아서 합친 다음에 가져와!"
  3. 수동 분쟁 조정 (Manual Resolution):
    • 영희가 철수 코드를 다운받으면 화면에 <<<<<<< HEAD (철수 코드) 와 ======>>>>>>> (영희 코드) 가 시뻘겋게 섞여서 나타난다. 영희는 철수에게 전화를 걸어 상의한 뒤, 불필요한 기호를 지우고 print("Hello Chulsoo & Younghee")로 예쁘게 수정하여 다시 올려야 한다.

📢 섹션 요약 비유: 공용 도화지에 그림을 그리는데, 철수는 사과를 그리고 영희는 같은 자리에 바나나를 그려서 제출했습니다. 출판사(Git)는 "내가 마음대로 사과나 바나나 하나를 지워버리면 둘 다한테 욕을 먹으니, 이 도화지를 반반씩 잘라줄 테니 둘이 만나서 믹서기에 갈든가 하나를 버리든가 합의를 보고 완성된 그림 1장만 가져와!"라고 두 손을 들어버리는 공정한 심판 시스템입니다.


Ⅱ. 정통파 해결책: Git Merge (기록의 보존)

역사는 흉측하더라도 있는 그대로 보존되어야 한다.

  1. Merge 커밋의 생성:
    • 영희가 철수 코드와 자기 코드를 합쳐서(Merge) 올리면, Git은 Merge branch 'main' into younghee라는 자동 안내문과 함께 별도의 **'병합 전용 점(Merge Commit)'**을 족보에 쾅 찍는다.
  2. 장점 (완벽한 추적성):
    • 족보(Commit History)를 보면 철수의 나뭇가지와 영희의 나뭇가지가 갈라졌다가 다시 하나의 다이아몬드 형태로 합쳐지는 궤적이 완벽하게 시각적으로 남는다.
    • 나중에 버그가 터졌을 때 "아, 철수와 영희가 각자 일하다가 이때 코드를 합쳤구나"라고 사건의 진상을 100% 추적할 수 있다.
  3. 단점 (스파게티 족보):
    • 50명의 개발자가 하루에 10번씩 Merge를 하면, Git 족보 선들이 실타래처럼 엉망진창으로 꼬여서(Spaghetti History) 리뷰어가 코드의 흐름을 읽는 것을 포기하게 된다.

📢 섹션 요약 비유: 부부 싸움(충돌)을 하고 화해(Merge)를 할 때마다, "2023년 5월 5일: 철수와 영희가 화해함"이라는 거대한 기념비(Merge Commit)를 집 앞마당에 하나씩 세우는 것입니다. 나중에 역사를 돌아보기엔 완벽하지만, 앞마당이 온통 화해 기념비로 꽉 차서 걷기조차 힘들어지는 단점이 있습니다.


Ⅲ. 역사를 조작하는 예술: Git Rebase (깔끔한 일직선)

기록은 거짓말을 하더라도, 최종 코드는 예뻐야 한다.

  1. Rebase의 원리 (뿌리 꺾어 붙이기):
    • 영희가 철수의 최신 코드를 다운받아 충돌이 났을 때, Merge 대신 Rebase 명령어를 친다.
    • Git은 영희가 짰던 코드의 '뿌리(Base)'를 예전 월요일 아침이 아니라, 방금 철수가 업데이트한 화요일 저녁 최신 지점(New Base)으로 통째로 뽑아서 옮겨 달아버린다.
  2. 장점 (일직선의 마법):
    • 영희가 며칠 동안 삽질했던 나뭇가지가 싹 사라지고, 마치 철수가 작업을 끝낸 직후 영희가 그 위에서 평화롭게 작업을 시작한 것처럼 **족보가 깔끔한 1자형 막대기(Linear History)**로 변조된다.
    • 다이아몬드 모양의 복잡한 흉터(Merge Commit)가 단 하나도 남지 않아 프로젝트 족보가 경이로울 정도로 깨끗해진다.
  3. 치명적 위험성 (협업 파괴의 룰):
    • "이미 남들과 공유한(Push한) 브랜치에서는 절대로 Rebase를 쓰지 마라." (Golden Rule of Rebase)
    • 영희가 뿌리를 꺾어서 족보를 조작해 버렸는데, 만약 민수가 영희의 옛날 뿌리 코드를 복사해서 쓰고 있었다면? 민수의 컴퓨터에 있는 족보와 서버의 조작된 족보가 충돌하면서 헬게이트가 열리고 수습 불가 상태에 빠진다.

📢 섹션 요약 비유: Rebase는 타임머신을 타고 과거로 돌아가서 역사를 조작하는 무서운 기술입니다. 철수와 영희가 각자 따로 일한 사실(분기)을 역사책에서 싹 지워버리고, "철수가 다 한 다음에 영희가 평화롭게 넘겨받아 했다"라고 위인전(일직선 족보)을 다시 쓰는 짓입니다. 혼자 보는 일기장에서는 맘대로 고쳐도 예쁘지만, 모두가 공유하는 공식 도서관 책을 마음대로 칼로 잘라 붙이면 다른 사람들이 혼란에 빠져 회사가 난장판이 됩니다.