CI/CD를 위한 브랜치 전략

탭과 스페이스 사용을 둘러싼 논의와 마찬가지로 브랜치 전략은 온라인과 오프라인 모두에서 뜨거운 논쟁을 유발하는 의견이 분분한 주제입니다.

소프트웨어 개발 과정의 여러 사항과 마찬가지로 많은 이들이 최선의 접근법을 두고 강력히 의견을 개진하지만, 올바른 접근법은 컨텍스트에 따라 다릅니다. 이를 염두에 두고 브랜치 전략의 정의 및 CI/CD와 브랜치 전략의 관계를 살펴보겠습니다.

간단히 설명하자면 브랜치 전략은 버전 관리 시스템에서 브랜치를 생성하고 병합하는 방식과 그 시기에 대한 팀의 합의를 의미합니다. 버전 관리 시스템 설정 및 브랜치 사용 방식은 CI/CD 파이프라인이 설정되는 방식에 영향을 미치므로 니즈에 부합하는 모델을 선택하는 것은 중요합니다.

DVCS의 브랜치

분산식 버전 관리 시스템(DVCS), 특히 브랜치 작업을 간소화한 Git의 인기가 높아짐에 따라 개발 팀에서 브랜치 전략을 고려하게 되었습니다.

분산식 시스템의 경우 저장소 사본이 여러 개 있으므로 소스 저장소도 여러 개입니다(물론 일반적으로 팀에서 중앙 또는 기본 사본 지정합니다). 팀은 저장소 사본의 변경 사항을 동시에 수정하고, 동일한 저장소의 다른 사본에 변경 사항을 푸시하여 작업을 공유할 수 있습니다.

Git을 통해 브랜치 생성 및 다른 브랜치의 커밋 병합 작업이 매우 간단해졌습니다. Git에서 브랜치는 특정한 이름으로 라벨이 지정된 커밋(또는 커밋 집합)입니다. 브랜치는 새로운 기능을 만들거나 해킹 실험을 수행하는 등 일련의 변경 사항을 제어하는 데 적합합니다.

또한 브랜치를 다른 저장소에 푸시하거나 마스터 브랜치 등의 다른 브랜치에 병합하여 코드베이스의 나머지 부분과 연결함으로써 변경 사항을 공유할 수도 있습니다. 변경 사항을 사용하지 않기로 한 경우엔 변경 내용을 삭제할 수도 있습니다. 브랜치 두 개를 병합할 때 Git은 각 브랜치에 대한 커밋 정렬을 처리합니다. 또한 다른 버전 관리 시스템의 병합과 관련한 복잡성도 감소합니다.

CI/CD를 위한 브랜치 활용

지속적 통합의 핵심은 개발 팀의 모든 구성원이 변경 사항을 자주 커밋하는 데 있습니다. 이를 통해 코딩 완료 후 여러 워크스트림 통합 작업에 몇 주나 몇 달을 소비하는 대신 모든 코드가 예상대로 작동하는지 주기적으로 테스트할 수 있습니다. 결과적으로 소프트웨어 업데이트의 릴리스 빈도를 늘려 지속적 전달 및 배포의 이점을 활용할 수 있습니다.

브랜치 활용 시 CI/CD와 상호 작용하게 되는 지점은 변경 사항 커밋 위치, 자동화된 빌드 및 테스트 실행 시점, 업데이트 릴리스 위치입니다. 가장 간단한 접근법은(적어도 개념적으로는) 브랜치를 아예 생성하지 않는 것입니다. 트렁크 기반 개발의 경우 모든 팀원이 중앙 저장소의 마스터 브랜치에 정기적으로 변경 사항을 커밋하며, 중앙 저장소는 릴리스 가능한 상태로 유지되며 프로덕션에 자주 배포됩니다.

트렁크 기반 개발은 특히 CI/CD 설정이 성숙되어 호스트 시스템에 지속적 배포를 실행하는 경우 효과적이지만 몇 가지 문제를 야기합니다. 브랜치 전략은 코드 변경 사항을 관리하는 대안적 방식을 제시하며, 방식별로 다른 장점과 단점이 있습니다. 다음은 CI/CD를 수행하는 개발 팀에서 가장 자주 사용하는 몇 가지 방식입니다.

피처 브랜치

이름에서 짐작할 수 있듯 피처 브랜치는 개별 기능을 코드베이스의 나머지 부분과 구분하여 별도로 유지하기 위해 생성됩니다. 마스터 브랜치와 진행 중인 작업을 별도로 유지하면, 릴리스 브랜치(하단 참조)가 아닌 마스터에서 직접 배포하는 경우 한층 쉽게 마스터를 배포 가능한 상태로 유지할 수 있어 미완성 기능이 프로덕션 단계로 배포될 가능성이 감소합니다. 브랜치를 중앙 저장소(지정된 경우) 또는 다른 저장소로 푸시하여 팀원들과 작업을 계속 공유할 수 있습니다.

