git flow; 환상과 현실 그 사이에 서비스

Written by Vallista2022년 2월 13일 · 6 min read

이미지 0

git을 이용하여 수많은 서비스의 코드가 관리되고 있다. 이 git은 버전관리 시스템인 만큼, 수많은 코드를 분기별로 저장시킬 수 있고 이 분기(브랜치)별로 개인이 로컬에 들고 있을수도 있다. 특히, 이 분기를 이용하여 다양한 환경에 소스코드가 배포되도록 하는 CD를 구축할 수도 있다. 그래서 CI/CD와 이 코드의 형상관리는 큰 관계성이 있는데, git으로 원격저장소에 어떻게 배포되냐에 따른 이벤트를 이용해 이후의 자동 단계를 어떻게 구성할지 정리하고 생산성을 향상 시킬 수 있기 때문이다.

오늘 글은 우리 팀에서 쓰이는 git flow의 여러 문제를 찾고, 그걸 어떻게 보완하려 했는지 이야기를 통해 앞으로 어떤 형태로 나아갈 것인지 이야기해보려 한다.

이 이야기를 하기 전, 메이저 전략을 사용하지 않는다고해서 실패한 전략이 아님을 이야기하고 싶다. 서비스의 크기, 특징, 구성원, 역량 등 수많은 상황은 이러한 전략을 바꿔서 적용해야하며, 이러한 브랜치 전략은 메타적으로 고려만 하면 된다. 우리는 서비스에 맞게 적용하면 된다.

분리 전, 팀 브랜치 관리

초기, 우리팀은 git flow를 사용했다. (git flow가 무엇인지는 아래에서 후술한다.) 워낙 배민에서 많이 쓰고 있기도 했고, 내가 조인했을때 우리팀은 브랜치 전략을 바꾸기에 열약한 환경이었다. (iteration이 너무나 빨랐고, 폭발적으로 성장하는 비즈니스에 비해 개발자는 턱없이 부족했다.)

그렇게 정돈되지 않은 채, 서버와 프론트가 얽히며 여러 작업이 가파르게 진행되고 있었다. 가파르게 진행되다보니 여러 작업은 롤백과 급작스런 추가도 반복되었다. 갈수록 git flow는 장점을 잃어갔다.

우리 팀에서 제작하던 B마트는 빈번한 배포를 통해 사용자의 가치를 끊임없이 제공하는 커머스 서비스였고, git flow의 수많은 브랜치는 신규/기존 입사자 상관없이 히스토리를 commit 상으로 해석하기 매우 어려웠다.

주마다 배포가 진행된 다음 오후에 백포팅이 진행되는데, 백포팅이 진행되면 거의 무조껀 컨플릭트는 일어났고, 가끔씩 변동사항이 너무 크면 다들 모여서 노트북 하나로 돌아가면서 컨플릭트를 고치고 develop에 force-push를 빈번히 했다.

프론트엔드 분리

정신없이 진행되던 한달 동안, 프론트엔드의 코드 베이스는 급속도로 커져갔고 이러한 문제를 프론트만이라도 분리하고 싶었다. 왜냐하면 추후 들어올 개발자가 히스토리 파악하는 고통을 겪게 하고 싶지 않았기 때문이었다.

그래서 프론트엔드 소스코드와 백앤드의 의존도를 분리시키는 작업을 진행했고, 진행 후 새로운 레포지토리에 코드를 테라포밍했다. 테라포밍 후 나는 브랜치 전략을 새로 확립하게 된다. 어떻게 확립했는지 설명하기위해 git flow에 대해서 설명한다.

git flow

이미지1

git flow의 대표적인 사진

가장 유명한 방법론인 "git flow"는 2010년 빈센트 드리슨이 창안했다. 여기서 가장 중요한 두 개의 브랜치는 다음과 같다.

  • main (master): 이 브랜치는 실제 프로덕션에 배포되는 코드에 해당한다. 모든 개발코드는 검수과정을 거쳐서 여기로 향하고 합쳐진다.
  • develop: 이 브랜치는 개발중인 코드가 포함되어있고, 곧 main에 머지될 코드들이다. (테스트를 진행중인) 후술할 feature 브랜치들을 개발자가 개발하면, 어느정도의 개발자 테스트를 통해 이 브랜치로 올린다.

