[밍글] GitHub 브랜치 전략 도입하기

KIM TAEHYUN·2023년 6월 14일
0
post-thumbnail

Introduction

프로젝트 “밍글”을 진행하며 브랜치 전략의 필요성을 느끼고, 다양한 브랜치 전략을 논의해보았습니다.

Git Flow, GitHub Flow, Trunk-based development 총 3가지의 브랜치 전략을 비교한 결과, GitHub Flow가 우리의 서비스에 가장 적합하다고 생각했습니다.

이번 글에서는 각 브랜치 전략을 비교해보고, 현재 branch 전략과 GitHub Flow로 결정한 이유와 GitHub Flow와 현재 브랜치 전략을 같이 가져가게 된 고민 과정을 적어보겠습니다.

각 브랜치 전략에 대한 설명은 https://tech.mfort.co.kr/blog/2022-08-05-trunk-based-development/, https://ujuc.github.io/2015/12/16/git-flow-github-flow-gitlab-flow/를 참고하였습니다.


현재 브랜치 전략

정말 극 초기에는 팀원의 이름으로 브랜치를 만들어 개발을 하고 동일 브랜치를 계속 유지했습니다. (컨플릭 지옥)

CI/CD를 도입하며 현재 밍글 repo에는 main, dev, prod 3개의 주요 브랜치가 존재합니다.

main에서 브랜치를 만들어 기능을 개발하고, 개발이 완료되면 PR을 남깁니다. main에 머지된 브랜치는 삭제합니다.

main 브랜치는 항상 최신 상태로 유지되며, 따로 배포에 연결된 브랜치는 아닙니다.

새 기능이나 업데이트를 위해 프론트엔드에게 배포가 필요할 시 main 브랜치를 dev에 머지하고 dev에 push합니다. 이는 CI/CD를 트리거하고 dev서버에 배포를 자동으로 해줍니다.

프론트엔드/백엔드 둘 다 개발이 완료되어 실제 유저들에게 배포가 필요할 시, 프론트엔드까지 개발이 완료된 기능, 즉 dev 브랜치를 Prod에 머지하고, prod에 푸시합니다. 이는 CI/CD를 트리거하고 prod서버에 배포를 자동으로 해줍니다.

현재 브랜치 전략도 개발하는데 큰 문제는 없었지만, 더 나은 전략을 위해 기업에서 쓰거나 정형화된 브랜치 전략을 도입해보고자 알아보게 되었습니다.

1. Git Flow

처음 브랜치 전략을 검색해보았을때 접한 가장 유명한 전략이었습니다.

Git Flow의 핵심은 master와 develop 두 개의 주 브랜치입니다. master 브랜치에 있는 작업은 운영환경에 배포될 수 있는 것, develop 브랜치에 있는 작업은 다음 릴리즈를 위해 준비된 것입니다. 이 외에도 featurereleasehotfix 이 세 가지 보조 브랜치가 존재합니다.

  • feature: 새로운 기능을 develop 브랜치 위에서 개발하기 위해 사용
  • releasedevelop의 변경사항을 운영환경에 배포하기 위해 사용
  • hotfix: 운영환경에 시급하게 수정되어야 할 이슈가 있을 때, master를 빠르게 변경하기 위해 사용

Git Flow의 어려움

브랜치 관리 규약이 복잡하다

