팀에서 여러 명이 같은 코드를 만지다 보면, 각자 자기 작업을 main 하나에 마구 쌓게 돼요. 그러다 어제까지 잘 되던 화면이 오늘 갑자기 깨지고, 누가 뭘 건드렸는지 아무도 몰라 서로 눈치만 보죠. 저도 신입 때 이걸 겪고 나서야 브랜치를 나눠 쓴다는 말의 무게를 알았어요. 브랜치 전략은 어렵게 들리지만, 결국 여러 사람이 같은 코드베이스에서 안 부딪히고 일하기 위한 약속이에요.
이번 편에서는 대표적인 세 가지 방식, Git Flow, GitHub Flow, 트렁크 기반 개발을 하나씩 풀어 볼게요. 정답이 하나 정해져 있는 게 아니라, 팀 규모와 배포 주기에 맞게 고르는 거라서 세 방식의 성격을 알아 두면 상황마다 판단이 서요. 이름은 거창해도 속을 들여다보면 생각보다 단순하니 겁먹지 말고 따라와 주세요.
83.1 브랜치는 왜 나눠 쓰나요?
핵심은 main을 항상 배포 가능한 상태로 지키는 거예요. 새 기능을 만들 땐 main에서 갈라져 나온 내 작업용 브랜치에서 실컷 만지고 부수다가, 다 되면 다시 main에 합쳐요. 그러면 작업이 끝나기 전 어수선한 코드가 main을 더럽히지 않죠. Git에서 브랜치 하나 만드는 건 파일을 복사하는 게 아니라 현재 지점을 가리키는 이름표 하나를 다는 정도라서 아주 가벼워요.
git switch -c feature/login
이렇게 하면 main에서 갈라진 feature/login 브랜치로 넘어가요. 여기서 커밋을 쌓다가 작업이 끝나면 main으로 돌아와 합쳐요. 합칠 때 저는 --no-ff를 즐겨 써요. 이 옵션은 "이 기능은 여기서부터 여기까지 한 덩어리였다"는 합침 표시(merge commit)를 남겨 줘서, 나중에 히스토리를 볼 때 기능 단위가 눈에 확 들어오거든요.
$ git merge --no-ff feature/login
Merge made by the 'ort' strategy.
이 한 줄이 뜨면 합침이 성공한 거예요. 브랜치를 나누면 동시에 여러 기능을 나란히 진행할 수 있고, 급한 버그가 터져도 남의 작업을 방해하지 않고 따로 갈라 고쳐서 바로 배포할 수 있어요. 작업이 엎어지면 그 브랜치만 버리면 되니 마음 편히 실험할 수도 있고요.
83.2 Git Flow는 뭐가 그렇게 복잡한가요?
Git Flow는 브랜치 종류를 역할별로 딱 나눠 두는 방식이에요. 늘 살아 있는 브랜치가 둘인데, 완성돼 배포된 코드가 사는 main과, 다음 배포를 준비하며 기능이 모이는 develop이에요. 새 기능은 develop에서 갈라진 feature 브랜치에서 만들고, 배포를 앞두면 release 브랜치에서 막판 점검을 하고, 이미 나간 버전에 급한 문제가 생기면 main에서 hotfix 브랜치로 바로 고쳐요.
규칙이 촘촘한 만큼, 정기적으로 버전을 묶어 배포하는 팀엔 잘 맞아요. 설치형 프로그램이나 버전 번호가 중요한 제품이 그렇죠. 어느 브랜치가 무슨 일을 하는지 딱 정해져 있어서 큰 팀도 헷갈리지 않아요. 대신 브랜치가 많고 오가는 규칙이 많아, 하루에 여러 번 배포하는 요즘 웹 서비스엔 과하다는 말을 자주 들어요. 저도 작은 팀에서 이걸 억지로 쓰다가 규칙에 치여 오히려 느려진 적이 있어요.
83.3 GitHub Flow는 왜 단순한가요?
GitHub Flow는 살아 있는 브랜치를 main 하나로 확 줄여요. 일이 생기면 main에서 짧게 사는 작업 브랜치를 따고, 거기서 작업한 뒤 풀 리퀘스트(코드 합침 요청)를 올려요. 동료가 리뷰하고 자동 검사가 통과하면 main에 합치고, 합친 건 바로 배포해요.
develop도 release도 없으니 머릿속이 훨씬 가벼워요. main은 언제나 배포 가능하다는 규칙 하나만 지키면 되거든요. 그래서 수시로 배포하는 웹 서비스 팀이 가장 많이 써요. 저희 팀도 이 방식으로 옮기고 나서, 기능 하나가 준비되면 그날 바로 내보내는 리듬이 생겼어요. 브랜치가 오래 살지 않으니 합칠 때 충돌도 확 줄었고, 리뷰 단위가 작아 서로 부담 없이 봐 주게 됐어요.
83.4 트렁크 기반 개발은 또 뭐예요?
트렁크 기반 개발은 GitHub Flow에서 한 발 더 나가요. 여기서 트렁크는 main을 부르는 다른 이름이에요. 모두가 아주 짧게 사는 브랜치로 작업하거나, 심지어 main에 하루에도 여러 번 직접 합쳐요. 브랜치가 며칠씩 떨어져 지내면 나중에 합칠 때 크게 부딪히니까, 아예 자주 합쳐서 충돌을 잘게 쪼개 버리는 거죠.
아직 덜 된 기능이 배포에 섞이는 걸 막으려고 기능 토글(코드로 켜고 끄는 스위치)을 함께 써요. 코드는 main에 들어가 있지만 사용자에겐 아직 꺼져 있는 식이죠. 배포 자동화가 탄탄한 팀이 가장 빠른 속도를 낼 때 택하는 방식이에요. 대신 자동 검사가 부실하면 main이 자주 깨지니, 받쳐 주는 도구가 준비돼야 해요. 준비 없이 흉내만 내면 오히려 사고가 잦아져요.
83.5 합치다 충돌이 나면 어쩌죠?
브랜치를 나눠 쓰면 같은 줄을 두 사람이 서로 다르게 고치는 일이 생겨요. 합칠 때 Git이 어느 쪽이 맞는지 판단을 못 해서 충돌(conflict)이라며 멈춰 서죠. 처음 보면 당황스러운데, 사실 Git이 함부로 덮지 않고 사람에게 물어봐 준 거예요. 충돌한 파일을 열면 양쪽 내용이 표식과 함께 나란히 들어 있어요. 우리는 둘 중 맞는 걸 남기거나 둘을 잘 섞어 정리한 뒤, 그 파일을 add하고 커밋으로 마무리하면 돼요.
충돌은 잘못이 아니라 여러 사람이 활발히 일한 증거예요. 다만 브랜치를 너무 오래 떨어뜨려 두면 그동안 쌓인 차이가 많아 충돌도 커지죠. 그래서 저는 브랜치를 짧게 살리고 자주 합쳐요. 앞의 GitHub Flow와 트렁크 기반이 브랜치를 짧게 가져가는 것도, 결국 충돌을 잘게 쪼개 다루기 쉽게 만들려는 이유예요.
83.6 그래서 우리 팀은 뭘 골라야 해요?
저는 세 가지를 봐요. 첫째 배포 주기예요. 몇 주에 한 번 버전을 묶어 내면 Git Flow, 준비되는 대로 수시로 내보내면 GitHub Flow나 트렁크 기반이 어울려요. 둘째 팀 크기와 숙련도예요. 사람이 적고 손발이 맞으면 단순한 쪽이 빠르고, 인원이 많고 배포가 무거우면 규칙이 있는 쪽이 안전해요. 셋째 자동화 수준이에요. 합칠 때마다 자동 검사와 배포가 돌아가면 단순한 전략을 밀어붙여도 main이 안 깨지지만, 그게 약하면 release 같은 완충 단계가 필요해요.
정하고 나면 그걸로 끝이 아니라, 반년쯤 써 보고 안 맞는 규칙은 덜어내며 팀에 맞게 다듬는 게 좋아요. 남의 팀에서 좋았다고 우리 팀에 그대로 맞는 건 아니거든요.
83.7 정리하면 브랜치 전략의 뼈대는요?
어떤 전략을 고르든 바탕은 같아요. main은 항상 배포 가능하게 지키고, 작업은 갈라진 브랜치에서 하고, 합치기 전에 리뷰와 자동 검사로 거른다는 거죠. 브랜치 이름도 feature/로그인, fix/결제오류처럼 종류와 내용이 보이게 지으면, 목록만 봐도 지금 무슨 일이 도는지 한눈에 들어와요.
전략은 팀을 옭아매는 족쇄가 아니라, 여러 사람이 안 부딪히게 돕는 공용 신호등이에요. 규칙이 일을 느리게 한다 싶으면 그건 팀에 안 맞는다는 신호니 과감히 바꾸면 돼요. 세 방식의 성격을 알아 두면, 팀이 커지거나 배포 방식이 바뀔 때 어느 쪽으로 옮겨야 할지 스스로 판단할 수 있을 거예요.