Git에서 충돌은 머지하는 과정에서 발생하는 현상이다. 충돌은 동일한 파일 또는 동일한 부분에서 서로 다른 변경 사항이 생길 때 발생한다. 이는 Git System이 어떻게 두 가지 변경 사항을 머지해야 할지 알 수 없을 때 발생한다.
Git은 충돌이 발생한 파일을 특수한 형식으로 표시하여 사용자가 충돌을 해결할 수 있도록 도와준다. 충돌 부분은 <<<<<<<, =======, >>>>>>>와 같은 마커로 표시하고, 이 마커 사이에는 충돌을 발생시킨 변경 사항이 포함되어 있다. 충돌을 해결하기 위해서는 원인이 되는 파일을 열어 직접 수정해야 한다.
Pull Request의 구현 과정을 자세히 보면, 두 개의 서로 다른 브랜치를 병합하는 과정이다. 그렇기 때문에 충돌이 발생할 수도 있는 것이다. GitHub에서 생성한 Pull Request에서 충돌이 발생하면 다음과 같은 화면을 볼 수 있을 것이다.
해결하는 방법은 크게 2가지이다.
Resolve conflicts 버튼을 누르면 4가지 옵션이 주이진다.<<<===>>>를 삭제 후 직접 코드 수정충돌을 해결하고, Mark as resolved 버튼을 클릭하면, 녹색 버튼의 Commet merge이 활성화가 된다. 이 버튼을 누르면 변경 사항을 커밋할 수 있다.
$ git checkout [브랜치] # 브랜치로 이동
$ git merge [브랜치]
이 명령어를 입력하면 충돌이 일어날 것이다. 텍스트 편집기로 해당 파일을 열러 <<<===>>> 로 표시되어 있는 부분을 직접 수정한다. 수정된 파일을 저장한 후, git add와 git commit을 사용하여 해당 파일의 병합이 완료되었음을 Git에 알릴 수 있다. 이렇게 하면 Git 시스템에 머지가 완료되었다는 정보가 기록된다. git push를 수행하면 성공적으로 충돌을 해결할 수 있다.
최신 코드로 작업하는 것은 충돌을 최소화하는데 매우 중요하다. 프로젝트를 진행할 때, 보통 하나의 메인 브랜치를 기반으로 새로운 기능을 개발하기 위한 브랜치를 생성한다.
하지만 개발 기간이 길어질수록 메인 브랜치와 개발 중인 브랜치 간의 차이가 커지며 충돌 가능성도 증가한다. 문제를 해결하기 위한 간단한 방법은 주기적으로 원본 브랜치의 최신 변경 이력을 main 브랜치로 가져오는 것(git merge feat)이다. 동기화를 위한 정기적인 루틴을 만들어두는 것이 중요하다. main 브랜치와 feat 브랜치가 이어지는 곳이 동기화가 진행된 부분이다.
Pull Request의 규모가 작아지면 변화하는 코드의 양도 적어질 뿐만 아니라 그 코드를 작성하는데 들어가는 시간도 줄어든다. 다시 말해, 원본 브랜치와의 차이가 적다는 것을 의미하고 충돌이 발생하더라도 코드의 일부만 문제가 발생했을 가능성이 높으며, Pull Request를 크게 만들 때보다 해결이 쉽다.
다만 작은 규모의 Pull Request를 만드는 것은 개인의 노력으로는 힘들 수도 있다. 하나의 기능을 작게 만들어 PR을 생성하는 것은 국소적인 변화만으로 기능을 구현할 수 있다는 것을 의미하고, 이는 소프트웨어의 구조가 잘 구성되어 있지 않다면 불가능할 수 있다.
코드의 충돌은 대체로 파일 단위로 발생한다. 큰 파일은 그만큼 충돌이 발생할 가능성이 높다. 작은 파일에서 발생한 충돌을 해결하는 것이 큰 파일의 충돌을 해결하는 것보다 상대적으로 간단할 가능성이 있다.
파일을 작게 나눌 경우 의미 없는 충돌을 최소화할 수 있다. 예를 들어 한 파일 안에 서로 상관관계가 없는 두 함수가 공존한다고 가정해 보자. 이러한 파일에는 앞으로도 서로 관계없는 함수들이 추가될 가능성이 높다. 상관관계가 없는 여러 함수들이 하나의 파일에 모여 있을 떄, 두 개의 브랜치에서 파일의 끝부분에 서로 다른 함수를 동시에 추가하는 상황이 발생할 수 있다.
Git 시스템은 함수의 순서를 결정하지 못해 충돌이 발생하게 된다. 이러한 충돌은 로직이 겹치는 것이 아니라 순서 때문에 발생한 것이므로, 실질적인 의미에서의 충돌로 보기 어렵다.
이렇게 파일을 작게 만드는 것만으로 코드의 가독성을 향상할 수 있다. 하나의 목적을 위한 파일 안에서는, 각각의 코드들 역시 같은 목적을 지닐 것이기 때문이다. 그러나 작은 Pull Request를 만드는 것과 마찬가지로, 파일을 작게 만드는 것은 소프트웨어의 아키텍처가 잘 구성되어 있지 않은 경우에는 적용하기 어려울 수 있다.
프로그램 개발은 단순히 기술적인 코딩 작업 이상의 의미를 갖는다. 프로그램의 아키텍처 디자인, 기획, 그리고 미래의 확장성 등을 고려해야 한다. 이러한 과정에서 커뮤니케이션은 매우 중요한 역할을 한다.
프로그램의 방향성을 미리 알고 진행하기 위해서는 많은 커뮤니케이션이 필요하다. 커뮤니케이션을 통해 개발 과정에서 코드를 작성할 때에도 충돌 가능성을 줄일 수 있다. 목적에 따라 인력과 기능을 적절하게 분배하고, 우선순위에 따라 서로 밀접하지 않은 기능을 먼저 개발하게 된다면 충돌은 자연스럽게 줄어들 것이다.
개발자 커뮤니에는 일반적으로 이해하기 쉽고 명확한 PR을 좋은 PR이라고 생각한다. 즉, PR은 목적과 변경 사항, 그리고 그것이 어떻게 프로젝트에 영향을 미치는지를 잘 이해할 수 있어야 한다.
협업에서 가장 중요한 요소는 바로 커뮤니케이션이다. PR에서 효과적인 커뮤니케이션은 무엇을 변경하려고 하는지, 왜 그렇게 하려고 하는지, 그리고 그것이 어떤 영향을 미칠 것인지를 명확히 전달하는 것이다. 이렇게 명확한 PR은 검토하는 프로세스를 원활하게 만들어 준다. 또한, 이는 리뷰어가 PR을 이해하는 데 필요한 시간을 줄이고, 피드백을 더 빠르게 제공하도록 돕는다.
PR에는 필요에 따라 스크린샷을 포함하거나, 복잡한 문제를 해결하는 경우에는 해결 방법을 상세히 설명해야 할 수도 있다. 필요하다면 외부 문서의 link를 달거나 이와 관련해 논의했던 slack thread 등을 링크하는 것도 좋은 방법 중 하나이다. 또한 어떻게 PR을 읽으면 좋을지 리뷰어에게 가이드를 줄 수도 있다.
PR을 작게 만들면 리뷰 하는데 걸리는 시간을 줄일 수 있고, 리뷰어가 PR의 의도에 더 집중할 수 있다. PR을 작게 만드는 몇가지 원칙을 알려 주도록 하겠다.
코드의 스타일은 이해력과 가독성에 직접적인 영향을 미치며, 이는 효율적인 코드 리뷰와 팀의 생산성에 결정적이다. 깔끔한 코드 스타일은 코드의 일관성을 증가시키고, 새로운 개발자가 프로젝트에 빠르게 적응하도록 돕는다.
개발자 커뮤니티에서는 여러 코드의 스타일 가이드를 제공한다. 스타일 가이드들은 변수명, 함수명, 들여 쓰기, 공백, 주석 작성 등의 측면에서 어떻게 코드를 구조화하고 문서화해야 하는지에 대한 기준을 제시한다. 각 프로그래밍 언어나 프레임워크마다 고유한 스타일 가이드가 존재하며, 이를 준수하면 코드의 일관성과 품질을 높일 수 있다.
하지만 스타일 가이드를 단순히 맹목적으로 따라서는 안된다. 깔끔한 코드 스타일을 유지하기 위해서는 일관성 있는 코드 리뷰가 필수적이다. 리뷰 과정에서 코드 스타일 가이드라인이 지켜졌는지 확인하고, 피드백을 주고받는 것이 좋다.
코드를 작성하고 PR을 만들기 전에 반드시 코드가 제대로 작동하는지 테스트해야 한다. 이는 코드의 안정성을 보장하고, PR을 리뷰하는 과정에서 발생할 수 있는 문제를 미리 해결하는데 큰 도움이 된다.
코드를 테스트하는 방법은 여러가지가 있다. 단위 테스트를 작성하여 코드의 개별 부분이 의도한 대로 작동하는지 확인할 수 있다. 통합 테스트를 통해 여러 코드 조각이 서로 작 작동하는지 검증할 수 있다. 또한, 수동 테스트를 통해 사용자 경험이 예상대로 흘러가는지 점검할 수 있다.
테스트가 성공적으로 수행되면, 그 결과를 PR에 명시적으로 표시하는 것이 좋다. 이는 리뷰어에게 신뢰감을 제공하며, 리뷰 과정을 간소화하는데 도움이 된다. 실패한 테스트가 있다면 해당 테스트를 만족할 수 있도록 수정하고 PR을 올리는 것이 좋다.
아래에서 다룰 내용들은 해당 상단바 Settings에서 확인해 볼 수 있다. 만약 Settings가 보이지 않는다면 해당 Repository에 권한이 없는 것이므로, 권한을 요청해서 받으면 접근할 수 있을 것이다.
Default branch
기본 브랜치는 일반적으로 main으로 설정되어 있다. PR을 생성할 때 기본값으로 main에 머지하게 된다. 때에 따라 이런 default branch를 develop으로 설정하고자 할 수 있는데, 이 Default branch를 변경하면 해당 브랜치로 머지를 요청하는 PR이 생성된다.
Pull Request의 머지 종류 제한하기
GitHub에서는 PR의 머지 방식을 제한하는 기능을 제공한다. 머지하는 방법은 팀 내 정책에 따라서 달라질 수 있다. 머지 방식을 통일하고자 한다면 아래 설정을 통해 특정 머지 방식을 강제할 수 있다. 깔끔한 커밋 히스토리를 유지하고자 할 때 유용하게 사용된다.
Pull Request가 머지된 이후, 자동으로 브랜치 삭제하기
PR이 머지된 후 자동으로 해당 브랜치를 삭제하는 설정도 가능하다. 일반적으로 PR이 머지됐을 때 남아있는 브랜치들은 PR을 위해 생성한 일회성 브랜치이다. 이 설정을 선택하게 되면 PR이 머지됐을 때 자동으로 해당 브랜치를 삭제한다. 불필요한 브랜치를 관리하는 수고를 덜 수 있다.
브랜치 보호 기능은 한 개발자의 실수가 다른 개발자에게 영향을 미치는 것을 막아 준다. 일반적으로는 main/develop 등과 같이 여러 개발자들이 공유하는 브랜치에 보호 설정을 한다. Settings 페이지 좌측 내비게이션바의 Branches를 통해 설정할 수 있다. 여기서 Add branch protection rule을 클릭하면 특정 브랜치들을 보호 상태로 설정할 수 있다.
브랜치 보호는 브랜치의 이름을 기반으로 이뤄진다. 정확히 말하면 브랜치 이름의 패턴을 기반으로 보호 설정을 걸 수 있는 것이다. 예를 들어 브랜치 이름이 main인 브랜치를 보호하고 싶다면 Branch name patter에 main이라고 적으면 된다.
브랜치 이름과 완전히 일치하게 적어도 되지만, 패턴 매칭을 통해 한 차원 높게 브랜치들을 보호할 수 있다. 예를 들어 feature/*와 같은 패턴을 사용한다면 feature/abc와 같이 feature/가 앞에오는 모든 브랜치들을 이 룰의 적용을 받게 할 수 있다.
Branch Protection에는 다양한 설정들이 있다. 그 중에서도 가장 중요하고 자주 사용하는 설정들을 알려주도록 하겠다. Branches 설정에서 브랜치 보호 규칙을 생성한 후, Edit 버튼을 통해 나오는 페이지에서 선택이 가능하다.
main에 푸시하는 실수를 막을 수 있다. 이 설정을 하게 되면, 마음대로 PR을 머지할 수 없게 된다.Require a Pull request before merging 설정을 선택하면 다음 이미지처럼 설정 항목들이 나오게 될 것이다.
Require approvals
이 설정은 Pull Request가 병합되기 전에 특정 수의 승인이 필요함을 명시할 수 있다. 예를 들어 이 설정에 1이라고 설정을 하게 되면, 1명이 승인하기 전까지 PR을 머지할 수 없다.
Require review from Code Owners
특정 부분의 코드에 대한 책임을 가진 사람(Code Owner)이 리뷰를 해야만 머지가 가능하도록 하는 설정이다.
Require linear history
이 설정을 활성화하면, Pull Request를 통해 머지한 커밋이 선형적인 이력을 유지하도록 할 수 있다. 특정 브랜치에서 rebase나 squash 방식만 사용하도록 강제하는 설정이다. 이 설정을 활성화하고 머지하면 아래 이미지처럼 표시가 된다.
Do not allow bypassing the above settings
이렇게 설정해도 경고를 해줄 뿐이지, 반드시 따라야 하는 것은 아니다. 때문에 이 설정을 선택해야 어느 누구도 어길 수 없는 규칙으로 설정할 수 있다. 이 설정과 위에서 Reuqire linear history를 함께 선택하면, 아래 이미지처럼 머지 커밋을 만드는 버튼은 완전히 사용할 수 없게 된다.