Waylog Blog

Git 고급 활용법: 실무에서 바로 쓰는 워크플로우와 트러블슈팅

DevOps

Git Workflow

Git은 현대 소프트웨어 개발의 필수 도구입니다. 기본적인 add, commit, push 명령어는 누구나 알지만, 실무에서 마주하는 복잡한 상황들을 해결하려면 더 깊은 이해가 필요합니다. 이 글에서는 Git의 고급 기능과 실제 팀 개발에서 유용한 워크플로우를 상세히 다룹니다.

1. 브랜치 전략의 이해

1.1 Git Flow vs GitHub Flow

Git Flow는 Vincent Driessen이 제안한 전통적인 브랜치 전략입니다. main(또는 master), develop, feature, release, hotfix 브랜치를 사용하여 체계적인 릴리스 관리를 합니다. 장기 프로젝트나 명확한 릴리스 주기가 있는 소프트웨어에 적합합니다.

GitHub Flow는 더 단순한 접근입니다. main 브랜치와 feature 브랜치만 사용하며, 모든 변경은 Pull Request를 통해 main에 병합됩니다. 지속적 배포(CD)를 실천하는 웹 서비스에 적합합니다.

두 전략 중 무엇이 더 좋다고 단정 짓기는 어렵습니다. 팀의 규모, 프로젝트의 특성, 배포 주기에 따라 적절한 전략을 선택해야 합니다. 소규모 팀이라면 GitHub Flow의 단순함이, 대규모 엔터프라이즈 환경이라면 Git Flow의 체계성이 더 적합할 수 있습니다.

1.2 Trunk-Based Development

최근에는 Trunk-Based Development(TBD)도 주목받고 있습니다. 모든 개발자가 짧은 수명의 feature 브랜치(하루 이내)를 사용하고, 자주 trunk(main)에 병합합니다. Feature Flag를 활용하여 완성되지 않은 기능을 숨기는 방식입니다. Google, Facebook 같은 대규모 조직에서 채택하고 있으며, 지속적 통합(CI)을 극대화할 수 있습니다.

2. 히스토리 관리의 기술

2.1 Interactive Rebase

git rebase -i는 커밋 히스토리를 정리하는 강력한 도구입니다. 여러 커밋을 하나로 합치고(squash), 커밋 메시지를 수정하고(reword), 커밋 순서를 변경하고, 불필요한 커밋을 삭제할 수 있습니다.

주의할 점은 이미 원격 저장소에 push한 커밋은 rebase하지 않는 것입니다. 히스토리가 재작성되면 다른 팀원들과 충돌이 발생하기 때문입니다. 로컬에서 작업 중인 브랜치에만 적용하세요.

2.2 좋은 커밋 메시지 작성법

커밋 메시지는 미래의 자신과 동료를 위한 문서입니다. 제목은 50자 이내로 명령문으로 작성합니다. 예를 들어 "Fixed bug"보다 "Fix login redirect loop on mobile browsers"가 훨씬 유용합니다.

본문에는 왜(Why) 이 변경이 필요한지, 무엇(What)을 변경했는지를 설명합니다. 어떻게(How)는 코드가 설명하므로 굳이 적지 않아도 됩니다. 관련 이슈 번호나 티켓 링크를 포함하면 추적성이 높아집니다.

3. 충돌 해결 전략

3.1 Merge vs Rebase

브랜치를 통합하는 두 가지 방법이 있습니다. Merge는 두 브랜치의 히스토리를 그대로 유지하면서 merge commit을 생성합니다. 언제 어떤 브랜치가 병합되었는지 명확히 기록됩니다.

Rebase는 feature 브랜치의 커밋들을 main 브랜치 위로 재배치합니다. 직선적인 히스토리를 만들어 가독성이 좋지만, 공유된 브랜치에서는 사용을 피해야 합니다.

일반적인 권장 사항은 로컬 작업에서는 rebase를, 공유 브랜치에서는 merge를 사용하는 것입니다. Pull Request를 올리기 전에 main을 rebase하여 최신 상태로 만들고, PR이 승인되면 merge합니다.

3.2 충돌 해결 도구

복잡한 충돌은 텍스트 편집기로 해결하기 어렵습니다. git mergetool 명령어로 시각적 도구를 활용하세요. VS Code, IntelliJ, Beyond Compare, P4Merge 등을 설정할 수 있습니다.

충돌이 자주 발생한다면 브랜치 수명이 너무 길지 않은지 점검해보세요. 작은 단위로 자주 병합하면 충돌의 규모와 빈도를 줄일 수 있습니다.

4. 실수 복구하기

4.1 git reflog - 최후의 보루

git reflog는 HEAD가 가리킨 모든 커밋의 기록을 보여줍니다. reset --hard로 커밋을 날렸더라도, rebase를 잘못했더라도, reflog에서 이전 상태를 찾아 복구할 수 있습니다. Git은 90일간 모든 기록을 보관합니다.

4.2 다양한 되돌리기 방법

git checkout은 특정 파일을 이전 상태로 되돌립니다. git reset은 커밋을 취소하되 --soft, --mixed, --hard 옵션에 따라 스테이징과 워킹 디렉토리 처리가 달라집니다. git revert는 새로운 커밋을 만들어 이전 변경을 취소하므로, 공유된 브랜치에서는 revert가 안전합니다.

5. 협업을 위한 고급 기능

5.1 git stash 활용

작업 중 급히 다른 브랜치로 전환해야 할 때 stash가 유용합니다. git stash로 현재 변경사항을 임시 저장하고, 나중에 git stash pop으로 복원합니다. git stash list로 여러 stash를 관리할 수 있고, git stash branch로 stash 내용을 새 브랜치로 만들 수도 있습니다.

5.2 git bisect로 버그 찾기

회귀 버그가 발생했을 때 어느 커밋에서 문제가 시작되었는지 찾기 어려울 수 있습니다. git bisect start로 시작하고, 버그가 있는 커밋(bad)과 정상인 커밋(good)을 표시하면, Git이 이진 탐색으로 문제의 커밋을 찾아줍니다.

5.3 git blame과 git log

git blame은 파일의 각 줄을 마지막으로 수정한 커밋을 보여줍니다. 왜 이렇게 작성되었는지 맥락을 파악할 때 유용합니다. git log -p는 커밋과 함께 diff를 보여주고, git log --follow는 파일 이름이 변경되어도 히스토리를 추적합니다.

6. 성능 최적화

대규모 저장소에서는 성능이 중요합니다. git gc는 불필요한 파일을 정리하고 저장소를 최적화합니다. Shallow Clone(git clone --depth 1)은 최신 히스토리만 가져와 CI/CD 환경에서 빌드 시간을 단축합니다. Sparse Checkout은 모노레포에서 필요한 디렉토리만 체크아웃할 수 있습니다.

결론

Git은 단순한 버전 관리 도구를 넘어 팀 협업의 근간입니다. 기본 명령어를 넘어 rebase, reflog, bisect 같은 고급 기능을 익히면, 복잡한 상황에서도 자신 있게 대처할 수 있습니다. 무엇보다 중요한 것은 팀과 프로젝트에 맞는 일관된 워크플로우를 정립하고, 모든 팀원이 이를 따르는 것입니다.