Skip to content

Protected Paths

JEONG edited this page Aug 31, 2026 · 1 revision

Protected Paths

승인 없이는 바꿀 수 없는 경로 — Repository ruleset + .github/CODEOWNERS. 저장소: UMC-PRODUCT/Big-Dipper-iOS

왜 있나

기수가 바뀌어도 기반 계층은 임의로 바뀌면 안 된다. UMCFoundation 하나를 잘못 건드리면 그걸 import 하는 449개 파일이 같이 흔들린다. 반면 Features/* 는 하나가 깨져도 그 피처만 죽는다.

그래서 blast radius 가 큰 경로에만 소유자 승인을 요구한다. 나머지는 지금까지처럼 자유롭게 머지된다.

어떻게 동작하나

두 개가 모두 있어야 발동한다. 하나만 있으면 아무것도 막지 않는다.

정하는 것 위치
Repository ruleset Protect branch 어떤 브랜치를, 어떤 규칙으로 GitHub 설정 (id 11514288)
.github/CODEOWNERS 어떤 경로에, 누구 승인이 저장소 파일 (base 브랜치 기준)

⚠️ CODEOWNERS 는 base 브랜치(develop)에 있는 버전이 적용된다. 내 브랜치에서 CODEOWNERS 를 고쳐도 그 PR 에는 적용되지 않는다.

1. Repository ruleset — Protect branch

대상 브랜치: main(default) · develop · release · testFlight

규칙 하는 일
deletion 브랜치 삭제 금지
non_fast_forward force push 금지
required_linear_history 머지 커밋 금지 (squash / rebase 만)
pull_request PR 없이 직접 푸시 금지. required_approving_review_count: 0 — 일반 PR 은 지금처럼 승인 없이 머지된다
require_code_owner_review CODEOWNERS 경로가 바뀐 PR 만 소유자 승인 필수
require_extra_approval_for_unattributed_changes GitHub 계정에 연결되지 않은 이메일로 만든 커밋이 섞이면 추가 승인 요구 (GitHub 기본값)

bypass actors — 아래 주체는 위 규칙을 우회한다.

주체 모드
Repository role Admin always
Team @UMC-PRODUCT/1st-ios always

신규 기수 팀원을 1st-ios 팀에 넣으면 이 문서의 모든 보호가 무력화된다. 새 팀은 자기 기수 팀(2nd-ios 등)에만 추가할 것.

2. .github/CODEOWNERS — 보호 경로 17개

소유자는 모두 @JEONG-J.

분류 경로
게이트 /.github/ 여길 열어두면 CI·CODEOWNERS 를 PR 하나로 지울 수 있다
빌드 그래프 UMCApp/Tuist/ Project+Feature.swift 가 전 피처의 의존성을 정의한다
UMCApp/Project.swift · Workspace.swift · Tuist.swift 워크스페이스·타겟 구성
UMCApp/mise.toml Tuist 버전 고정 (4.155.0)
UMCApp/Makefile 빌드 진입점
서명 · 키 UMCApp/Secrets/ xcconfig · 키
UMCApp/UMCApp.entitlements App Group · capability
동결 AppProduct/ v2.2.0 릴리즈 상태로 동결 (절대 규칙 #9)
코어 모듈 UMCApp/Core/Foundation/ UMCFoundation
UMCApp/Core/DesignSystem/
UMCApp/Core/UIComponents/
UMCApp/Core/Domain/
UMCApp/Core/DI/
UMCApp/Core/Network/

CODEOWNERS 는 마지막에 일치한 줄이 이긴다. 항목을 추가할 땐 순서를 확인할 것.

선정 기준 — fan-in

코어 모듈은 취향이 아니라 실제 import 하는 파일 수로 골랐다.

모듈 import 파일 수 보호
Core/Foundation (UMCFoundation) 449
Core/DesignSystem 222
Core/UIComponents 134
Core/Domain 113
Core/DI 103
Core/Network 78
Core/NearbyExchange 23
Core/WatchDesignSystem 21
Core/WatchConnectivity 18
Core/Routing 15
Core/Photo 1
Core/WidgetShared 0

측정 명령:

cd UMCApp
grep -rl --include='*.swift' '^import UMCFoundation' Core Features UMCApp UMCWatchApp UMCAppWidget UMCWatchComplication | wc -l

의도적으로 제외한 것

  • Features/* 전부 — 팀이 실제로 일하는 곳이다. 여기까지 막으면 승인 대기로 개발이 멈춘다. 승인 게이트의 값어치보다 리뷰 대기 비용이 크다.
  • fan-in 낮은 Core 모듈 — 격리돼 있어 깨져도 해당 기능만 영향받는다.
  • Core/Routing(15) 은 경계선. 딥링크 회귀가 반복되면 그때 추가한다.

자주 겪는 상황

"PR 에 Review required 가 떴는데 승인자가 안 보인다"

보호 경로를 건드렸다는 뜻이다. PR 의 Files changed 에서 어떤 파일이 위 표에 해당하는지 확인하고, @JEONG-J 를 리뷰어로 요청한다. 보호 경로를 건드릴 의도가 없었다면 그 파일을 커밋에서 빼는 게 빠르다.

"내 커밋이 unattributed 라고 나온다"

로컬 git 이메일이 GitHub 계정에 등록돼 있지 않다. 확인:

git config user.email                        # 로컬 설정
gh api user/emails --jq '.[].email'          # GitHub 에 등록된 이메일

GitHub Settings → Emails 에 그 주소를 추가하거나, git config user.email 을 등록된 주소로 맞춘다.

"보호 경로를 진짜 바꿔야 한다"

정상적인 경로다. 막는 게 목적이 아니라 혼자 조용히 바뀌는 걸 막는 게 목적이다. 이슈에 변경 이유와 영향 범위(어떤 모듈이 같이 깨질 수 있는지)를 적고 PR 을 올린 뒤 승인을 요청하면 된다.

"경로를 추가하거나 빼고 싶다"

.github/CODEOWNERS 를 수정하는 PR 을 올린다. 그 PR 자체가 /.github/ 경로에 걸리므로 소유자 승인이 필요하다.

확인 명령

# CODEOWNERS 문법 검증 (잘못된 경로는 조용히 무시되므로 반드시 확인)
gh api repos/UMC-PRODUCT/Big-Dipper-iOS/codeowners/errors

# 현재 ruleset 상태
gh api repos/UMC-PRODUCT/Big-Dipper-iOS/rulesets/11514288 \
  --jq '{enforcement, rules: [.rules[].type], bypass: .bypass_actors}'

# bypass 권한을 가진 팀 멤버 (게이트가 실제로 걸리는지 확인)
gh api orgs/UMC-PRODUCT/teams/1st-ios/members --jq '.[].login'

관련 문서: Git Workflow · Module Structure · Coding Conventions

Clone this wiki locally