성장, 그리고 노력

부족하더라도 어제보다 더 잘해지자. 노력은 절대 배신하지 않는다.

도구, 기술, 이론/이론

트렁크 기반 개발(Trunk-Based Development)

제이콥(JACOB) 2020. 2. 11. 02:46

아래 그림은 내가 평소에 사용하던 github branching stategy이다.

 이슈마다 feature 브랜치를 생성 후 마스터 브랜치에 merge 하는 방식이다. 경우에 따라서 여기에 develop 브런치나 release 브런치를 추가로 만들어 관리하는 경우가 있다. 이 방식은 git branching stategy보다 적용하기 쉬워 평소에도 협업할 일이 있다면 이 방식으로 많이 사용하는 편이다.

 

  다른 것들을 검토하기 전에 먼저 내가 자주 사용하던 GitHub Flow를 먼저 살펴보자. 

 

  1. 이슈 관리툴(Jira 등)에서 백로그(BackLog)를 확인하고 작업할 내용을 선택한다.
  2. 개발자는 master에서 새 브랜치(feature)를 만들고 작업을 한다.
  3. 원하는 지점에서 commit하고 push 하며 이를 반복한다.
  4. 코드는 푸쉬될 때마다 빌드 툴(Circleci, Jenkins 등)이 작동되며 내가 작성한 코드를 빌드한다.
  5. 개발이 완료되었다면 개발자는 PR을 생성한다.
  6. PR이 생성되면 코드 오너 혹은 리뷰어들은 그 코드를 검토하며 리뷰해 준다.
  7. 리뷰가 되면, 리뷰에 대해 이야기 나누며 수정 사항을 다시 반영하여 PR을 다시 보내고 이를 계속 반복한다.
  8. 더 이상 리뷰어가 의견이 없다면 PR을 승인하게 되고 master로 merge 된다.
  9. CI 툴에 의해 마스터에서 빌드가 진행된다.

 위 과정이 발생되면서 개발이 완료되고 PR이 승인되기 위해 대부분 긴 시간이 걸린다(길게는 일주일도..!). 이렇게 되면 어떤 문제가 발생할 수 있을까? 뭐가 문제길래 다른 개발론이 필요하다고 말하고 싶은 걸까? 예를 들어 위 작업이 동시에 4명의 개발자에 의해 일어나고 있다고 하자. master 브랜치를 기준으로 feature a, feature b, feature c, feature d 브랜치 총 4개가 생성되었을 것이다. 물론 장점은 a개발자가 빌드에 실패하더라도 b, c, d의 개발자가 개발하는 데에는 아무런 영향을 미치지 않는다는 것이다. 반면에 단점은 4개의 브랜치는 각각 다른 시점에 병합되며, 4개가 모두 프로덕션 환경에 릴리즈 되었을 때 실제로 잘 작동하는지 예측할 수가 없다.

 

 물론 4명의 개발자가 처리하고 반영될 이슈와 기능에 대해서 어느 정도 예측을 할 수 있다고 해보자. 근데 사업이 너무 잘되고 우리의 애플리케이션이 커짐에 따라 개발팀이 더 커져 개발자가 50명이 되었다고 하자. 50명의 개발자가 각기 다른 브랜치에서 작업 후, 머지한다면 그때도 예측할 수 있을까? 이슈 하나를 해결하기 위해 50번째로 작업이 완료되는 개발자는 pull과 수많은 merge conflicts를 해결해야 될 것이며 이 이슈가 프로덕션 환경에서 잘 작동하는지는 테스트하기 더 어려울 것이다.

 또 한 가지 문제점은 피드백을 늦게 받는다는 것이다. 어떤 이슈에 대해 담당 개발자가 개발을 끝까지 하기 전에는 코드 오너이건 PL이건 신경 쓰지 않는다. 개발을 끝내고 PR을 요청했을 때 비로소 다른 사람들은 그 코드와 비즈니스 로직에 대해 검증해 보며 피드백을 준다. 피드백이 크리티컬 할수록 PR이 승인되는 시간은 길어질 가능성이 커지며, 핫픽스나 시간상 여유가 없을 경우에는 이조차 생략하여 이질적인 코드 스타일이 프로젝트에 종종 녹아들기도 한다.

 

 우리는 개발 서버를 새로 만들고 최대한 동일한 환경에서 미리 릴리즈를 해보며 테스트해보는 방식으로 이를 극복해 나가곤 한다. 그래도 규모가 큰 팀에서는 역시나 한계가 존재하는 법이다. 아니면 QA팀의 인원을 늘려서 더 세밀한 테스트를 할 수도 있다. 하지만 비용의 증가와 더불어 하나의 기능을 추가하기 위해 장기간이 소요될 가능성이 더 커진다. 우리 개발팀은 더 느려지고 둔한 조직이 될 수도 있는 것이다. 그래서 나온지는 20년도 더 된(절대 새로운 개발론이 아니다!), Google과 FaceBook에서 사용해서 유명해진 브랜칭 모델 중에 하나인 트렁크 기반 개발에 대해 알아보려 한다.

 