피처 브랜치의 가장 큰 단점이자 트렁크 기반 개발을 선호하는 분들이 자주 비판하는 점은 기능이 ‘완성’될 때까지 변경 사항의 통합이 지연되므로 지속적 통합의 이점을 누릴 수 없다는 점입니다. 이로 인해 병합 충돌의 위험이 발생하고, 변경 사항이 반복적으로 커밋된 경우 수정하는 데 오랜 시간이 소요되는 복잡한 버그가 발생합니다.

피처 브랜치와 마스터(또는 릴리스를 준비하는 데 사용되는 브랜치)에서 자동화된 빌드 및 테스트를 실행하도록 CI 서버를 설정하면 이러한 문제를 줄일 수 있습니다. 이를 통해 빌드 중인 항목에 대한 즉각적 피드백을 받고, 변경 사항 병합 시 발생하는 문제의 위험도 줄일 수 있습니다.

병합 충돌을 최소화하는 한 가지 방법은 피처 브랜치의 수명을 최대 하루나 이틀 정도로 짧게 구성하는 것입니다. 또 다른 옵션은 브랜치를 정기적으로 마스터의 변경 사항에 리베이스 또는 병합하는 것입니다. 이를 통해 마스터에 커밋된 다른 모든 변경 사항을 피처 브랜치에 업데이트할 수 있습니다. 하지만 동시 작업이 가능한 대다수의 피처 브랜치가 모두 마스터 커밋을 지연시키는 경우 충돌 위험이 제거되지는 않습니다.

릴리스 브랜치

피처 브랜치는 진행 중인 작업을 관리하는 방법을 제공하는 한편 릴리스 브랜치는 릴리스 전에 변경 사항을 안정적으로 만드는 데 사용됩니다. 릴리스 브랜치는 업데이트가 준비될 때 즉시 전달되기보다 간격을 두고 전달되는 ‘지속적 전달’ 모델에 매우 적합하며, 이를 통해 프로덕션의 여러 버전을 간편하게 지원할 수 있습니다.

릴리스 브랜치 워크플로를 활용하면 특정 릴리스에서 계획된 변경 사항이 준비될 때 관련 변경 사항을 포함하는 릴리스 브랜치가 (마스터 또는 다른 개발 브랜치에서) 생성되며 그 이후에 병합되는 기능은 없습니다. 한편, 다른 릴리스에서 계획된 기능 개발은 마스터 브랜치나 다른 브랜치에 지속적으로 병합될 수 있습니다.

다음으로 릴리스 브랜치 빌드를 대상으로 일련의 자동화된 테스트를 수행하고, 릴리스 브랜치에서 모든 버그를 수정한 후 릴리스 준비가 완료될 때까지 추가 테스트 라운드를 수행합니다. 이때 버그 수정이 향후 제품 버전에도 포함되도록 마스터 브랜치에 다시 적용해야 합니다.

소프트웨어의 각 버전에 대한 지원이 필요한 기간에 릴리스 브랜치를 유지하면 훨씬 간편하게 이전 버전의 수정 사항을 배포할 수 있습니다.

업데이트가 필요한 경우 핫픽스 브랜치나 마스터에서 개발하고 정상적인 테스트를 거쳐 릴리스 브랜치에 업데이트를 적용할 수도 있습니다(예: 체리픽 방식으로).

이후 해당 브랜치의 CI/CD 파이프라인에 업데이트가 적용되며, 변경 사항을 배포하기 전 해당 버전에 문제가 없는지 확인됩니다. 또는 릴리스 브랜치에서 작업을 직접 수행하고 필요한 경우 마스터에 다시 적용하는 것도 가능합니다.

핫픽스 브랜치

핫픽스 브랜치의 작동 원리는 피처 브랜치와 유사하지만 모든 방식을 소개하기 위해 살펴볼 가치가 있습니다. 핫픽스 브랜치는 버그 수정 사항을 프로덕션에 최대한 빠르게 제공하는 것을 목표로 합니다.

배포 위치에 따라 마스터 또는 릴리스 브랜치에서 핫픽스 브랜치를 생성할 수 있습니다. 피처 브랜치와 마찬가지로 릴리스 또는 마스터 브랜치에 병합하기 전 핫픽스 브랜치에서 일부 CI/CD 요소를 실행하도록 선택할 수 있습니다. 따라서 릴리스에 앞서 자동화된 테스트를 추가로 수행할 수 있습니다.

마무리

이 글에서는 CI/CD 워크플로에서 브랜치를 활용하는 가장 보편적인 방법을 살펴보았으나, 훨씬 다양한 옵션이 있습니다.

구체적인 컨텍스트에 맞는 전략을 찾는 것이 가장 중요합니다. 지속적 개선을 수행하는 DevOps 사고방식을 바탕으로 다양한 옵션을 유연하게 활용하면, CI/CD 관행이 점차 성숙해짐에 따라 니즈에 부합하는 접근법을 지속적으로 조정할 수 있습니다.