회원가입을 하면 원하는 문장을
저장할 수 있어요!
다음
AWS 이용 중이라면 최대 700만 원 지원받으세요
국내 IT 기업은 한국을 넘어 세계를 무대로 할 정도로 뛰어난 기술과 아이디어를 자랑합니다. 이들은 기업 블로그를 통해 이러한 정보를 공개하고 있습니다. 요즘IT는 각 기업의 특색 있고 유익한 콘텐츠를 소개하는 시리즈를 준비했습니다. 이들은 어떻게 사고하고, 어떤 방식으로 일하고 있을까요?
회원가입을 하면 원하는 문장을
저장할 수 있어요!
다음
회원가입을 하면
성장에 도움이 되는 콘텐츠를
스크랩할 수 있어요!
확인
국내 IT 기업은 한국을 넘어 세계를 무대로 할 정도로 뛰어난 기술과 아이디어를 자랑합니다. 이들은 기업 블로그를 통해 이러한 정보를 공개하고 있습니다. 요즘IT는 각 기업의 특색 있고 유익한 콘텐츠를 소개하는 시리즈를 준비했습니다. 이들은 어떻게 사고하고, 어떤 방식으로 일하고 있을까요?
이번 글은 국내 대표 이커머스 기업 ‘SSG’의 공통서비스개발팀에서 ‘수시 배포를 위한 깃 브랜치(Git branch) 전략’을 소개합니다.
수시 배포를 위한 git branch 전략
안녕하세요. SSG공통서비스개발팀에서 백엔드 개발을 담당하고 있는 권태진입니다. 이번 글에서는 저희 팀에서 운영 중인 ‘수시 배포를 위한 Git branch 전략'을 소개하려고 합니다. 이에 대한 내용을 찾는 분들에게 조금이나마 도움이 되었으면 합니다.
이하 “Git branch 전략”과 “branch 전략”은 동일한 의미로 사용하도록 하겠습니다.
한창 MSA(Microservices Architecture)로의 전환을 진행하는 중이었던 저희 팀은 새로운 branch 전략이 필요한 상황이었습니다. MSA로 전환하면서 기존 정기 배포 방식은 버리고 수시 배포를 하기로 결정했기 때문이었죠. Git-flow, Github-flow, Gitlab-flow를 포함해 여러 branch 전략을 살펴보았지만, 팀 환경에 꼭 맞는 branch 전략은 없었습니다. 그래서 팀의 요구 사항과 환경에 맞는 branch 전략을 직접 만들기로 결정했습니다.
글의 이해를 돕기 위해 팀 개발 환경을 간략하게 설명하고 넘어가겠습니다. 현재 빌드&배포는 Bamboo를 이용하고 있고, Kubernetes를 사용 중입니다.
그리고 zone이라고 부르는 4가지 환경이 있습니다.
① Dev: 주로 개발자들이 테스트를 진행하는 환경
② QA: 주로 기획자와/QA가 테스트를 진행하는 환경
③ stg: 실제 운영 데이터를 사용하고 운영환경과 유사한 테스트 환경
④ prod: 실제 운영환경
버전 관리 저장소 및 코드 리뷰 도구는 bitbucket을 사용하고 있습니다.
branch 전략 수립을 위해 요구사항을 먼저 정리할 필요가 있었습니다. 우리는 왜 branch 전략을 도입하려고 하는가에 대한 답이었죠. branch 전략은 “이게 좋다더라”, “남들이 이렇게 하니깐 우리도 이렇게 하자”가 아닌 필요에 의한 것이어야 합니다. 여러분들도 branch 전략을 만들어야 한다면, 우선 branch 전략이 왜 필요한지에 대해 먼저 답해보세요. 사실 필요 없다면 굳이 도입할 필요는 없으니 말이죠.
저희의 요구사항은 크게 두 가지였습니다.
이렇게 적고 보니깐 요구사항이 간단해 보입니다. 하지만 위 두 가지 요구사항을 만족하는 branch 전략을 단기간에 만들기는 어려운 일이었습니다. 이제 저희 팀에서 운영 중인 branch 종류를 먼저 설명하고, branch를 기반한 배포 프로세스를 얘기해 보겠습니다. 그리고 브랜치 전략 수립 시 고민했던 이슈와 이슈를 해결한 방법을 공유해 볼게요.
아래는 저희 팀에서 운영 중인 branch와 branch가 배포되는 zone을 간략히 표시한 그림입니다.
그림1에 나와 있는 것처럼 저희 팀에서 운영 중인 브랜치는 “develop”, “feature”, “release”, “feature-ltp” 크게 4가지입니다.
실제 개발을 하기 위해 생성하는 branch로 release branch로부터 생성합니다. 개발 작업은 특별한 이유가 없는 한 feature branch에서 해야 합니다.
$ git checkout release
$ git checkout -b feature/12345/jira-1
#feature branch는 "feature/사번/jira번호" 형태로 생성하기로 약속되어 있습니다.
dev/qa zone으로 배포되는 branch입니다.
feature-ltp branch에 관한 얘기는 뒤에서 좀 더 자세히 다루겠습니다. 우선은 특수한 목적을 가진 branch 정도만 기억해 주시면 될 것 같습니다. 주요한 특징을 뽑아서 요약하면 아래와 같습니다.
이제 branch가 어떤 과정을 거쳐 운영(prod zone)까지 배포되는지 살펴보겠습니다.
개발할 사항이 생기면 가장 먼저 feature branch를 생성합니다.
$ git checkout release
$ git checkout -b feature/12345/jira-1
#feature 브랜치는 "feature/사번/jira번호" 형태로 생성하기로 약속되어 있습니다.
feature branch에서 개발 작업을 완료하고 나면 feature branch를 develop branch로 merge 합니다.
$ git checkout develop
$ git merge feature/12345/jira-1
이때 merge는 작업자가 직접 하고 충돌이 나는 경우 충돌 해결 후 merge를 완료합니다. develop branch로 merge가 되면 자동으로 빌드가 되고 빌드 된 결과가 dev/qa zone으로 배포됩니다. 배포 후 dev/qa zone에서 테스트를 진행합니다.
dev/qa zone에서 테스트를 완료하면 1) stg zone을 거쳐 2) 운영 환경(prod zone)으로 배포합니다. stg/prod zone 배포에는 release branch를 사용합니다. 운영에 반영되는 소스이니만큼 PR 후 코드 리뷰를 거쳐야 release branch로 merge 할 수 있습니다. 앞서 언급 드린 것처럼 저희 팀은 bitbucket을 통해 PR 및 코드 리뷰를 진행하고 있습니다.
코드 리뷰가 완료되면 bitbucket merge 버튼이 활성화되고 merge를 할 수 있습니다. (PR 없는 release branch 변경은 허용하지 않으며 release branch merge는 개발자가 직접 하지 않습니다.) merge 되면 release branch는 자동으로 빌드가 되고 stg zone으로 배포됩니다.
stg zone부터는 운영 데이터로 테스트가 가능하며 운영 배포 전 최종 테스트를 진행합니다. stg zone까지 테스트가 완료되었다면 이제 운영환경으로 배포가 가능합니다.
dev, qa, stg zone까지는 자동 빌드&자동 배포입니다. prod zone 배포는 자동으로 하지 않는데요. 자동 배포로 인한 혹시 모를 사고를 방지하기 위함입니다. prod zone 배포는 실제로 release branch를 사용하는 것은 아니며, stg zone에서 생성된 이미지를 그대로 prod zone으로 복사한다고 보시면 됩니다. prod zone 배포를 마지막으로 전체 배포 프로세스가 완료되었습니다.
저희 팀이 원했던 요구사항은 두 가지였습니다.
비정기 배포를 하기 위해서는 서로 간의 변경 사항이 독립적이어야 했고, 나의 배포에 다른 누군가의 변경 사항이 같이 배포되는 일이 없어야 했습니다. 이 이슈 해결을 위해 저희는 먼저 branch를 분리했습니다. 배포용 branch인 release branch와 개발을 위한 feature branch로 말이죠.
하지만 feature branch로 각자의 개발 작업을 분리했다고 해서, 다른 누군가의 변경 사항이 배포되지 않는다는 보장은 없었습니다. 운영 배포용 release branch는 하나이기 때문에, 다른 누군가의 feature도 merge가 되어 있을 수 있기 때문이죠.
이 문제를 해결하기 위해 저희는 다음과 같은 규칙을 정했습니다.
1) release branch에 반영된 내용은 가능한 한 빨리 운영에 배포하고 길어도 최대 하루를 넘기지 말 것
2) release branch를 중/장기간 테스트해야 한다면 release branch를 사용하지 말고 feature-ltp branch를 사용할 것
규칙을 정하면서 “그럼에도 다른 누군가의 변경 사항이 같이 배포되는 일이 생길 수 있을 것 같다”라는 고민은 계속되었습니다. 어쨌든 하루라는 기간 동안 서로 다른 누군가의 변경 사항이 동시에 있을 수 있기 때문이였죠. 고민 끝에 저희가 내린 해답은 우선 해보고 실제로 그런 일이 자주 발생한다면 그 때 방안을 다시 찾아보자였습니다. 이론과 실제는 다를 수 있고 저희가 고민했던 문제의 경우, 실제 발생했을 때 해결해도 큰 이슈가 될 일이 아니었기 때문이죠.
결론부터 말씀드리면 저희가 우려했던 “release 브랜치에 서로 다른 여러 명의 배포되지 않은 변경 사항이 존재하는 경우”는 거의 없었습니다. 오히려 수시 배포를 진행할 수 있다 보니, 배포되지 않은 소스가 머물러 있는 시간이 적었기 때문이죠.
코드 리뷰에 대해 고민되는 부분은 아래의 두 가지였습니다.
1) 언제 코드 리뷰를 할 것인가?
2) 코드 리뷰를 배포 프로세스에 자연스럽게 녹아들게 하려면 어떻게 해야 할까?
branch의 종류가 완전히 정해지기 전에는 어떤 branch로 merge 하기 전(즉 어느 시점에)에 리뷰를 할 것이냐에 대한 고민이 많았습니다. 그러다 branch의 종류와 용도가 정해지면서 자연스럽게 1번의 문제가 해결되었습니다. release branch가 실제 서비스되는 branch이므로, release branch로 merge 하기 전에 코드 리뷰를 하면 되는 거였죠.
두 번째 문제는 bitbucket을 활용해서 해결했습니다. bitbucket에는 branch 별로 권한을 줄 수 있는 기능과 Merge 가능한 조건을 부여할 수 있는 기능이 있습니다. 위 두 기능을 활용하면 PR을 올린 후 코드 리뷰를 완료해야, release branch로 merge 될 수 있도록 강제할 수 있습니다. 코드 리뷰가 강제되기 때문에 배포 프로세스에 코드 리뷰 과정을 포함시킬 수 있었습니다.
feature branch는 release branch로부터 생성합니다. develop branch도 release branch로부터 생성되죠. feature branch가 develop branch에서 생성된 게 아니다 보니, develop branch로 merge를 하다 보면 develop branch의 history는 알아보기 힘들게 되고, 소스 병합 시 충돌도 자주 발생하지 않을까 하는 우려가 있었습니다.
아래는 현재 develop branch의 commit 그래프입니다.
그림2) develop branch commit graph
commit history의 일부분인데도 한눈에 history가 엉망인 걸 알 수 있습니다. 실제 충돌도 종종 발생하고 있고요. 우려했던 현상이 발생하고 있습니다. history가 엉망인 것은 작은 문제였지만, 잦은 충돌은 개발 생산성을 떨어뜨릴 수 있기 때문에 중요한 문제였습니다. 저희가 고민 끝에 내린 결론은 문제가 심해지면 ‘develop branch’를 새로 만들자였습니다.
어차피 저희에게 중요한 것은 release branch이고 develop branch는 “그렇게까지 중요하지 않다.”, “다시 만든다고 큰 문제가 생기지는 않을 것이다.”라고 생각했습니다. 문제가 될 만한 상황은 develop branch로 테스트 중인 누군가의 코드가 삭제될 수도 있다는 것이었죠. 하지만 이 부분도 실상은 큰 문제가 안 되는 것이 develop branch 재생성을 하기 전에 1차적으로 팀 내 공유하면 됐고, feature branch는 여전히 있기 때문에 develop branch 재생성 후 feature branch를 다시 merge 하면 되는 일이었습니다.
그렇게 결정하고 운영하는 지금까지 누군가의 코드가 삭제된 경우는 없었습니다. 결정적으로 develop branch를 재생성하는 일이 생각보다 자주 발생하지 않고 있습니다.
브랜치 전략 만들 당시 release branch에 대한 우려는 크게 두 가지였습니다.
서로 같은 소스를 수정하게 되는 경우 release branch로 소스 병합 시 충돌이 발생할 수 있습니다. release branch는 운영에 배포되는 소스들입니다. 충돌을 해결하는 과정에서 소스를 잘못 merge 하는 일이 생긴다면 장애로 이어질 수도 있습니다. 충돌이 자주 나게 되면 merge 과정에서 그러한 실수가 발생할 확률이 올라가게 되죠.
첫 번째 고민에 대한 해결은 우선 적용해 보자였습니다. (우선 적용해 보자가 좀 많네요.) “실제로 우리가 생각하는 것만큼 자주 충돌이 발생하지 않을 수도 있다.”, “팀원과 bitbucket을 믿어보자.”, “잦은 충돌로 문제가 생기면 그때 해결책을 깊게 고민해 보자”라는 생각이었습니다. 지금까지 운영하면서 release branch로 merge가 잘못되어 문제가 생긴 경우가 없는 것을 보면, 옳은 선택이었던 것 같습니다. 이런 일이 왜 생기지 않는지 생각해 보면 수시 배포이기 때문에, 오히려 더 괜찮은 것 같습니다. 수시 배포 때문에 걱정했던 일이 사실은 수시 배포로 인해서 더 생기지 않고 있는 것이죠.
두 번째 고민은 소스 병합 정책을 merge로 할 건지 rebase로 할 건지였습니다. 이 고민도 우선은 merge로 결정하고, release branch의 history가 어떻게 생기는지를 보고 수정해 가기로 했습니다. merge로 정책을 정하고 어느 정도 시간이 흐른 후, release branch history를 보니 문제가 두 가지 있었습니다. ① history가 깔끔하지 않은 점, ② 불필요한 커밋이 많은 점이었습니다.
merge로 운영할 당시 release branch의 commit history입니다.
그림3) release branch commit(feat. merge)
develop branch의 history보다는 낫지만, 그럼에도 깔끔하다는 생각은 들지 않습니다. 그리고 의미 없는 commit들이 꽤 많았습니다. 이상적으로는 하나의 feature에 하나의 commit이 생성되어야 하지만, 하나의 feature에 하나의 commit을 만든다는 게 생각보다 쉽지 않습니다. 코드 리뷰 단계에서 코드를 수정해야 하거나, 그 외 생각지 못한 시점에 코드를 수정해야 하는 경우는 언제든 발생할 수 있으니 말이죠.
그래서 저희는 소스 병합 정책을 merge에서 squash + rabase로 변경했습니다. (소스 병합 정책은 bitbucket 설정에서 쉽게 변경 가능합니다.)
소스 병합 정책 변경 후 commit 그래프는 아래와 같습니다.
저희가 원하는 대로 깔끔한 history 그래프가 만들어졌고 이후로도 쪽 이런 모습을 유지하고 있습니다.
지금까지 저희 팀에서 운영 중인 수시 배포를 위한 Git branch 전략을 소개해 봤습니다. 사실 브랜치 전략에는 정답이 없는 것 같습니다. 전략을 만들 당시에도 이게 맞을까, 저게 맞을까 정답을 찾고자 많이 고민했고요. 결국 중요한 것은 정답이 아니라 우리의 상황에서 최선이 무엇인지를 찾는 것이었죠. 저희도 계속해서 부족한 부분을 수정해 나가고 있는 중입니다.
여러분도 branch 전략을 만들 때 정답이 아닌, 현 상황에 맞는 최선이 무엇인지를 고민해 보신다면 팀에 꼭 맞는 branch 전략을 만들 수 있을 거라 생각합니다.
<원문>
요즘IT의 모든 콘텐츠는 저작권법의 보호를 받는 바, 무단 전재와 복사, 배포 등을 금합니다.