“내 코드가 사라졌어요” Git 충돌을 안전하게 푸는 웹개발 습관
팀원이 올린 변경 사항을 받았을 뿐인데 터미널에 CONFLICT가 나타나고, 파일 안에는 낯선 꺾쇠 기호가 잔뜩 생겼나요? 급한 마음에 표시를 모두 지우거나 상대방 코드를 통째로 선택하면 명령은 끝날 수 있지만, 정상 동작하던 기능까지 조용히 사라질 수 있습니다. Git 충돌은 고장이 아니라 같은 위치를 서로 다르게 수정해 자동 선택이 불가능하다는 알림입니다.
충돌 메시지가 뜨면 먼저 작업 상태부터 보존합니다
Git 충돌은 왜 같은 줄에서 발생할까
두 개발자가 서로 다른 파일을 수정했다면 Git은 대체로 변경 사항을 자연스럽게 합칩니다. 문제가 되는 순간은 같은 파일의 같은 줄이나 인접한 코드 블록을 각자 수정했을 때입니다. 예를 들어 한 사람은 로그인 버튼의 문구를 바꾸고, 다른 사람은 같은 버튼에 로딩 상태를 추가했다면 어느 구현을 남겨야 하는지 Git이 판단할 수 없습니다.
충돌은 git pull에서만 발생하지 않습니다. 브랜치를 합치는 git merge, 커밋을 재배치하는 git rebase, 특정 커밋을 가져오는 git cherry-pick에서도 생깁니다. 따라서 해결 전에 어떤 명령을 실행하다 멈췄는지 확인해야 합니다. merge 중단 명령과 rebase 중단 명령은 서로 다르기 때문입니다.
코드를 명령의 집합으로 표현하는 기본 개념은 지식백과의 코딩 용어 설명에서도 확인할 수 있습니다. Git은 그 결과물뿐 아니라 변경 과정까지 기록합니다. 충돌을 만났을 때도 파일만 바라보지 말고 변경 기록과 작업 흐름을 함께 읽는 습관이 중요합니다.
- 현재 상태 확인:
git status를 실행해 충돌 파일과 진행 중인 작업 종류를 봅니다. - 변경 내용 보존: 커밋하지 않은 파일이 있었다면 별도 폴더에 복사하거나 패치로 보관합니다. 충돌 해결 도중 무관한 수정을 섞지 않습니다.
- 대상 브랜치 확인:
git branch --show-current로 현재 브랜치가 맞는지 검사합니다. - 원격 기록 갱신: 필요하면
git fetch로 원격 정보만 안전하게 받아 차이를 확인합니다. - 중단 여부 결정: 상황을 이해하지 못했다면 억지로 진행하지 말고 merge는
git merge --abort, rebase는git rebase --abort로 시작 전 상태로 돌아갑니다.
충돌 직후의 첫 목표는 빨리 합치는 것이 아니라, 되돌릴 수 있는 상태를 확보하는 것입니다. status 확인 없이 파일부터 수정하지 마세요.
표시 세 덩어리를 읽으면 사라진 코드가 보입니다
현재 변경과 들어오는 변경을 구분하는 법
충돌 파일에는 보통 <<<<<<<, =======, >>>>>>> 표시가 삽입됩니다. 위쪽은 현재 체크아웃한 브랜치의 변경이고, 아래쪽은 합치려는 브랜치의 변경입니다. 다만 rebase에서는 화면에 보이는 ours와 theirs의 의미가 평소 merge에서 기대한 방향과 다르게 느껴질 수 있으므로, 버튼 이름만 믿기보다 실제 코드를 읽어야 합니다.
가령 현재 브랜치에는 입력값 검증이 있고 상대 브랜치에는 비동기 요청 처리가 있다면, 둘 중 하나를 버리는 선택이 정답이 아닐 수 있습니다. 검증을 먼저 실행한 다음 요청을 보내도록 두 변경의 의도를 재구성해야 합니다. VS Code의 Accept Current Change와 Accept Incoming Change는 편리하지만, 한 번의 클릭이 비즈니스 로직 전체를 삭제할 수도 있습니다.
어떤 코드를 선택할지 애매하면 커밋 제목과 diff를 거슬러 올라가세요. git log --oneline --decorate --graph --all로 브랜치 흐름을 보고, git diff로 아직 해결되지 않은 차이를 확인할 수 있습니다. 작성자를 탓하기보다 “각 변경이 해결하려던 문제가 무엇인가?”라고 질문하면 훨씬 정확한 병합이 가능합니다.
| 화면 표시 | 뜻 | 확인할 내용 |
|---|---|---|
| Current | 현재 작업 기준의 코드 | 내 브랜치에서 반드시 유지할 기능인지 확인 |
| Incoming | 합치려는 변경의 코드 | 상대 브랜치가 추가한 기능과 버그 수정 확인 |
| Both | 양쪽 코드를 함께 유지 | 중복 선언, 실행 순서, 반환문 충돌 검사 |
| Compare | 양쪽 차이를 나란히 표시 | 이름 변경인지 실제 로직 변경인지 판별 |
- 충돌 표시의 위·아래 코드를 각각 읽고 수행 목적을 한 문장씩 적습니다.
git show 커밋해시로 관련 커밋 전체를 확인해 주변 파일의 변경까지 살핍니다.- 한쪽 선택, 양쪽 유지, 새 코드로 재작성 가운데 가장 안전한 방식을 결정합니다.
- 중복된 import, 변수 선언, 이벤트 등록, 반환문을 제거합니다.
- 모든 충돌 표시가 사라졌는지 검색하되, 단순 삭제가 아니라 완성된 문법인지 다시 읽습니다.
파일 종류에 따라 해결 전략도 달라집니다
자바스크립트나 타입스크립트 소스는 함수의 의도를 비교해 직접 합치는 편이 좋지만, 패키지 잠금 파일은 사정이 다릅니다. package-lock.json을 눈으로 수백 줄 편집하면 의존성 트리가 어긋날 수 있습니다. 팀에서 사용하는 패키지 관리자와 버전을 확인한 뒤 선언 파일을 먼저 해결하고, 정해진 설치 명령으로 잠금 파일을 다시 생성하는 방식이 더 안전합니다.
- 소스 코드: 함수 입력, 반환값, 예외 처리와 호출 위치를 함께 확인합니다.
- 설정 파일: 쉼표와 들여쓰기뿐 아니라 환경별 키의 중복 여부를 검사합니다.
- 잠금 파일: 팀 규칙에 따라 재생성하고 의존성 설치 결과를 검증합니다.
- DB 마이그레이션: 실행 순서가 데이터에 영향을 주므로 파일명만 합치지 말고 담당자와 순서를 조율합니다.
- 생성 파일: 원본을 해결한 뒤 빌드 도구로 다시 만드는 것이 가능한지 먼저 확인합니다.
해결 완료 버튼보다 테스트 순서가 더 중요합니다
add와 commit 전에 실행할 단계별 검증
충돌 표시를 지우고 git add를 실행하면 Git은 해당 파일을 해결된 것으로 간주합니다. 그러나 이것은 문법과 기능이 정상이라는 인증이 아닙니다. Git은 두 버전 사이의 선택이 끝났다는 사실만 기록하므로, 웹개발 프로젝트에서는 정적 검사부터 실제 사용자 흐름까지 별도로 검증해야 합니다.
먼저 git diff --check로 공백 오류와 남은 문제를 살피고, 프로젝트의 포매터·린터·타입 검사를 실행합니다. 이어서 단위 테스트와 통합 테스트를 돌린 후 개발 서버에서 충돌 지점을 직접 조작해 보세요. 로그인 코드가 충돌했다면 성공 사례만 보지 말고 빈 입력, 틀린 비밀번호, 네트워크 지연, 연속 클릭까지 시험해야 숨어 있는 회귀 오류를 찾을 수 있습니다.
프로그래밍의 기본 개념처럼 프로그램은 명령을 논리적인 순서로 구성한 결과입니다. 충돌 해결에서도 코드가 모두 남아 있다는 사실보다 실행 순서와 상태 변화가 올바른지가 중요합니다. 특히 비동기 함수, 상태 관리, 데이터베이스 트랜잭션은 줄 단위 비교만으로 오류를 발견하기 어렵습니다.
- 표시 검사:
git diff --check와 프로젝트 검색으로 충돌 표식이 남았는지 확인합니다. - 빠른 정적 검사: 포매터, ESLint, 타입 검사를 실행해 문법과 타입 연결 문제를 찾습니다.
- 자동 테스트: 충돌한 모듈과 관련된 테스트부터 실행한 뒤 전체 테스트로 범위를 넓힙니다.
- 수동 테스트: 브라우저에서 정상 흐름과 실패 흐름을 각각 재현합니다.
- 변경 검토:
git diff --staged로 커밋될 최종 코드만 다시 확인합니다. - 작업 완료: merge라면 커밋을 만들고, rebase라면
git rebase --continue로 다음 커밋을 진행합니다.
테스트가 오래 걸린다면 최소한 충돌한 함수의 호출 경로와 핵심 사용자 동작은 직접 검증하세요. “빌드가 됐다”와 “기능이 보존됐다”는 같은 말이 아닙니다.
충돌 해결 커밋은 리뷰 가능한 크기로 남깁니다
충돌을 해결하는 김에 변수 이름을 전부 바꾸거나 오래된 함수를 리팩터링하고 싶을 수 있습니다. 하지만 병합과 개선 작업을 한 커밋에 섞으면 리뷰어는 어떤 차이가 원래 기능이고 어떤 차이가 충돌 해결 과정에서 생겼는지 구분하기 어렵습니다. 긴급한 수정이 아니라면 충돌 해결을 먼저 끝내고 리팩터링은 별도 커밋으로 분리하세요.
- 커밋 메시지에 어떤 브랜치를 합쳤는지보다 어떤 충돌 의도를 조정했는지 기록합니다.
- 삭제된 함수나 API 필드가 있다면 이유와 대체 경로를 풀 리퀘스트 설명에 남깁니다.
- 자동 테스트가 없는 영역은 직접 검증한 URL, 입력값, 브라우저 환경을 기록합니다.
- 충돌 당사자에게 리뷰를 요청할 때는 파일 전체가 아니라 판단이 필요했던 코드 블록을 지정합니다.
충돌을 키우는 세 가지 웹개발 습관부터 끊습니다
오래 묵힌 브랜치와 거대한 포맷 변경
첫 번째 실수는 기능 브랜치를 며칠 또는 몇 주 동안 갱신하지 않는 것입니다. 기준 브랜치가 계속 변하는데 작업 브랜치만 과거 상태에 머물면 마지막 병합에서 수십 개 파일이 한꺼번에 충돌합니다. 작업 단위를 작게 나누고 원격 변경을 자주 확인하면 충돌이 발생해도 어느 변경 때문인지 추적하기 쉽습니다.
두 번째 실수는 기능 변경과 프로젝트 전체 포맷 변경을 동시에 커밋하는 것입니다. 세미콜론, 따옴표, 들여쓰기가 모든 줄에서 바뀌면 Git은 실제 로직 한 줄의 차이도 정확히 식별하기 어려워집니다. 포매터 설정을 팀에서 통일하고 대규모 포맷 커밋은 기능 작업과 분리해야 합니다.
세 번째 실수는 충돌 파일이 많다는 이유로 ours 또는 theirs 옵션을 디렉터리 전체에 적용하는 행동입니다. 이 방법은 명령이 빠르게 끝나는 대신 다른 개발자의 보안 수정이나 내 브랜치의 핵심 기능을 통째로 제거할 수 있습니다. 프로그래밍 결과가 여러 모듈의 연결로 만들어진다는 점은 관련 프로그래밍 용어 자료와 함께 살펴볼 수 있으며, 충돌 해결 역시 파일 하나가 아닌 연결 관계를 기준으로 판단해야 합니다.
- 브랜치를 오래 방치하지 않기: 작은 작업 단위로 커밋하고 기준 브랜치의 변화를 정기적으로 반영합니다.
- 포맷과 기능을 섞지 않기: 코드 스타일 일괄 수정은 독립된 커밋과 리뷰로 처리합니다.
- 전체 강제 선택을 피하기: 파일별로 변경 의도를 확인하고 필요하면 해당 코드 작성자에게 질문합니다.
- 생성 파일을 직접 고치지 않기: 원본 설정이나 스키마를 해결한 뒤 공식 명령으로 다시 생성합니다.
- 해결 직후 push하지 않기: staged diff와 테스트 결과를 확인하고 원격에 올립니다.
다음 충돌을 작게 만드는 팀 규칙
반복 충돌이 특정 파일에 몰린다면 개인의 Git 숙련도만 탓할 일이 아닙니다. 하나의 거대한 컴포넌트나 설정 파일을 모두가 동시에 수정하는 구조일 가능성이 큽니다. UI 컴포넌트, API 호출, 상태 관리 책임을 적절히 분리하고 코드 소유 영역을 명확히 하면 충돌 빈도와 해결 난도가 함께 낮아집니다.
또한 pull request를 작게 유지하고, 병합 전에 자동 테스트와 린트를 필수 검사로 연결하는 편이 좋습니다. 충돌이 자주 나는 파일과 원인을 회고 문서에 한 줄씩만 기록해도 팀의 반복 패턴이 드러납니다. 결국 피해야 할 것은 충돌 자체가 아니라 의도를 확인하지 않은 선택, 검증 없는 커밋, 되돌릴 방법 없는 진행입니다.
- 한 pull request에는 리뷰 가능한 하나의 목적만 담습니다.
- 공통 설정 파일을 바꿀 때는 팀 채널에 변경 범위와 적용 시점을 알립니다.
- 자주 충돌하는 대형 파일은 책임별 모듈로 나눌 수 있는지 검토합니다.
- 병합 전 CI에서 린트, 타입 검사, 핵심 테스트가 통과하도록 설정합니다.
- 충돌 해결 후에는 삭제된 코드와 중복된 로직을 중심으로 동료 리뷰를 받습니다.

- 이전글웹개발에서 WebAssembly는 자바스크립트를 대체할까? 26.08.23
- 다음글여름 코딩 환경: 노트북 발열로 느려진 웹개발 살리기 26.08.21
등록된 댓글이 없습니다.