A successful Git branching model 에서는 featurereleasehotfix 세 가지의 보조 브랜치가 어디서 생성될 수 있으며, 어디로 머지(Merge)되어야 하는지, 그리고 어떤 이름을 가질 수 있는지 상세하게 설명합니다. 이러한 규약을 익히는 것도 어려운 점 중 하나이지만, 규약으로 인해 브랜치 관리의 본질적인 복잡성이 증가하는 것 또한 문제가 됩니다. 이러한 복잡성을 보여주는 예시를 간단히 짚어보자면 아래와 같습니다.

  • release 와 hotfix 브랜치를 머지하기 복잡합니다. 기본적으로 두 종류의 브랜치는 변경 사항이 덮어씌워지는 것을 방지하기 위해 master와 develop에 동시에 머지되어야 하며, 활성화된 release 브랜치가 있다면 이것도 신경써야 합니다.
  • 새로운 작업을 feature 브랜치와 hotfix 브랜치 중 어떤 것으로 해야 할지 판단하기 어렵습니다. 일반적으로는 계획된 기능을 feature, 계획되지 않은 픽스를 hotfix로 하지만 이런 분류가 애매해지는 순간이 분명 존재합니다.
  • hotfix에서 새로 발생한 변경 사항을 feature 브랜치에 적용하고 싶다면, 최소 3번의 머지가 필요합니다. (hotfix -> masterhotfix -> developdevelop -> feature)

브랜치가 오래 유지된다

Git Flow에서는 브랜치가 보통 오래 유지되고, 상대적으로 많은 변경사항을 한 번에 머지하는 것을 선호합니다. 이러한 선호는 여러가지 부작용을 같이 야기합니다.

먼저, 서로 다른 두 브랜치에서 독립적으로 작업을 하게 되면 동시에 수정한 부분이 생길 수 있는데, 이 때 두 브랜치를 머지하기 위해서는 Git 상에서 컨플릭트를 해결해야 합니다. 두 브랜치가 각각 독립적으로 오래 작업되었을수록, 컨플릭트는 보통 더 크고 해결하기 어려워집니다. 컨플릭트 해결 중 발생하는 휴먼 에러로 버그를 만들 확률 또한, 컨플릭트의 규모가 커질수록 같이 증가합니다.

우리 프로젝트에 적합한가

Git Flow 전략과 이의 어려움을 살펴보았을때, 밍글에는 적합하지 않다고 느꼈습니다.

  • 브랜치 관리 규약이 복잡하기에, 브랜치 관리 비용이 크다고 생각했습니다.
  • 빠르게 개발하고 배포를 해야하는 상황에 브랜치 규약을 지키고, 컨플릭을 해결하는것은 좋지 않다 느꼈습니다.
  • hotfix 도 하나의 feature라고 생각해, 굳이 hotfix 브랜치를 둘 필요성을 느끼지 못했습니다.

우리는 개발자가 많지 않고, 굳이 많은 브랜치를 두어 관리하기에는 너무 많은 비용이 들 것이라 생각했습니다.

2. Trunk-based Development

Git Flow 를 알아보던중 Git Flow의 어려움에 대응하는 전략인 Trunk-based Development를 알아보았습니다.

트렁크 기반 개발은 Git Flow의 대안으로서 주로 사용되는 브랜치 전략입니다.

트렁크 기반 개발의 실천법

트렁크 기반 개발에서는 main(또는 trunk)이라는 주 브랜치 하나만 운영합니다. 신규 피쳐는 main에 바로 커밋하거나, 며칠 내로 main에 머지할 피쳐 브랜치에서 작업합니다.

main 브랜치에 코드가 머지되었다면, 먼저 자동화된 CI 시스템이 main브랜치에 대해 테스트 / 통합 과정을 통과하는지 확인합니다. 문제가 없다면, main 브랜치의 코드가 그 즉시 운영 환경에 배포됩니다.

(이는 큰 틀에서 Github Flow와의 실천법과도 일치하기 때문에, Github Flow 또한 트렁크 기반 개발의 일종이라고 할 수 있겠습니다.)

이러한 간단한 방식은 Git Flow의 많은 문제들을 해결합니다.

  • 브랜치 관리에 드는 리소스가 대폭 절약됩니다. 개발자가 각자 자신이 맡은 피쳐 브랜치를 main 브랜치와 싱크를 맞추는 것만으로도 충분합니다.
  • 며칠 단위로 main에 머지하기 때문에, 머지 시 발생하는 변경이 작아집니다. 따라서 컨플릭트는 보통 작거나 없고, 코드 리뷰도 용이합니다.
  • 배포하기 위해서는 main에 머지하는 것만으로 충분합니다. 배포 프로세스가 간단해져서, 더욱 자주 배포할 수 있게 됩니다.

