최근 학회 프로젝트에서 백엔드 2명이서 협업하면서 git 전략에 대해 깊이 고민할 일이 생겼다. 이 글은 그 과정을 기록한 것이다. merge, rebase, squash에 대한 개념 정리와 함꼐, 내가 어떤 기준으로 전략을 선택했는지도 담겨 있다.
1-1. 마주한 문제상황을 명확히 정의하시오.
이번 프로젝트에서 두 명이서 백엔드 개발을 하게 되었다. 팀원 분이 git을 이용해 협업하시는 것은 이번이 처음이다.
이 2가지를 충족하고 싶고, 이를 위해 git 사용에 대한 전반적인 고민이 필요하다.
1-2. 이 문제를 해결하는 데 있어 내가 현재까지 알고 있는 것을 정리하시오.
git으로 전에 협업한 적이 있고 기본적인 사용법을 알고 있다.
rebase나 squash 같은 거 잘 쓰면 git history 깔끔하게 할 수 있다고 들었는데 정확히 모름
1-3. 문제를 해결하기 위해 새로 배워야 할 지식을 정하고, 그 이유를 서술하시오.
git merge의 3가지 전략 개념 (merge vs. squash merge vs. rebase)
git flow vs. github flow 뭐가 지금 우리에게 나은 선택인가?
얘네들을 알고 왜 언제 쓰는 건지 이해해야 지금 나의 고민을 해결할 실마리를 잡을 수 있다.
1-4. 학습 및 탐구 실행 계획을 구체적으로 작성하시오.
새로 배우기로 한 지식을 유튜브, 블로그, ChatGPT와의 문답 등을 통해 배운다.
연습용 git repo 파서 git merge 실습한다.
다른 사람들의 git 사용 전략에 대한 의견을 유튜브 댓글을 통해 탐색한다.
2-1. 탐구 실행 계획을 통해 새롭게 알게 된 내용을 정리하시오.
내가 만든 feature branch를 main 브랜치에 PR merge하고 싶다고 상상하자.
이때 3가지 선택지가 있다.
내가 알고 있었던 일반 merge는 맨 오른쪽 그림처럼 내 feature branch를 그대로 놔두고 main 브랜치에 내 feature branch 내용을 합친다. 제일 직관적이고, 브랜치 흐름을 파악할 수 있다는 장점이 있으나,
main 브랜치에서 갈라졌다 나오는 수많은 브랜치들의 흔적이 얽히고설켜 git history 가독성 및 아름다움이 떨어진다.
내 feature branch의 여러 커밋에 걸친 변경사항들을 하나의 커밋으로 뭉뚱그려서(squash해서) main 브랜치에 갖다 붙인다. 말하자면, 갈라져 나온 나뭇가지를 똑 떼서, 공 모양으로 찌끄러뜨려서 원래 큰 줄기 끝에 갖다 붙이는 것이다.
일단 이름의 의미를 곱씹을 필요가 있다. rebase라는 건 “base를 새로 설정한다는 것”. base란 뭐냐? 이 feature branch가 갈라져 나온 “근본 지점” = base. 우리가 이 rebase를 사용하고 싶을 땐 이런 때다. “내가 지금까지 작업한 feature branch의 내역들이, 마치 원래 main 브랜치에서 쭉 작업하고 있었던 것처럼 보이게 옮겨 놓고 싶다.” 말하자면 원래 줄기에서 갈라져 나온 나뭇가지를 똑 떼서 다시 원래 줄기 끝에 갖다 붙여서 깔끔하게 만들고 싶다는 것. 그게 rebase다.
rebase란 개념을 처음 배우면 괜히 헷갈리기만 하고 이런 걸 언제 사용하는지 감이 안 잡힌다. 그래서 전에 한 번 배운 적이 있지만 헷갈리고 까먹고 흐지부지되었던 기억이 있다.
이번에 학습하면서 rebase를 사용할 수 있는 2가지 케이스를 알게 되었다.
사실 이중에 PR merge할 때보다 feature branch를 최신화할 때 rebase를 사용하는 게 더 좋겠단 생각이 들었다. 이 경우에는 말하자면 그런 상황이다. feature branch에서 기능 개발 열심히 하고 있는데, 이 와중에 main 브랜치에는 다른 사람들이 만든 새 기능들이 속속 업데이트되면서, 내 feature branch의 근본은 main 브랜치의 끄트머리가 아니라 중간 지점이 되어버렸다. 그러니까 내가 작업하고 있는 feature branch는 현재 최신 main 브랜치의 새로 추가된 기능들을 반영하고 있지 않다.
이런 경우에 나는 중간중간에 main 브랜치를 내 feature 브랜치에 일반적인 merge를 하는 방식을 쓴 적이 있는데 이게 또 git history를 더럽게 만들어서 보기 영 좋지 않았다. 말하자면 큰 줄기랑 갈라진 나뭇가지가 서로 엉겨붙어 있는 것 같이.
이럴 때 rebase를 하면, 일종의 속임수를 쓸 수 있다. 마치 방금, main 브랜치의 끄트머리에서 갓 갈라져 나온 파릇파릇한 feature branch인 척을 할 수 있다. 내 작업 중인 feature branch를 똑 뗴어서, main 브랜치 끄트머리에 갖다 붙이는 것이다. 하지만 이 경우는 main 브랜치를 갱신하기 위한 게 아니고 내 feature branch에 main 브랜치 최신사항을 반영해 conflict를 미리 해결하면서도, 예쁘게 해결하기 위한 것이라 할 수 있다.
이 영상 댓글을 보다가 맘에 쏙 드는 방법을 발견했다.
기능 개발 중엔 rebase로 main 브랜치 최신 사항 반영하고, 개발 완료 시에는 squash merge하면,
중요한 브랜치를 깔끔하고 의미 있게 관리할 수 있다.
squash merge는 내가 기능 개발하면서 만든 자잘한 commit들을 하나로 합치기 때문에, commit message에 핵심을 요약해 정리할 수 있다. 만약 PR merge할 때 rebase를 쓴다면, 자잘한 commit들이 중요한 develop이나 main 브랜치에 그대로 흘러들어오기 때문에, 나중에 commit history를 봤을 때 큰 개발 흐름을 보기가 어렵다. (오타 수정, 버그 수정, 사소한 변경 등으로 범벅되어 있는 히스토리..)
이 방법이 맘에 들었으니 이번에 도입을 해보고 싶다.
git flow가 보기엔 더 멋져 보이고 고수 냄새가 나는 건 사실이다.
하지만 화려하고 큰 게 무조건 더 좋은 것은 아니다. 우리 백엔드 팀은 2명이라는 소규모이고, 팀원 분이 git 사용이 처음이고, 쉽고 효과적인 협업이 필요하다. 그렇기 때문에 github flow라는 단순하지만 효과적인 방법을 사용하기로 결정했다.
2-2. 문제 해결 과정에서 추가로 얻게 된 관련 지식을 정리하시오.
IntelliJ에서 사용할 수 있는 유용한 git 관련 플러그인 중 “Git Dev Tools”라는 걸 알게 되었다. 이 플러그인을 설치하면, 코드의 각 줄마다 누가 언제 어떤 커밋에서 변경한 내용인지를 바로바로 표시해준다. 내 탓 네 탓을 쉽고 간편하게 할 수 있다.
2-3. 문제 해결 과정에서 가장 중요했던 정보를 출처와 함께 정리하시오.
merge 3가지 전략 학습한 영상: https://www.youtube.com/watch?v=0chZFIZLR_0
이 영상에 있던 댓글:
git flow vs. github flow: https://velog.io/@gmlstjq123/Git-Flow-VS-Github-Flow
3-1. 한 주간 WIL을 통해 성장한 점, 느낀 점을 자유롭게 적어주세요.
한동안 안 해서 까먹고 있던 git을 이번에 다시 익숙하게 해서 몸이 좀 풀렸다.
거기에 그동안 미뤘던 rebase, squash 개념 복습을 다시 했고, 얘네들을 어떻게 쓸지, git 전략 어떻게 할지에 대한 고민도 이번에 해결할 수 있었다.
3-2. 문제 상황과 관련된 후속 학습 계획이 있다면 자유롭게 적어주세요.
내가 도입하고자 하는 이러한 방법을 팀원과 함께 컨벤션으로 정하고 그걸 강제적으로 모두가 지킬 수 있게 하는 방법에 대해 탐색하고 싶다. (GitHub ruleset 이용해서 컨벤션에 맞지 않는 행위를 원천 방지할 수 있나? 문서화는 어떻게 하는 게 좋을까?)
아직 Git Flow는 내 상황에선 오버스펙이라 느껴졌지만, 추후 팀 규모가 커지거나 릴리즈 관리가 필요해질 때 다시 도입을 검토할 예정이다.
혹시 더 나은 전략이나 경험이 있다면 댓글로 공유해 주세요! 읽어주셔서 감사합니다