개발주기 (해당 프로젝트의 개발사항이 운영에 반영되어야 하는 개발 일정) 동안 develop, main 브랜치를 기준으로 여러 브랜치가 파생된다.

  • feature-*: 일반적으로 피쳐 브랜치라고 일컫는데, 새로운 기능을 "feature"라고 표현한다. feature 브랜치는 현재 remote 의 개발 브랜치 기준으로 만들어지며, 개발로 병합된다.
  • hotfix-*: 핫픽스 브랜치는 main에서 분기가 되며, 운영의 기능이 긴급하게 고쳐져야 하는 경우 사용된다.
  • release-*: 운영 환경에 새로 배포될 릴리즈를 묶는 역할을 한다. develop에서 분기하고 작업 후 마스터로 병합한다.

이러한 git flow의 장점은 명확하게 요소들이 어떤 역할을 하는지 오랜시간에 걸쳐 규칙적으로 개발자들에게 내려져 온 만큼 수많은 툴들이 존재하며, 이 수많은 툴로 하여금 git flow는 쉽게 운영할 수 있다는 것에 있다. 또한 CI/CD에 디펜던시 되어있지 않기 때문에 철학적으로만 집중할 수 있으며, DevOps가 없는 스타트업이나 목표가 명확한 SI등의 업체에서 자동화를 하지 않고 수동으로 해당 전략을 사용할 수도 있다.

비즈니스 환경에 맞게 개조하기

git flow는 main의 버전이 태그단위로 운용된다. 그래서 롤백을 진행할 때 tag로 하여금 빠르게 롤백할 수 있도록 제공한다. 그렇기에 우리 비즈니스상 배포가 되는 것은 결국 하나이므로, main 전략은 유지해도 되었다. 문제는 이 전의 release와 develop이었다. git flow 전략에서 release는 하나만 존재한다. develop에 완료된 기능을 release에 태우고, release에서 테스트한다. (보통 담당하는 QA 분들이 있을 것이다. 혹은 기획자라도.) 운영으로 나가기 이 전에 테스트를 release에서 진행하는데, 상황을 한 가지 예시를 들어보겠다.

B마트의 비즈니스는 매 주 배포가 빈번히 나가며, 심지어 매주 두 번 나갈수도 있고, 그 이상 나갈수도 있다. 그런데 이 배포의 사이즈가 일관적이지 않으며 이번주는 작지만, 다음주엔 큰 배포건이 있을수도 있다는 것이다. 이번주에 나가기 위해 저번주에 release로 develop에 있는 내용을 옮겼다. 그렇게 release를 배포를 진행했는데 다음주에 배포가 진행되는 건이 사이즈가 커서 저번주에 함께 나갔었다.

이러한 상황에서 release는 온전히 main으로 머지될 수 있는가? 여기서 release branch는 테스트 배포를 위한 브랜치 목적으로 사용되었는데, 온전히 main으로 병합될 수 없으므로 release에서 cherry-pick이든 후속처리를 통해 main으로 가야한다. 그렇기에 B마트의 비즈니스상 릴리즈 브랜치가 여러개 존재할 수 밖에 없고, 이는 git flow와 다른 양상으로 흐르게 된다.

그래서 B마트 프론트는 release/v1.6.0 형태로, 배포 번호를 매겨 그 주에 배포될 건을 매칭시켰다. 이를 지라의 릴리즈 노트와 연결시켜 기획, QA와 싱크업을 진행했다. 그렇게 언제 어떤 작업이 나가는지 기획은 릴리즈 노트와 티켓의 현황을 확인하면 되고, 개발자는 티켓을 작성하고 릴리즈 노트에 연결시킨 후, release 브랜치 기준으로 작업이 진행되었다.

