Git 워크플로우 및 협업 전략

Git의 강력한 브랜칭 및 병합 기능을 효과적으로 활용하기 위해서는 팀이나 프로젝트의 특성에 맞는 워크플로우(Workflow), 즉 작업 절차와 규칙을 정립하는 것이 매우 중요합니다. 잘 정의된 워크플로우는 코드 충돌을 최소화하고, 변경 사항 추적을 용이하게 하며, 여러 개발자가 동시에 효율적으로 협업하고 안정적인 버전 관리를 가능하게 합니다. 팀의 규모, 프로젝트의 성격, 배포 주기 등에 따라 다양한 워크플로우를 선택하거나 조합하여 사용할 수 있습니다.

중앙 집중식 워크플로우 (Centralized Workflow)

중앙 집중식 워크플로우는 Subversion(SVN)과 같은 전통적인 중앙 집중식 버전 관리 시스템의 작업 방식과 유사하게 Git을 사용하는 방법입니다. 이 워크플로우에서는 origin이라는 단일 중앙 원격 저장소가 모든 프로젝트 코드의 중심 역할을 하며, 모든 팀원은 이 중앙 저장소를 기준으로 작업합니다. 개발자들은 주로 main (또는 master)이라는 하나의 공용 브랜치에 직접 자신의 로컬 변경사항을 커밋하고 푸시(push)합니다.

작업 흐름은 다음과 같습니다:

  1. 개발자는 먼저 중앙 원격 저장소를 자신의 로컬 컴퓨터로 복제(git clone)합니다.
  2. 로컬에서 코드를 수정하고, 변경사항을 로컬 커밋으로 만듭니다.
  3. 자신의 변경사항을 중앙 저장소에 푸시하기 전에, git pull (Workspace에 merge) 또는 git pull --rebase(Workspace에 rebase) 명령을 사용하여 다른 팀원이 중앙 저장소에 먼저 올린 최신 변경사항을 자신의 로컬 작업 내용과 통합합니다. 이 과정에서 충돌이 발생하면 해결해야 합니다.
  4. 로컬 작업 내용이 중앙 저장소의 최신 상태와 성공적으로 통합되면, git push 명령으로 자신의 변경사항을 중앙 저장소의 main 브랜치에 반영합니다.

이 방식은 Git을 처음 도입하거나 SVN 등에서 마이그레이션하는 팀에게는 이해하기 쉽고 간단하다는 장점이 있습니다. 하지만 여러 개발자가 동시에 main 브랜치에 직접 푸시할 경우 코드 충돌이 잦고, main 브랜치의 안정성을 유지하기 어려울 수 있다는 단점이 있습니다. 따라서 소규모 팀이나 매우 간단한 프로젝트에 제한적으로 사용되거나, 다른 워크플로우의 기본 형태로 활용될 수 있습니다.

기능 브랜치 워크플로우 (Feature Branch Workflow)

기능 브랜치 워크플로우는 현대적인 Git 기반 프로젝트에서 가장 널리 채택되는 방식 중 하나로, 모든 새로운 기능 개발이나 버그 수정 작업이 각각의 독립된 브랜치(기능 브랜치, Feature Branch) 에서 이루어지는 것을 핵심 원칙으로 합니다. 이렇게 하면 주 개발 라인인 main 브랜치는 항상 안정적인 상태를 유지할 수 있습니다.

작업 흐름은 다음과 같습니다:

  1. 새로운 기능(예: ‘사용자 프로필 편집 기능’) 개발이나 버그 수정(예: ‘로그인 오류 해결’)을 시작할 때, 기준 브랜치(보통 main 또는 develop)로부터 해당 작업의 목적을 명확히 나타내는 이름의 새 브랜치(예: feature/profile-editfix/login-error)를 생성합니다. (git switch -c feature/profile-edit main)
  2. 개발자는 새로 생성한 기능 브랜치로 이동하여 해당 기능을 구현하고, 관련 변경사항들을 로컬 커밋으로 만듭니다.
  3. 기능 브랜치에서의 작업 내용은 주기적으로 해당 브랜치 이름 그대로 중앙 원격 저장소에 푸시하여 백업하거나 다른 팀원과 공유할 수 있습니다. (git push origin feature/profile-edit)
  4. 기능 개발이 완료되면, 이 기능 브랜치를 원래의 기준 브랜치(예: main)로 통합하기 위해 풀 리퀘스트(Pull Request, PR) 또는 머지 리퀘스트(Merge Request, MR) 를 생성합니다. (GitHub, GitLab 등의 플랫폼에서 제공하는 기능)
  5. 풀 리퀘스트를 통해 팀 동료들은 제안된 코드 변경사항을 리뷰하고, 필요한 경우 피드백을 주고받거나 추가 수정을 요청합니다. 자동화된 테스트가 있다면 이 단계에서 실행될 수도 있습니다.
  6. 코드 리뷰와 테스트가 완료되고 모든 것이 승인되면, 기능 브랜치는 기준 브랜치(예: main)로 병합(merge)됩니다.
  7. 병합이 완료된 기능 브랜치는 더 이상 필요 없으므로 로컬 및 원격 저장소에서 삭제하여 정리합니다. (git branch -d feature/profile-editgit push origin --delete feature/profile-edit)
    이 워크플로우는 각 기능 개발을 격리하여 main 브랜치의 안정성을 크게 높이고, 풀 리퀘스트를 통한 코드 리뷰 문화를 장려하여 코드 품질을 향상시키며, 여러 기능을 병행하여 개발하기 매우 용이하다는 핵심적인 장점들을 가지고 있습니다.

