소스트리를 사용한 GIT-FLOW 전략(2) - GIT-FLOW 전략과 실무 프로젝트에서 충돌난 과정 복기...
GIT 브랜치 전략
브랜치 전략이란 여러 개발자가 하나의 저장소를 사용하는 환경에서 저장소를 효과적으로 활용하기 위한 work-flow다.
GIT 브랜치를 생성하고 병합하는 방식, 그 시기에 대해 팀과 합의하여 개발자들간의 혼란을 최대한 줄인다. 그에 맞는 니즈에 부합하는 모델을 선택하는 것이 중요하다.
즉, 브랜치 생성에 규칙을 만들어 협업을 유연하게 하는 방법론이라고 할 수 있다.
브랜치 전략이 없다면 필자가 겪은 상황처럼
- 어떤 브랜치가 최신 브랜치인지,
- 어떤 브랜치가 배포에 적용되는 브랜치인지,
- 어떤 브랜치를 끌어와 개발을 해야하는지,
몰라서 제멋대로 개발을 하다가 나중에 크게 후회할 일이 생길 것이다.
GIT-FLOW 전략
실무에서 쓰이는 브랜치 전략과 약간의 차이는 있겠지만 GIT-FLOW 전략에 대해 알면 많은 도움이 될 것이라 생각했다.
GIT-FLOW는 'feature > develop > release > hotfix > master' 형태로 가지가 이루어져 있다.
- master : 제품을 출시할 때, 실서버에서 구동되는 브랜치. 이용자들이 실제로 사용하는 부분이다.
- hotfix : 배포한 버전에서 버그가 발생했을 때 빠르게 수정하고 배포하기 위해 master 브랜치에서 분리하여 사용하는 브랜치. hotfix 브랜치의 변경사항은 작업 후 develop 브랜치에도 병합하여 문제 부분을 처리해준다.
- release : QA, 테스트 서버에서 구동되는 브랜치. master 브랜치에 올리기 전 출시가 가능한지에 대해 확인하여 배포를 위한 최종적인 버그 수정 등의 개발을 수행한다.
- develop : 다음 버전 출시를 위해 개발 중인 브랜치.
- feature : 새로운 기능 개발을 위해 사용하는 브랜치. 기능을 다 완성할 때 까지 유지하여 완성되면 develop 브랜치로 merge 하고 완성되지 못할 경우 버리는 방향을 취한다.
소스트리를 활용한 GIT-FLOW 전략
앞서 설명했듯 소스트리는 GIT GUI를 통해 작업내역 관리를 보다 직관적이고 효율적으로 운용할 수 있게 해준다.
그래서 소스트리를 이용하여 오류가 났을 당시의 깃 상태를 예제로 재현하고 오류가 났던 이유를 복기한 뒤 효율적으로 풀어나가는 방법에 대해 알아봤다.
현재 실제로 진행중이던 프로젝트를 복사하여 깃허브에 올리고 소스트리에 가져왔다. master 브랜치에 최초 커밋까지 되어있는 상태이다.
'저장소 - 저장소 초기화'를 이용하여 GIT-FLOW 전략에 맞는 브랜치를 맞춰주었다.
다만 개발 버전의 develop 브랜치와 다음 버전 출시를 위한 테스트에 사용하는 브랜치인 release 브랜치를 구분하지 않고 통합하여 qa 라는 이름의 브랜치를 만들었다. qa 브랜치에 배포 경로를 만들고 qa 서버에서 테스트가 가능하게 세팅되었다고 가정했다.
테스트를 커밋을 한 상태이다. 먼저 master 브랜치에서 qa 브랜치를 생성했다. 그리고 test1, test2 push 작업을 하였다. 이는 실무에서 실제로 다음 버전 업데이트를 위해 feature 브랜치들의 작업물들을 병합하는 과정이라고도 볼 수 있다.
그리고 qa 브랜치에서 agency 브랜치를 만들어주었다. 이는 실제로 개발을 위해 만들은 브랜치로 feature 폴더에 들어가야 할 브랜치이다. 다만 당시 상황 재현을 위해 별도의 브랜치로 만들었다. 이 agency 브랜치에서 기능을 개발하고 푸쉬한 것을 'push test3' 이라고 가정하였다.
qa 와 agency 두 브랜치의 HEAD 를 보면 서로 다른 시점에 향해있는 것을 알 수 있다. qa 브랜치는 계속해서 다음 버전 출시를 위한 소스들을 커밋할 것이고 agency 브랜치는 해당 기능 개발을 위한 소스들을 커밋할 것이다.
이 과정에서 소스의 괴리는 점점 커질 것이다. 때문에, agency 브랜치에서 작업중인 '나' 는 주기적으로 qa의 소스를 병합하여 최신화를 시켜주어야 한다.
agency 브랜치에서 qa 브랜치 최신 소스를 병합할 때 맞지 않으면 충돌이 발생한다. 이는 qa에서 작업한 소스와 agency 에서 작업한 소스가 서로 같은 곳을 수정한 경우이다.
다음과 같이 스테이지에서 충돌이 발생한 부분을 확인할 수 있다.
'>>> HEAD' 는 agency 브랜치의 최신화된 소스이고 '>>>> qa' 는 qa 브랜치의 소스이다. 구분선을 통해 두 브랜치의 소스가 구분이 되어있으며 이는 작업내역을 비교하여 수기로 수정하거나 agency 혹은 qa 브랜치의 소스로 덮어씌울 수 있다.
이러한 과정을 통해 소스의 최신화를 시켜주지 않고 냅다 내가 개발할 부분만 신경쓰면 나중에 qa 서버에 올리기 위해 qa 브랜치와 병합할 상황이 왔을 때 (나처럼) 100개가 넘는 충돌을 해결해야 하는 끔찍한 상황을 맞닥뜨릴 수 있을 것이다. 소스 간 괴리가 심해 건들기조차 두려운 소스를 말이다.
어떻게든 브랜치의 충돌을 정리하고 qa 브랜치에 성공적으로 소스를 병합한 상황이라고 가정하자.
master 브랜치에서 qa_gpay 브랜치라는 새로운 브랜치를 생성했다. 이는 일반적인 상황도 아니고 당시 상황을 정확하게 재현한 것도 아니지만 qa 브랜치와 소스 괴리가 있는 또 다른 qa 브랜치라는 것을 나타내기 위해 임의로 만든 브랜치이다.
그리고 qa 서버 배포 경로를 qa 브랜치에서 qa_gpay 브랜치로 변경하였다. 이 경우, qa 브랜치에서 생성한 agency 브랜치의 소스를 테스트하기 위해서 qa_gpay 브랜치로 병합해야 하며 높은 확률로 충돌이 발생할 것이다.
이 충돌을 최소화하기 위해서 agency 브랜치에서 '내'가 작업한 부분만 커밋하는 '체리 픽' 을 사용해야 한다. 일반적인 병합(merge)을 할 경우 소스 간 차이가 나는 모든 부분을 병합하여 agency의 상위 브랜치인 qa 브랜치와의 충돌까지 해결해야 하기 때문이다.
일반적인 GIT-FLOW 전략을 사용할 때도 develop 브랜치에서 feature 브랜치 소스들을 병합할 경우 다양한 변수에서 기인된 충돌을 피하려면 체리 픽을 이용해야 안전하게 소스를 병합할 수 있을 것이다.
agency 브랜치의 작업내역인 'agency test1 push' 와 'agency test push2' 커밋을 체리픽을 사용하여 병합하였다. agency test push2 를 병합하는 과정에서 충돌이 발생하였고 발생한 충돌은 수정 후 커밋하여 '에이전시 충돌 해결' 코멘트를 입력하였다. 위 과정을 통해 agency 브랜치를 수정한 소스만 병합하여 충돌을 최소화하였고 빠르게 qa 서버에서의 테스트가 가능해졌다.
그리고 체리 픽을 할땐 과거 > 현재 작업내역 순서로 진행한다. 반대로 진행하면 충돌 발생을 해결한 소스가 다시 과거의 소스로 병합되며 재충돌이 날 수 있다.
다음은 정산이라는 기능을 개발하기 위해 만든 feature 브랜치이다. 예제에서는 해당 기능을 개발하기 위해 두 사람이 같은 브랜치에 붙어 작업을 하고 있다고 가정했다.
보편적으로 작업자들은 자신이 추가한 소스를 해당 이미지와 같이 커밋하고 푸쉬한다. 그리고 같은 브랜치로 개발중인 다른 사람의 소스를 pull을 통해 받아온다.
하지만 작업을 하다보면 약간의 시간차나 부주의로 인해 같은 브랜치에서 개발한 사람의 소스를 pull 하지 않고 커밋하는 상황이 발생한다. 그리고 서로 같은 부분을 동시에 수정했다면,
당연히 다음과 같은 충돌이 발생한다. 작업자A와 작업자B가 브랜치의 동일한 HEAD에서 같은 소스를 다른 결과값으로 수정했을 때 나는 오류이다.
패치를 하자 커밋한 '정산개발 test3' 작업내역이 나타난 것을 확인 할 수 있었고 소스 충돌이 일어났기 때문에 가지가 갈라져 있다.
충돌이 난 부분을 조율하여 해결한 뒤 커밋을 하자 정산개발 브랜치가 정상적으로 병합된 것이 확인되었다. 소스의 많은 부분이 겹친다면 충돌시점의 HEAD로 작업내역을 회귀하여 두 작업내역을 각각 붙혀도 될 것이고 겹치는 부분이 적다면 해당 시점에서 바로 수정이 가능할 것이다.
이와 같이 다른 브랜치가 아닌 한 브랜치에서 여러명이 작업을 하는 경우에도 충돌이 발생하니 소스 최신화와 단위 별 커밋, 푸쉬가 동반되어야 효율적으로 깃을 이용할 수 있을 것이다.
마치며
개발을 하며 정말 자주 사용하는 깃인데 개념을 잘 이해하지 못하니 누군가의 소스를 병합하고 충돌을 해결하는 과정에서 두려움이 많이 들었다. 하지만 내가 겪었던 상황을 복기하며 어느 부분을 잘못했는지, 어떻게 해결하면 되는지에 대해 생각해보니 다음에는 같은 부류의 상황에서 좀 더 좋은 대처를 할 수 있겠다고 생각했다.