이미지2

  • 마스터를 기준으로 develop 브랜치를 생성한다.
  • develop 브랜치를 기준으로 feature 브랜치를 작업해서 develop에 머지할 수도 있으며, 그게 아니라면 만들어두고 release에 머지할수도 있다.
  • 모든 작업은 해당 브랜치로 rebase후 merge를 진행한다
  • main를 기준으로 release 브랜치를 생성한다
    • release/YYYY-MM-DD 로 진행한다.
    • 현재는 개발 초반이므로 배포 날짜가 확정되지 않았으므로 release로 칭한다
  • release를 기준으로 feature 브랜치를 작업해서 release에 머지한다. 이때 PR을 요청한다.
    • PR의 merge로 인한 no-ff 옵션을 추가해서 제공한다
    • 다만 역인, develop으로 가는것은 권장하지 않는다
  • release에는 다양한 배포날 단위의 작업이 포함되며, 배포날 main에 병합되며 운영 배포가 진행된다.

이렇게 되니 개발과 기획이 싱크업이 편해졌으며, 여러 release 버전에 대해 사전에 만들어두기만 하면 쉽게 대응 할 수 있게 변경되었다. 다시 이야기로 돌아와서, 문제라고 언급했던 두 릴리즈의 테스트는 아래와 같이 제공했다.

  • release/v1.6.0 이 배포되어있는 상황에서 release/v1.7.0 이 배포되어 테스트 필요
  • 채널에 베타를 점유한다고 공지 후, release/v1.7.0 을 배포, QA는 1.7.0 테스트를 진행한다
  • release/v1.7.0 테스트가 종료된 후, release/v1.6.0으로 재배포하여 진행한다.

커뮤니케이션만 진행되면 문제는 발생하지 않는다. 음. 근데 커뮤니케이션만 진행되면? 커뮤니케이션이 안되면 무슨 참사가 일어나는걸까?

  • 커뮤니케이션이 진행되지 않고 릴리즈 롤백이나 다른 배포 피쳐가 추가되면 QA가 진행하고 있는 QA 시트가 커다란 문제가 생긴다.
  • 갑자기 없었던 피쳐가 구동되고, 기존에 어디까지 완료되었는지 체크할 수가 없게된다. 또한 어떤 사람은 재확인했는데 피쳐가 없어져서 수많은 사람이 채널에 나 혹은 배포자를 태그하고 확인하려 할 것이다.

커뮤니케이션이 안되었다고 이러한 큰 혼란이 일어나는 이유는, 릴리즈들은 main 혹은 develop 기준으로 만들어졌기 때문에 이후 release와 이전 release가 서로간 작업이 배포되지 않는 한 공유가 되지 않는다.

git flow; 빈번한 배포환경에 맞는걸까?

release가 어떻게 붙었든, 결국 작업은 동일하고 배포를 어디에, 언제 하는지만 다른건데 왜 우리는 두 작업이 서로 나뉘어져 있어서 "커뮤니케이션"을 통해 테스트를 잡아야하는 환경이 된 걸까?

이는 git flow의 너무 많은 정책때문에 그렇다. 매주 빈번하게 나가는 서비스는 git flow의 수많은 브랜치를 거치지 않으면서 유동적으로 배포될 수 있어야 한다. develop, release, main이 필요없다는 소리이다. develop을 삭제하고 main을 기점으로 release만 반복하면 된다. 배포는 tag를 기준으로 나누면 된다.

develop을 삭제하면 아래와 같은 이점을 얻을 수 있다.

  • main에서 develop으로 백포팅을 안해도 된다.
  • hotfix는 main를 기준으로 진행되기 때문에 hotfix가 머지된 main를 다시 develop으로 백포팅하지 않아도 된다
  • 자동화가 쉬워진다. 기존 develop과 main이 싱크업이 안되어 여러 자동화 작업이 불가능 했기 때문이다.
  • gitlab, github등에 존재하는 default branch를 develop으로 할지 고민 안해도 된다

어느정도 팀이 안정되고 대규모 개발이 일어나서 빈번한 배포가 일어날때, git flow는 장점이 퇴색된다. 많은 브랜치는 신경써야할 부분을 크게 늘리며, 자동화를 하기 까다롭게 만든다. 그렇기 때문에 이를 더 단순화시킨 gitlab flow, github flow등이 존재한다.

beyond next, gitlab flow?