하지만 이러한 전략에도 어려움이 존재합니다.

큰 기능 문제

만들고자 하는 기능이 몇 주 내지는 몇 달의 개발 기간이 필요한 큰 기능일 수 있습니다. 이런 상황에서는 트렁크 기반 개발의 실천법대로 며칠마다 메인 브랜치에 머지하기 어려워집니다.

의존적인 기능 문제

만들고자 하는 기능 A가 다른 기능 B의 릴리즈에 의존적이라면, B 기능이 완료되기 전까지 A 기능도 배포하기 어렵습니다.

불안정한 기능 문제

기능을 별도의 스테이지 서버에서 검증하고 배포하는 것이 아니라 바로 실서버로 배포하기 때문에, 잘못된 기능이 메인 브랜치로 머지될 위험이 증가합니다.

그럼에도 불구하고,

트렁크 기반 개발에도 분명히 어려움이 존재하지만, 위의 어려움을 해결하는 나름의 방법 또한 존재합니다.

더 작은 단위의 배포

예를 들어서, 한 달이 걸릴 작업 하나를 일주일에 한번 배포하는 방식으로 쪼개서 배포할 수 있습니다. 물론 작업의 유형에 따라 그렇게 하기 상대적으로 더 어려운 경우도 존재하지만, 그렇게 했을 때 얻을 수 있는 이득 또한 분명합니다.

  • 여러 개의 작은 배포로 큰 배포 하나를 대체함으로서, 큰 기능 문제를 해결할 수 있습니다.
  • 의존적인 기능 문제 또한, 두 기능이 공통적으로 의존하는 부분을 추출해서 먼저 작업해 배포한다면, 두 기능을 실제로 구현하는 것은 그 후에 이어서 독립적으로 진행할 수 있게 됩니다.
  • 한 번의 배포에 추가되는 변경사항의 개수가 작을수록, 오류가 발생할 확률도 적어지고, 오류가 발생했을 때의 디버깅 또한 용이해집니다. 따라서 불안정한 기능 문제 의 영향을 최소화할 수 있습니다.

피쳐 토글

피쳐 토글(Feature Toggle)을 사용한다면, 작성한 코드를 운영 서버에 미리 배포해두고, 이 코드를 실제로 운영 환경에서 실행할지를 원격으로, 별도의 배포 없이 결정할 수 있습니다. 이는 코드의 배치 (deploy)와 기능의 릴리즈 (release)를 분리해서 다룰 수 있게 합니다.

  • 의존적인 기능 문제 가 발생했을 때, 먼저 내가 제어할 수 있는 부분만 작업해서 코드를 배치해 두고, 내가 의존해야 하는 부분이 완료된 후에 피처 토글을 통해 기능을 정식으로 릴리즈하는 방식으로 해결할 수 있습니다.
  • 정확히 구현된 것을 확신하기 어려운 작업 또한, 피쳐 토글로 보호된 상태로 코드만 먼저 배치해 둘 수 있습니다. 그 후 피쳐 토글을 통해 기능을 일시적으로 릴리즈하고, 기능에 문제가 없는 것을 확인한 후 영구적으로 릴리즈하는 방식을 사용할 수 있습니다. 이를 통해 불안정한 기능 문제 를 해결할 수 있습니다.

테스트 코드와 통합 단계에서의 테스트 자동화

테스트 코드의 중요성은 트렁크 기반 개발에서도 그 빛을 발합니다.

  • 새로운 기능을 위한 테스트 코드를 작성해 두면, 새로운 기능이 의도와 다르게 동작할 확률을 최소화할 수 있습니다. 또한 그 후에 만약 새로운 기능이 추가되어서 기존 기능에 영향을 주더라도, 테스트 코드를 통해서 이를 감지할 수 있습니다. 이는 자연스럽게 불안정한 기능 문제를 해결합니다.

