iOS 프로젝트 개발에 적합한 git 브랜치 전략은 뭐냐고 물어보았다.

#iOS#git branch#strategy

사실 갑자기란건 없다. 어디서 무언가로 찔렸으니까지.

아무튼 누군가에게서 만들어져 내 손에 들어온 브랜치 전략에 대해 물어보았다.

claude는 3가지 문제가 있다고 지적했다.

순환 복잡성 / develop의 역할 불명확 / 병합 충돌 위험

그리고 자연스럽게 대안도 이야기해주었다.

사실, 딱 봐도 별로인데(대외비라 공개 불가) 말단이 말해봤자 씨알도 안먹혀서,

이 별로인 전략을 사용해야하기에 claude skills로 만들어두려다가,

이걸 또 얘한테 어떻게 하나 고민하다 현타가 와서 나중에 나는 저렇게 하지 말아이야지.. 생각하며 클로드랑 티키타카한걸 남겨놓는 글.


iOS 배포 자동화를 포함한 브랜치 전략 3가지 옵션

  자동화 도구 비교 먼저 정리

  | 도구         | 장점                                                                   | 단점                                              | 적합한 상황                        |

  |----------|--------------------------------------------------------------------|---------------------------------------|------------------------------------|

  | Fastlane     | - 가장 강력하고 유연- 모든 CI/CD와 통합 가능- 커스터마이징 자유도 높음 | - 초기 설정 복잡- Ruby 지식 필요                  | 복잡한 배포 플로우다양한 환경 관리 |

  | GitLab CI/CD | - GitLab과 네이티브 통합- .gitlab-ci.yml 기반- 무료 러너 제공          | - iOS 빌드 시 macOS 러너 필요                     | GitLab 사용 팀내부 서버 환경       |

  | Xcode Cloud  | - Apple 공식, 설정 간단- Xcode와 완벽 통합- 인증서 자동 관리         | - 비용 발생- 커스터마이징 제한- Apple 생태계 종속 | 간단한 배포Apple 생태계 선호 |