Gitflow 워크플로우

Gitflow 워크플로우는 Vincent Driessen에 의해 제안된, 보다 정교하고 구조화된 브랜칭 모델입니다. 이 워크플로우는 정기적인 배포 주기를 가지는 규모 있는 프로젝트나, 여러 버전을 동시에 유지보수해야 하는 경우에 적합하며, 각기 다른 역할을 가진 여러 종류의 브랜치를 정의하여 사용합니다.
Gitflow의 핵심 브랜치들은 다음과 같습니다:

  • main (또는 master): 실제 배포된 안정 버전의 코드만을 반영합니다. 이 브랜치에는 오직 배포 가능한 상태의 코드만 존재해야 하며, 각 배포 시점에는 버전 태그(예: v1.0, v1.1.0)가 기록됩니다.
  • develop: 다음 릴리즈를 위한 개발 내용이 통합되는 주축 브랜치입니다. 모든 기능 개발은 이 develop 브랜치에서 시작하여 다시 develop 브랜치로 병합됩니다. 즉, develop 브랜치는 항상 다음 배포 버전의 최신 개발 상태를 나타냅니다.
    이 두 주요 브랜치 외에 다음과 같은 보조 브랜치(supporting branches)들이 특정 목적을 위해 사용됩니다:
  • feature/* (기능 브랜치): 새로운 기능 개발을 위해 develop 브랜치에서 분기합니다. 개발이 완료되면 다시 develop 브랜치로 병합됩니다. (예: feature/user-authentication)
  • release/* (릴리즈 브랜치): 다음 버전 배포 준비가 되었을 때 develop 브랜치에서 분기합니다. 이 브랜치에서는 배포 전 최종 테스트, 문서화 작업, 자잘한 버그 수정 등 배포와 직접 관련된 작업만 수행합니다. 준비가 완료되면 main 브랜치(여기에 버전 태그 생성)와 develop 브랜치 양쪽에 모두 병합됩니다. (예: release/1.2.0)
  • hotfix/* (핫픽스 브랜치): 이미 배포된 main 버전에서 발생한 긴급한 버그를 수정하기 위해 main 브랜치에서 직접 분기합니다. 수정이 완료되면 즉시 main 브랜치(여기에 새로운 패치 버전 태그 생성)와 develop 브랜치 양쪽에 모두 병합됩니다. (예: hotfix/1.0.1)
    Gitflow는 역할이 명확히 구분된 브랜치들을 통해 매우 체계적이고 안정적인 버전 관리 및 배포 관리가 가능하다는 장점이 있습니다. 하지만 브랜치 종류가 많고 절차가 다소 복잡하여, 팀원 전체가 워크플로우를 정확히 이해하고 따라야 하는 높은 수준의 규율이 요구됩니다. 따라서 소규모 프로젝트나 지속적 배포(Continuous Delivery/Deployment)를 지향하는 환경에는 다소 과하거나 비효율적일 수 있습니다.

GitHub Flow / GitLab Flow

GitHub Flow

GitHub Flow는 Gitflow보다 훨씬 단순하며, 지속적 배포(Continuous Delivery/Deployment) 환경에 최적화된 워크플로우입니다. 이 워크플로우의 핵심 철학은 main 브랜치가 항상 배포 가능한 최신 상태를 유지하는 것입니다.
작업 흐름은 다음과 같습니다:

  1. 모든 새로운 작업(기능 추가, 버그 수정, 실험 등)은 main 브랜치에서 직접 생성한 설명적인 이름의 기능 브랜치(예: add-user-gravatar-supportfix-login-page-redirect-bug)에서 시작합니다.
  2. 개발자는 해당 기능 브랜치에서 작업을 수행하고, 로컬 커밋을 만들며, 주기적으로 원격 저장소의 동일한 이름의 브랜치로 푸시합니다.
  3. 작업이 완료되거나 중간 피드백, 또는 병합 준비가 되면 원격 저장소에서 풀 리퀘스트(Pull Request)를 생성합니다.
  4. 풀 리퀘스트를 통해 팀 동료들과 코드 리뷰, 논의, 추가 수정 등의 협업 과정을 거칩니다. CI 서버가 있다면 이 단계에서 자동화된 테스트가 실행됩니다.
  5. (선택 사항) 리뷰 중인 기능 브랜치를 테스트 환경에 직접 배포하여 실제 동작을 검증해 볼 수도 있습니다.
  6. 모든 리뷰와 테스트가 성공적으로 완료되면, 해당 기능 브랜치는 main 브랜치로 병합됩니다.
  7. main 브랜치로 병합된 내용은 즉시 또는 정기적으로 자동화된 시스템을 통해 프로덕션 환경에 배포됩니다.
  8. 병합된 기능 브랜치는 삭제합니다.

GitHub Flow는 절차가 매우 간단하고 빨라서 신속한 개발과 잦은 배포에 유리합니다. 또한, 모든 작업이 풀 리퀘스트를 중심으로 이루어지므로 코드 리뷰와 팀 내 커뮤니케이션을 자연스럽게 장려합니다. 하지만 main 브랜치가 항상 배포 대상이므로, 철저한 테스트와 코드 리뷰가 더욱 중요해집니다.

GitLab Flow

GitLab Flow는 GitHub Flow의 단순함을 기반으로 하면서도, 실제 프로젝트 환경의 다양한 요구사항(예: 여러 배포 환경 관리, 특정 시점 릴리즈)에 대응할 수 있도록 유연성을 더한 워크플로우입니다.
GitLab Flow는 한 가지 고정된 방식이라기보다는, 프로젝트의 특성에 맞춰 GitHub Flow를 확장하는 여러 패턴을 제시합니다. 대표적인 예는 다음과 같습니다.

  • 환경 브랜치(Environment Branches) 활용: 개발(예: main 또는 develop), 스테이징(staging), 프로덕션(production)과 같이 각 배포 환경에 대응하는 장기 실행 브랜치를 운영할 수 있습니다. 새로운 기능은 main(또는 develop) 브랜치에 병합된 후, QA를 위해 staging 브랜치로, 최종적으로 안정성이 확인되면 production 브랜치로 병합(또는 체리픽)되어 배포될 수 있습니다. 이는 각 환경별로 코드의 안정성을 관리하고, 특정 환경에만 변경사항을 점진적으로 적용하는 데 유용합니다.
  • 릴리즈 브랜치(Release Branches) 활용: 명시적인 버전 릴리즈 관리가 필요하지만 Gitflow만큼 복잡한 절차를 원하지 않는 경우, main 브랜치에서 특정 시점에 릴리즈 브랜치(예: release/1.2.0stable-2.3)를 생성하여 해당 버전의 안정화 작업(버그 수정, 문서화 등)을 진행하고, 이 브랜치를 배포한 후 변경사항을 다시 main에 병합하는 방식을 사용할 수 있습니다.

GitLab Flow는 이처럼 GitHub Flow의 신속함과 Gitflow의 안정성 사이에서 프로젝트의 상황에 맞는 균형점을 찾을 수 있도록 도와줍니다. 핵심은 main 브랜치를 깨끗하게 유지하면서, 필요에 따라 추가적인 통합/배포 브랜치를 유연하게 운영하는 것입니다.

포크 (Fork) & 풀 리퀘스트 (Pull Request / Merge Request) 기반 협업

포크(Fork) 및 풀 리퀘스트(Pull Request, 또는 GitLab/Bitbucket 등에서는 머지 리퀘스트 Merge Request라고도 함) 기반의 협업 모델은 주로 오픈 소스 프로젝트에서 외부 기여자들이 프로젝트에 참여하는 방식이지만, 조직 내에서도 특정 핵심 저장소에 대한 직접적인 쓰기 권한을 제한하고 엄격한 코드 리뷰를 통해 변경사항을 통합하고자 할 때 널리 사용됩니다.
작업 흐름은 다음과 같습니다.

  1. 기여자는 먼저 변경사항을 제안하고자 하는 원본 프로젝트 저장소를 자신의 계정으로 포크(Fork) 합니다. 포크는 원본 저장소의 완전한 복사본을 자신의 원격 저장소 공간에 만드는 것입니다.
  2. 자신의 계정으로 포크한 저장소를 로컬 컴퓨터로 클론(git clone) 합니다.
  3. 로컬 저장소에서 새로운 기능이나 버그 수정을 위한 기능 브랜치를 생성하고 작업을 수행합니다.
  4. 작업이 완료되면 변경사항을 커밋하고, 이 기능 브랜치를 자신의 포크된 원격 저장소에 푸시합니다. (git push my-fork feature-branch)
  5. 자신의 포크된 저장소(예: GitHub 웹사이트)에서 원본 프로젝트 저장소를 대상으로 풀 리퀘스트(PR) 또는 머지 리퀘스트(MR)를 생성합니다. 이 PR/MR에는 내가 제안하는 변경사항에 대한 설명, 이유, 관련 이슈 번호 등을 상세히 작성합니다.
  6. 원본 프로젝트의 관리자(maintainer)들은 제출된 PR/MR을 검토하고, 코드에 대한 논의를 진행하며, 필요한 경우 추가적인 수정이나 테스트를 요청할 수 있습니다.
  7. 제안된 변경사항이 승인되면, 프로젝트 관리자는 해당 PR/MR을 원본 프로젝트의 주 브랜치(예: main 또는 develop)에 병합합니다.
  8. (선택 사항이지만 권장) 기여자는 주기적으로 원본 프로젝트 저장소의 최신 변경사항을 자신의 로컬 저장소 및 포크된 원격 저장소로 가져와 동기화하는 것이 좋습니다. 이를 위해 원본 저장소를 upstream이라는 이름의 원격으로 등록(git remote add upstream <원본_저장소_URL>)하고, git fetch upstreamgit merge upstream/main (또는 rebase) 등의 명령을 사용합니다.

이 포크 & 풀 리퀘스트 모델은 프로젝트에 대한 직접적인 쓰기 권한 없이도 누구나 안전하게 기여할 수 있는 통로를 제공하며, 모든 변경사항이 풀 리퀘스트를 통해 투명하게 논의되고 검토되므로 코드 품질을 높이고 체계적인 협업 문화를 장려하는 데 매우 효과적입니다.

워크플로우 선택 시 고려사항

어떤 Git 워크플로우가 모든 상황에 완벽하게 맞는 “만능 정답”은 없습니다. 성공적인 협업과 효율적인 버전 관리를 위해서는 다음과 같은 요소들을 종합적으로 고려하여 팀과 프로젝트에 가장 적합한 워크플로우를 선택하고, 팀원 모두가 이를 명확히 이해하고 일관되게 따르도록 하는 것이 중요합니다.

  • 팀의 규모와 Git 숙련도: 팀원이 적고 Git 경험이 많다면 비교적 단순한 워크플로우도 괜찮지만, 팀원이 많거나 Git 초보자가 있다면 좀 더 명확한 규칙과 절차가 있는 워크플로우가 도움이 될 수 있습니다.
  • 프로젝트의 성격 및 규모: 예를 들어, 매일 여러 번 배포되는 웹 서비스인지, 몇 달에 한 번씩 특정 버전으로 패키징되어 배포되는 소프트웨어인지에 따라 적합한 워크플로우가 달라질 수 있습니다.
  • 릴리즈 및 배포 주기: 배포가 매우 잦고 지속적이라면 GitHub Flow와 같이 간결한 모델이, 정기적이고 계획된 릴리즈를 한다면 Gitflow나 GitLab Flow의 릴리즈 브랜치 전략이 유용할 수 있습니다.
  • 코드 리뷰 및 테스트 자동화(CI/CD)와의 통합 필요성: 풀 리퀘스트 기반의 코드 리뷰가 필수적이거나 CI/CD 파이프라인과의 긴밀한 연동이 필요하다면 기능 브랜치 워크플로우, GitHub Flow, GitLab Flow 등이 좋은 선택이 될 수 있습니다.

마무리

자신들의 상황에 맞는 워크플로우를 선택하고 꾸준히 개선해나가는 과정 자체가 건강한 개발 문화를 만드는 데 기여할 것입니다.

댓글 달기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

위로 스크롤