gitlab flow는 기능 중심 개발 방법과 이슈 트래킹 시스템에 연계되는 기능 개발 브랜치를 합친 개념이다. 쉽게 이야기하면 위에서 언급한 feature 단위의 개발 방법을 이슈 트래킹 시스템과 연동시켜, 단순하고 빠르게 git 작업을 진행할 수 있게한다.

이미지 3

gitlab flow는 git flow의 복잡한 여러 브랜치에서, 단순히 배포로 가기위한 과정을 목표로 축약한 개념이다.

  • production: 위에서 언급한 main에 대응되는 개념이다. 하지만 다른점은, 오로지 배포만을 바라본다.
  • pre-production: production으로 가기 전, main 브랜치의 당시 형상을 배포하여 테스트하는 브랜치.
  • main (master): developer 권한 사용자는 main 브랜치에서 신규 브랜치를 추가하여, MR을 생성하고 main으로 merge 요청을 보낸다. 모든 작업이 main 한 브랜치에 쌓이게 된다.

결국 develop과 master의 분리가 필요없는게, 코드는 다 가지고 있고 언제 뭐가 배포되는지만 신경쓰면 되는 것이다. 관점이 다른데, git flow는 "개발"이 주 목적이지만, gitlab flow는 "배포"가 중심에 있다. 그렇기 때문에 배포 전용 branch가 존재하고, 이 브랜치에서 히스토리를 전부 관리할 수 있다. 또한 다양한 배포가 진행되면서 배포가 된 것인지 확인을 쉽게할 수 있는 장점이 있다.

CI/CD 관점에서도 gitlab flow는 용이한데, pre-production에 배포되면 무조껀 jenkins나 배포 툴에서 배포되도록 하면 된다. 운영도 동일하다. 하지만, 이와 같이 단방형으로 잘 운영되려면 단순한 만큼 테스트가 철저해야한다는 점이 중요하다.

gitlab flow, 간단하지만 않다

gitlab flow는 git flow에 비해서 축약된 만큼 여러 규칙이 존재한다. GitLab의 CEO인 Sid Sijbrandij11가지 규칙에 대해서 이야기 한 바가 존재한다.

  1. main에 직접 커밋하지 않고 feature 브랜치를 사용한다.
  2. main 뿐 아니라 모든 커밋을 테스트 해야한다.
  3. 모든 커밋에 대해 모든 테스트를 실행한다. (5분이상 실행되면 병렬로 실행할 수 있다)
  4. main으로 합병 전, 코드 리뷰를 해라
  5. 배포를 진행할 때 branch, tag 기반으로 자동으로 진행되어야 한다.
  6. 태그는 CI가 아닌 사용자가 설정한다.
  7. 릴리즈는 태그를 기반으로 진행한다.
  8. push된 커밋은 리베이스를 하면 안된다.
  9. 모든 것은 main에서 시작해서 main으로 합병된다
  10. main의 버그를 수정하고 branch에 릴리즈한다.
  11. 커밋 메시지는 의도를 반영하여 잘 작성해라

가장 중요한 이야기는, 테스트하라는 내용이 많은 것처럼, 테스트는 무조껀 필수적인 사항이다. gitlab flow를 적용했다고 이야기하려면 CI/CD등의 자동화도 이루어져야 하지만, 테스트 코드부터 커버리지를 높혀야한다.

끝으로

서비스가 점점 커져감에따라, 지금은 B마트 뿐아니라 커머스 전체를 커버하게되면서 자동화된 브랜치 전략, 자동화된 배포 등이 필요해졌다. git flow는 자동화하기 어렵고, 브랜치를 관리하기도 규모가 커짐에따라 어려워진다.

특히나 최근의 웹 프론트는 monorepo로 회사들이 전환하고 있다. monorepo에서 수많은 개발자가 수많은 요구사항에 대한 반영을 진행하며 폭발적으로 커지면 git flow로는 커버할 수가 없는 지점에 도착하게 된다. 서비스 규모가 커지기 전에 CI/CD를 구축해야하며, 구축하기 전 테스트 커버리지를 틈틈히 올리고 git branch 전략을 심플하게 전환시켜야 한다.