위와 같은 장점들과 설명만 들었을 땐 간단한 방식이라 작은 서비스인 우리의 서비스에 적합하다고 생각했고, 팀원들 모두에게 최신 코드가 적용된다는 장점에 고려를 했지만 이 또한 우리의 프로젝트에 도입하기엔 적합하지 않다고 느꼈습니다.

  • 앞서 나열한 세가지 어려움을 해결하기 위한 방법들을 도입하기 어렵습니다. (피쳐 토글, 테스트 자동화)
  • 이에 테스트코드나 코드를 검증하기 위한 단계가 없어, 바로 main 브랜치에 배포를 해 운영환경에 배포하는것은 위험하다고 생각했습니다.
  • main 브랜치에 바로 커밋을 하는것은 분명한 장점이 있지만, main 브랜치에 대한 테스트/통합 과정을 통과하는지 확인하는 인프라가 갖춰져있지 않아 위험합니다. 기능이 동작하지 않더라도 conflict만 없으면 실제 운영환경에 배포될 수 있다는 것입니다.

3. GitHub Flow

마지막으로 알아본 방식은 GitHub Flow 입니다.

흐름이 단순한 만큼 룰도 단순하다. master 브런치에 대한 role만 정확하다면 나머지 브런치들에는 관여를 하지 않는다. 그리고 pull request 기능을 사용하도록 권장을 한다.

특징

  • release 브런치가 명확하지 않은 시스템에서 사용에 맞게 되어있다.
  • hotfix와 가장 작은 기능을 구분하지 않는다. 어차피 둘 다 개발자가 수정해야 되는 일중에 하나이다. 단지 우선순위가 어디가 높냐라는 단계이다.

그럼 어떻게 사용할 것인가?

1. master 브런치는 어떤 때든 배포가 가능하다.

master 브런치는 항상 최신의 상태이며, stable 상태로 Product에 배포되는 브런치이다. 그리고 이 브런치에 대해서는 엄격한 role를 주어 사용한다.

2. 새로운 일을 시작하기 위해 브런치를 master에서 딴다면 이름은 어떤 일을 하는지 명확하게 작성한다.

git flow 와는 다르게 feature 브런치나 develop 브런치가 존재하지 않는다. 그렇기에 새로운 기능을 추가하거나 버그를 해결하기 위한 브런치의 이름은 자세하게 어떤 일을 하고 있는지에 대해서 작성해주도록 하자. Github 페이지에서 보면 어떤 일들이 진행되고 있는지를 확인하기 쉽게 말이다.

3. 원격지 브런치로 수시로 push를 한다.

git flow 와 가장 상반되는 방식이다. 항상 원격지에 자신이 하고 있는 일들을 올려 다른 사람들도 확인할 수 있도록 해준다.

이 방법의 좋은 점은 하드웨어에 문제가 발생하여 작업하던 부분이 없어지더라도 원격지에 있는 소스를 받아서 작업을 할 수 있도록 해준다.

4. 피드백이나 도움이 필요할 때, 그리고 머징 준비가 완료되었을 때는 pull request를 생성한다.

pull request 는 코드 리뷰를 도와주는 시스템이다.

그렇기에 이것을 이용하여 자신의 코드를 공유하고, 리뷰를 받을 수 있도록 한다. 물론 머지가 준비 완료되어 master 브런치로 반영을 요구하여도 된다.

5. 기능에 대한 리뷰와 사인이 끝난 후 master로 머지한다.

곧장 product로 반영이될 기능이기에 이해관계가 연결된 사람들과 충분한 논의 이후 반영하도록 한다.

6. master로 머지되고 푸시되었을 때는 즉시 배포되어야 한다.

GitHub Flow의 핵심인듯한 master로 머지가 일어나면 hubot을 이용하여 자동으로 배포가 되도록 설정해놓는다.