(git 서버를 사내 서버에서 돌리고 있어서 전략이 조금 다를 수 있다.)

  ---

  옵션 1: 환경 기반 전략 (가장 추천) 🌟

  브랜치 구조

  main (개발 소스)

    ├── feature/* (개발 중)

    ├── staging (TestFlight 배포용)

    └── production (App Store 배포용)

  배포 플로우 + 자동화

  main → feature → main PR 머지

    ↓ (자동)

  Firebase App Distribution (알파)

    ↓ (수동 트리거)

  main → staging 머지

    ↓ (자동)

  TestFlight 배포 (베타)

    ↓ (수동 트리거)

  staging → production 머지

    ↓ (자동)

  App Store 배포 (프로덕션)

  자동화 세부 구성

  1. Feature → Main PR 머지 시

  트리거: main 브랜치에 푸시 또는 PR 머지

  자동화: GitLab CI/CD

  # .gitlab-ci.yml

  test_and_build_dev:

    stage: test

    only:

      - main

    script:

      - fastlane test

      - fastlane build_dev

      - fastlane firebase_distribution

    tags:

      - macos

  Fastlane 작업:

  # fastlane/Fastfile

  lane :firebase_distribution do

    build_app(

      scheme: "town-dev",

      configuration: "Debug",

      export_method: "development"

    )

    firebase_app_distribution(

      app: ENV["FIREBASE_APP_ID_DEV"],

      groups: "internal-developers",

      release_notes: last_git_commit[:message]

    )

  end

  결과:

  - ✅ main에 머지되면 자동으로 Firebase App Distribution 배포

  - ✅ 내부 개발자들이 즉시 테스트 가능

  - ✅ Bundle ID: kr.co.proptier.town.dev

  2. Main → Staging 머지 시 (TestFlight)

  트리거: staging 브랜치에 머지 (수동 트리거 권장)

  자동화: GitLab CI/CD

  build_testflight:

    stage: deploy

    only:

      - staging

    when: manual  # 수동 승인 필요

    script:

      - fastlane beta

    tags:

      - macos

  Fastlane 작업:

  lane :beta do

    # 인증서 및 프로비저닝 프로파일 동기화

    match(type: "appstore", readonly: true)

    # 빌드 넘버 자동 증가

    increment_build_number(xcodeproj: "town.xcodeproj")

    # 빌드

    build_app(

      scheme: "town",

      configuration: "Release",

      export_method: "app-store"

    )

    # TestFlight 업로드

    upload_to_testflight(

      skip_waiting_for_build_processing: true,

      distribute_external: false,  # 내부 테스터만

      groups: ["QA", "Stakeholders"]

    )

    # Slack 알림

    slack(message: "TestFlight 배포 완료!")

  end

  결과:

  - ✅ staging 머지 → 수동 승인 → TestFlight 자동 업로드

  - ✅ QA 팀 자동 알림

  - ✅ Bundle ID: kr.co.proptier.town

  3. Staging → Production 머지 시 (App Store)

  트리거: production 브랜치에 머지

  자동화: GitLab CI/CD

  deploy_appstore:

    stage: production

    only:

      - production

    when: manual  # 수동 승인 필수

    script:

      - fastlane release

    tags:

      - macos

  Fastlane 작업:

  lane :release do

    match(type: "appstore", readonly: true)

    # 버전 태깅

    add_git_tag(tag: "v#{get_version_number}")

    push_git_tags

    # App Store 제출

    upload_to_app_store(

      submit_for_review: false,  # 수동 제출

      automatic_release: false,

      skip_screenshots: true,

      precheck_include_in_app_purchases: false

    )

    slack(message: "App Store 배포 대기 중!")

  end

  결과:

  - ✅ App Store Connect에 자동 업로드

  - ✅ 수동으로 최종 제출

  - ✅ Git 태그 자동 생성

  전체 워크플로우 요약

  | 단계     | 브랜치               | 배포 타겟                 | 자동화               | 트리거            |

  |----------|----------------------|---------------------------|----------------------|-------------------|

  | 개발     | feature → main       | Firebase App Distribution | GitLab CI + Fastlane | PR 머지 (자동)    |

  | 베타     | main → staging       | TestFlight                | GitLab CI + Fastlane | 머지 후 수동 승인 |

  | 프로덕션 | staging → production | App Store                 | GitLab CI + Fastlane | 머지 후 수동 승인 |

  장점

  ✅ 환경별 브랜치로 명확한 분리

  ✅ main이 항상 최신 개발 코드

  ✅ 선택적 배포 가능 (staging에서 체리픽)

  ✅ 자동화로 휴먼 에러 최소화

  ---

  옵션 2: 단순화된 Release 전략

  브랜치 구조

  main (소스)

    ├── feature/*

    └── release/YYYYMMDD (배포 준비)

  배포 플로우 + 자동화

  main → feature → main PR 머지

    ↓ (자동)

  Firebase App Distribution

  배포 시점:

  main → release/YYYYMMDD 생성

    ↓ (자동)

  TestFlight 배포

    ↓ (수동)

  release → main 머지

    ↓ (자동)

  App Store 배포

  자동화 구성

  1. Main PR 머지 시

  # .gitlab-ci.yml

  deploy_dev:

    stage: deploy

    only:

      - main

    script:

      - fastlane firebase_distribution

  2. Release 브랜치 생성 시

  deploy_testflight:

    stage: deploy

    only:

      - /^release\/.*$/  # release/로 시작하는 브랜치

    script:

      - fastlane beta

  Fastlane:

  lane :beta do

    match(type: "appstore")

    increment_build_number

    build_app(scheme: "town", configuration: "Release")

    upload_to_testflight(groups: ["QA", "Stakeholders"])

  end

  3. Release → Main 머지 후 태그 푸시 시

  deploy_production:

    stage: production

    only:

      - tags  # v1.0.0 같은 태그 푸시 시

    when: manual

    script:

      - fastlane release

  전체 워크플로우 요약

  | 단계     | 액션                  | 배포 타겟                 | 자동화    |

  |----------|-----------------------|---------------------------|-----------|

  | 개발     | main PR 머지          | Firebase App Distribution | 자동      |

  | 베타     | release/YYYYMMDD 생성 | TestFlight                | 자동      |

  | 프로덕션 | Git 태그 푸시         | App Store                 | 수동 승인 |

  장점

  ✅ 브랜치 구조 단순

  ✅ release 브랜치로 선택적 배포

  ✅ 날짜 기반 명명으로 이력 추적 용이

  단점

  ⚠️ 환경별 브랜치 없어 롤백 어려움

  ---

  옵션 3: Git Flow + iOS 배포

  브랜치 구조

  develop (통합 개발)

    ├── feature/*

    ├── release/*

    └── main (프로덕션)

  배포 플로우 + 자동화

  develop → feature → develop PR 머지

    ↓ (자동)

  Firebase App Distribution

  배포 시점:

  develop → release/YYYYMMDD

    ↓ (자동)

  TestFlight 배포

    ↓ (수동)

  release → main 머지

    ↓ (자동)

  App Store 배포

    ↓ (자동)

  release → develop 백머지

  자동화 구성

  1. Develop PR 머지 시

  deploy_alpha:

    stage: deploy

    only:

      - develop

    script:

      - fastlane firebase_distribution

  2. Release 브랜치 생성 시

  deploy_beta:

    stage: deploy

    only:

      - /^release\/.*$/

    script:

      - fastlane beta

  3. Main 머지 시

  deploy_production:

    stage: production

    only:

      - main

    when: manual

    script:

      - fastlane release

      - git checkout develop

      - git merge main

      - git push origin develop  # 백머지

  전체 워크플로우 요약

  | 단계     | 브랜치            | 배포 타겟                 | 자동화    |

  |----------|-------------------|---------------------------|-----------|

  | 개발     | feature → develop | Firebase App Distribution | 자동      |

  | 베타     | develop → release | TestFlight                | 자동      |

  | 프로덕션 | release → main    | App Store                 | 수동 승인 |

  | 백머지   | main → develop    | -                         | 자동      |

  장점

  ✅ 업계 표준 검증된 패턴

  ✅ develop에서 통합 테스트 가능

  ✅ 백머지로 핫픽스 동기화

  단점

  ⚠️ feature가 develop에서 분기 (사용자 현재 규칙과 충돌)

  ⚠️ 브랜치 관리 복잡

  ---

  최종 추천: 옵션 1 + GitLab CI/CD + Fastlane

  이유

  1. 환경 분리 명확: staging/production 브랜치로 롤백 쉬움

  2. GitLab 통합: 내부 GitLab 서버와 완벽 연동

  3. 자동화 강력: Fastlane으로 복잡한 배포 플로우 관리

  4. 현재 규칙 유지: feature를 main에서 분기 (사용자 요구사항)

  구현 우선순위

  1. ✅ 1단계: Fastlane 설정 (인증서 관리, 빌드, 배포)

  2. ✅ 2단계: GitLab CI/CD 파이프라인 구축

  3. ✅ 3단계: Firebase App Distribution 연동

  4. ✅ 4단계: TestFlight/App Store 자동 업로드

  ---


글 쓰면서 다시 살펴보니 얘가 만들어준 것도 또 좀 그렇네?!

하하하.

Discussion

Loading comments...