왜 수동 리뷰어 지정이 문제인가
PR을 올릴 때마다 "이 코드는 누가 봐야 하지?"를 고민하는 팀이 많다. 결정이 사람에게 맡겨지면 리뷰어가 특정 인원에게 몰리고, 도메인 담당자가 빠진 채 병합되며, 신규 입사자는 코드 소유자를 모른다. 리뷰 누락은 곧 장애로 이어진다. CODEOWNERS는 "경로 → 소유자" 매핑을 코드로 선언해 이 결정을 자동화한다. GitHub·GitLab 모두 지원하며, 브랜치 보호 규칙과 결합하면 소유자 승인 없이는 병합이 막힌다.
기본 문법과 매칭 규칙
파일은 저장소 루트, .github/, docs/ 중 한 곳에 둔다. gitignore와 유사한 글롭 패턴을 쓰되, 마지막에 매칭된 규칙 하나만 적용된다는 점이 핵심이다. 여러 규칙에 걸쳐도 누적되지 않는다.
# .github/CODEOWNERS
# 기본 소유자 — 위 규칙에 안 걸린 모든 파일
* @org/platform-team
# 디렉터리 단위 소유
/services/payments/ @org/payments @alice
/infra/terraform/ @org/sre
# 확장자 단위
*.tf @org/sre
*.sql @org/data-eng
# 특정 파일만 별도 소유 (아래일수록 우선)
/infra/terraform/prod.tfvars @org/sre-leads
주의: *.tf와 /infra/terraform/가 동시에 걸리는 파일은 아래쪽 규칙 하나만 적용된다. 세분화된 규칙은 반드시 파일 하단에 배치한다.
승인 라우팅을 강제하는 브랜치 보호
CODEOWNERS만으로는 리뷰어가 "지정"될 뿐 "필수"가 아니다. 브랜치 보호 규칙에서 Require review from Code Owners를 켜야 승인 게이트가 된다. API로 관리하면 저장소 수십 개에 일관 적용할 수 있다.
gh api -X PUT repos/OWNER/REPO/branches/main/protection \
--input - <<'JSON'
{
"required_pull_request_reviews": {
"require_code_owner_reviews": true,
"required_approving_review_count": 1
},
"required_status_checks": { "strict": true, "contexts": ["ci/test"] },
"enforce_admins": true,
"restrictions": null
}
JSON
팀 매핑과 권한 전제 조건
흔한 실수는 CODEOWNERS에 적은 팀·유저가 실제로 저장소 쓰기 권한이 없는 경우다. 권한 없는 소유자는 조용히 무시되어 승인 게이트가 사실상 비어버린다. 배포 전 검증을 CI에 넣는다.
import re, sys, subprocess, json
owners = set()
for line in open(".github/CODEOWNERS"):
line = line.split("#")[0].strip()
if not line:
continue
owners.update(re.findall(r"@[\w/-]+", line))
# 저장소 협업자/팀 권한 조회 후 대조
valid = json.loads(subprocess.check_output(
["gh", "api", "repos/OWNER/REPO/collaborators", "--paginate"]))
valid_logins = {"@" + u["login"] for u in valid}
missing = [o for o in owners if "/" not in o and o not in valid_logins]
if missing:
print("권한 없는 소유자:", missing)
sys.exit(1)
GitHub vs GitLab 동작 차이
| 항목 | GitHub | GitLab |
|---|---|---|
| 매칭 규칙 | 마지막 매칭 1개 | 섹션 내 마지막 매칭 |
| 섹션 그룹핑 | 없음 | [Section] 문법 지원 |
| 필수 승인 | 브랜치 보호에서 설정 | Approval Rules와 연동 |
| 선택적 소유자 | 미지원 | ^[Optional] 지원 |
GitLab은 섹션별로 독립적인 승인 수를 요구할 수 있어, 보안·인프라 같은 다중 게이트 설계에 유리하다.
운영 시 주의점
- 단일 인원 지정 금지: 개인 계정만 소유자로 두면 휴가·퇴사 시 병합이 막힌다. 반드시 팀(
@org/team)을 우선한다. - 자기 코드 자기 승인 불가: 소유자 본인이 작성자면 다른 소유자가 필요하다. 팀 인원이 1명이면 데드락이 생긴다.
- 과도한 세분화 경계: 규칙이 많아질수록 한 PR에 리뷰어가 5~6명 붙어 오히려 리뷰가 지연된다. 디렉터리 단위로 크게 잡고 필요한 곳만 좁힌다.
- CODEOWNERS 자체 보호: 이 파일의 소유자를 플랫폼 팀으로 지정해 임의 변경을 막는다.
정리
CODEOWNERS는 "누가 리뷰하는가"를 사람 기억이 아닌 저장소 규칙으로 옮긴다. 브랜치 보호로 강제하고, 권한 검증을 CI에 넣고, 팀 단위로 소유권을 잡으면 리뷰 누락과 병목을 동시에 줄일 수 있다. 규칙은 최소한으로 시작해 실제 리뷰 흐름을 보며 조금씩 세분화하는 편이 유지보수에 유리하다.