앞서 언급했던 현재 우리의 방식은 GitHub Flow와 유사한 방식을 취하고 있습니다.

  1. master(main) 브랜치를 항상 최신 상태로 유지하며,
  2. 새로운 일을 시작하기 위해 main 브랜치에서 브랜치를 따 구현하고자 하는 기능/일로 브랜치 이름을 작성합니다.
  3. 원격 브랜치로 수시로 push 를 합니다.
  4. 해당 브랜치에서 구현할 내용이 모두 완료되었을 때, pull request를 생성합니다.
  5. 코드 리뷰 후 main으로 머지합니다. 하지만, 이는 곧장 product로 반영될 기능이 아닙니다. 이는 현재 브랜치 전략과 GitHub Flow의 가장 큰 차이입니다.
  6. GitHub flow의 master 브랜치와 같은 기능을 하는 우리의 prod 브랜치로 푸시되었을 때, GitHub Action을 이용해 자동으로 배포가 되도록 설정해놓았습니다.

장점

  • 브런치 전략이 단순하다.
  • 처음 git을 접하는 사람에게 정말 좋은 시스템이 된다.
  • Github 사이트에서 제공하는 기능을 모두 사용하여 작업을 진행한다.
  • 코드 리뷰를 자연스럽게 사용할 수 있다.
  • CI가 필수적이며, 배포는 자동으로 진행할 수 있다.
  • Github가 작업을 할 때 이렇게 작업하고 있다.

단점

  • CI와 배포 자동화가 되어있지 않은 시스템에서는 사람이 관련된 업무를 진행한다.
  • 많은 것들이 올라오기 시작하면… 그때부터는 헬이…

프로젝트 “밍글”이 우리에겐 처음 git을 접했기에 쉬운 전략이 필요했고, 단순한 브랜치 전략으로 쉽게 관리를 할 수 있다는 점이 매력적으로 다가왔습니다.

Pull request를 권장하고, 코드 리뷰를 자연스럽게 하도록 하는것도 장점으로 다가왔습니다.

단점 또한 우리는 GitHub Action을 통해 CI와 배포 자동화가 되어있기에 단점은 문제가 되지 않았습니다.

Git Flow vs Trunk-based Development vs GitHub Flow

위와 같이 세 브랜치 전략을 비교해 본 결과, 우리의 서비스와 현재 인프라 및 인력을 고려했을 때 GitHub flow가 가장 적합하다고 생각했습니다.

현재 브랜치 방식과 GitHub Flow

현재 우리의 브랜치가 main,dev,prod로 되어있는걸 GitHub Flow로 바꾸게 된다면,

dev 브랜치가 main이 될 것이고, prod 브랜치는 그대로 남아

main(dev) 와 prod 브랜치 두개로 유지할 것입니다.

하지만 GitHub Flow를 도입하기엔 하나의 문제점이 있었습니다.

우리는 dev서버에 배포를 하기 전, main에 충분히 변경 사항들을 모아놓고 한번에 dev로 머지 후 푸시를 합니다.

그리고 main에는 GitHub Action이 연결되어 있지 않습니다.

만약 main에 자잘한 변경사항이나 한 기능의 일부가 배포될 때 마다 GitHub Action이 돌아간다면, GitHub에서 제공하는 한달 무료인 300분을 넘기게 될것입니다.

그렇기에, 우리는 main에 작은 변경사항까지 최대한 빠르게 커밋하고 업데이트 한 후,

배포하기에 충분한 기능이 완성될 때 까지 main에 모앗다가 한번에 싶을 때 한번에 dev에 머지해 푸시를 하고 github action을 한번에 실행하고 dev 서버에 배포합니다.

전반적으로 GitHub Flow 방식을 채택하되, 비즈니스가 크지 않기에 금전 문제로 main을 하나 더 놔두는 방향을 선택했습니다.

마무리

이에 우리의 서비스와 인프라, 그리고 비용을 고려해 GitHub Flow 를 최대한 도입하고 우리에 서비스에 맞게 main 브랜치를 따로 유지하는 방안으로 유지하고 있습니다.

다양한 브랜치 전략을 알아보고 고민해본 좋은 경험이었습니다.


출처

0개의 댓글