Trunk-Based Development

 Trunk-Based Development(트렁크 기반 개발, TBD)란 모든 개발자가 트렁크(trunk or master)라는 단일 브랜치에서 직접 모든 작업을 하는 것이다. 물론 이 말만 본다면, "그냥 마스터 브랜치에서 모든 작업을 하는 거 아냐? 트렁크라는 이름만 붙이고?"라고 반문할 수 있다. 물론 틀린 말은 아니다. 하지만 그냥 작업하는 것이 아닌 몇 가지 절대적이고 안전한 규칙들을 지켜져야 된다.

소규모의 트렁크 기반 개발(https://trunkbaseddevelopment.com/),  위 그림은 소규모 트렁크 기반 개발을 나타내고 있다. 물론 우리 조직이 소규모인지 아님 대규모인지는 이 개발론을 적용하려는 실무자들이 정하면 된다. 

 

1. Pair Programming or Mob Programming

 페어 프로그래밍(Pair Programming)이란, 두 명의 개발자가 한 곳에서 한 명은 코드를 작성하고 한 명은 각 코드행을 검토하는 방식을 말하며, 이 둘 사이의 역할은 자주 전환되는 프로그래밍을 말한다. 이 방식을 따르면 작성 중인 코드가 실시간으로 검토되기 때문에 나중에 PR 요청이 필요하지 않다. 또한 작업은 더 빨리 완료되며, 더 높은 품질, 코딩 스타일 통일, 지식 공유, 문제 해결에 대한 해결책을 같이 찾음 등의 이점이 있다. 

 몹 프로그래밍(Mob Programming)도 페어 프로그래밍과 유사한 방법이며, 전체 팀이 동일한 공간에서 동일한 컴퓨터에서 동일한 작업을 수행하는 개발 방식이다.

 

2. 신뢰할 수 있는 빌드가 필요

코드 베이스가 항상 릴리즈가 가능한 상태임을 확신할 수 있어야 한다. 그러기 위해서는 충분한 자동 테스트가 실행되어야 하며, 그렇기 때문에 효과적이고 우수한 테스트 전략 필요하다.

 

3. 'Branch by Abstraction' 또는 'Feature Flags'를 사용하여 작업이 완료되지 않는 부분 숨기기

 마스터에는 완료되지 않는 코드가 있을 수 있다. 어떤 코드이건 빠르게 작업을 할 수 있는 부분은 아니기 때문이다. 제목에 쓰여있는 두 개의 방법 모두 매우 멋진 방식이다. 간단한 것은 Branch by Abstraction을 사용하고 복잡한 태스크의 경우 Feature Flags를 사용하자. 사용법은 링크를 참고 바란다.

 

4. 소규모 배치로 개발

 팀이 프로덕션 릴리스를 더 자주 할 수 있게 작업을 분해하며, 작업이 며칠이 아닌 몇 시간 안에 완료할 수 있는 소규모 변경사항으로 쪼갠다.

 

5. 빠른 빌드 필요

 빌드 및 테스트 프로세스는 몇 분 내에 실행되어야 한다. 만약 그렇게 할 수 없다면 시스템 아키텍처에 개선이 필요하다는 것을 의미한다.

트렁크 기반 개발의 좋은 점

1. 빠른 피드백과 좋은 품질의 피드백

 코드가 완성되기 이전인 코드 작성할 때 바로 피드백을 줄 수 있기 때문에 피드백이 빠르고, 받은 내용을 토대로 변경하기 쉽다. 또한 코드 작성 시 페어 프로그래밍을 하며 문제에 대해 직접 토론하며 명확하고 명쾌한 설명으로 더 좋은 피드백을 줄 수 있으며, 바로 IDE 등으로 코드 리뷰를 보면서 하다 보니, 더 나은 리뷰가 가능하다(타입 추론 등의 기능을 모두 활용하여 같이 검토할 수 있다).

 

2. 팀 코딩 스타일

 페어 프로그래밍을 하면 혼자 코드를 작성할 때보다 팀 스타일로 코드를 작성할 수 있으며, 개인의 코드에 대한 방어적인 대응으로 발생할 수 있는 작은 갈등이나 토론이 발생할 확률이 줄어든다.

 

3. 우리(팀)의 코드

 개인이 코드를 작성한 경우 간혹 누군가가 a코드에 대해 질문하면, "그건 A가 작성한 거야, A에게 물어봐"라고 내 코드 이외에 부분에 대해 배타적인 반응을 하는 대신에, 팀이 작성한 코드이고 우리가 작성자라는 더 많이 줄 수 있고 보다 높은 수준의 협업이 가능해진다.

 

4. 진정한 의미의 CI (Continuous Integration)

 위에서 설명한 기존의 개발론에서는 개발자가 코드를 완성한 경우에만 PR이 발생하지만, master로 직접 푸시하면 코드가 즉시 통합되고 진정으로 CI의 의미에 맞게 된다.

 

5. 대규모 리팩터링을 보다 쉽게 처리

 병합 충돌 자체가 일어날 수 없기 때문에 기존 방법에 비해 훨씬 더 대규모 리펙토링을 하기 쉽다. 

 

 트렁크 기반 개발은 특별한 것이 아니며, 사실 특별한 이점이 있는 건 없다. 우리가 얻는 것은 몇 가지 절대적이고 안전한 규칙을 지키면서 갖추어야 할 관행에서 나오는 이점(팀이 마스터 작업 시 코드가 깨지지 않는 방법, 좋은 테스트 작성, 효과적인 공동 작업 방법 등)인 것이다.

왜 master에 직접 push를 왜 지양해야 하지?

 이 질문에 대한 대답은 이미 들어왔던 내용이라 대부분의 머릿속에 있을 것이다. "master는 좋은 상태를 유지해야 하며  항상 릴리즈 가능한 상태이어야 한다, 다른 사람의 코드를 리뷰하여 좋은 퀄리티로 우리의 코드 베이스를 유지해야 한다, 독립된 branch에서 작업하는 것이 더 효율적이다" 등이다.

 

 물론 틀린 말은 아니지만, 잘 생각해 보면, 위 논리는 모두 팀이 아닌 개인의 성과를 최적화하고 있다. 이는 워터폴(waterfall) 방식에서 자주 사용되는 접근 방법이다. 이는 자원 효율성(Resource Efficiency)을 통해 각 개인에 맞게 최적화하는 것이며, 이로 인해 지연 비용(cost of delay)이 발생한다. 

https://www.jrothman.com/wp-content/uploads/2015/09/ResourceEfficiency.png

 우리가 지향해야 되는 것은 팀의 성과이다. 개인의 생산성보다는 팀의 생산성을 최적화시켜야 한다. feature 브랜치를 사용하는 방법은 개별 능력을 최적화하지만, 트렁크 기반 개발은 팀 능력을 최적화할 수 있다.

https://www.jrothman.com/wp-content/uploads/2015/09/FlowEfficiency.png

 그렇다고 브랜치를 완전히 사용 안 하는 것은 아니다! 

 다른 방식으로 branch가 사용된다는 말이 가장 맞는 말 같다. 예를 들어 수명이 짧은 브런치라면 사용해도 된다. 이런 경우가 아니라면 브랜치는 더 이상 사용하지 않는다. 대신 모든 개발자는 항상 프로덕션 환경에서 실행되는 최신 버전의 코드를 가지게 된다.

참고

https://www.jrothman.com/mpd/agile/2015/09/resource-efficiency-vs-flow-efficiency-part-1-seeing-your-system/

https://trunkbaseddevelopment.com/

https://medium.com/factualopinions/git-to-know-t0his-before-you-do-trunk-based-development-tbd-476bc8a7c22f

https://cloud.google.com/solutions/devops/devops-tech-trunk-based-development

https://kean.github.io/post/trunk-based-development

반응형