PR 리뷰어 매번 손태그하다 깃허브 코드 오너로 못 박은 자리

지난달에 KMP 멀티모듈 PR을 올려놓고 리뷰어 태그를 깜빡했어요. 이틀 뒤 사내 슬랙에서 "이거 아무도 안 봤네요?" 소리를 듣고 나서야 알았습니다. 그날 바로 CODEOWNERS를 박았네요.
생각해보면 이게 한두 번이 아니었어요. PR 올릴 때마다 "이 모듈은 누가 보지?" 하고 머릿속으로 담당자를 찾는 비용이 매번 들었거든요. 신규 입사한 동기는 모듈 담당자를 매번 슬랙으로 물어봤고요. 리뷰어 미지정으로 2~3일씩 떠 있던 PR도 분기마다 한두 건씩 나왔습니다.
CODEOWNERS로 깃허브 리뷰어 자동 배정 설정하기
깃허브(GitHub)는 .github/CODEOWNERS 파일 하나로 경로별 담당자를 묶어줍니다. 파일을 루트나 docs/에 둬도 인식은 하지만, 여러 군데 있으면 .github/가 가장 우선이라 저는 여기에 둡니다.
문법은 .gitignore랑 거의 똑같아요. glob 패턴 옆에 소유자(@org/team-name 또는 @username)를 적으면 끝입니다.
# 전역 규칙은 반드시 최상단 (Last Match Wins)
* @my-org/platform
# 아래로 갈수록 우선순위가 높음
/feature/login/ @my-org/login-team
/core/ @my-org/core-team
*.gradle.kts @my-org/build-team
여기서 딱 하나 헷갈리기 쉬운 게 마지막 규칙이 이긴다(Last Match Wins) 는 점입니다. 한 파일이 여러 패턴에 걸리면 파일 안에서 제일 아래 적힌 규칙만 적용되지요. 그래서 전역 규칙 *은 무조건 맨 위에 올려야 합니다. 이걸 거꾸로 깔면 모든 PR이 죄다 플랫폼 팀한테만 가는 사고가 납니다.
그리고 오타나 권한 문제는 에러 없이 조용히 무시될 때가 많아요. 소유자로 적은 팀/사람은 저장소에 쓰기(write) 권한이 있어야 하고요. 파일이 3MB를 넘으면 아예 로드가 안 됩니다. 그러니까 설정한 다음엔 테스트 PR을 한 번 올려서 리뷰어가 실제로 자동 호출되는지 눈으로 확인하시는 게 안전해요.

KMP 멀티모듈 담당 경계를 팀 단위로 매핑
여기가 KMP 프로젝트에서 제일 효과를 본 부분입니다. 모듈이 잘게 쪼개진 구조일수록 경로별 담당이 명확해집니다.
저는 이렇게 나눴어요.
- 기능 모듈(
feature/login/,feature/cart/)은 해당 도메인 팀에 매핑합니다. - 공용 모듈(
core/,build-logic/)은 플랫폼·공통 팀으로 묶습니다. - 빌드 전반을 흔드는
*.gradle.kts나 버전 카탈로그(libs.versions.toml)는 별도 규칙으로 빌드 담당자를 추가합니다.
소유자를 개인이 아니라 팀(@org/team-name)으로 적는 게 핵심이라고 봅니다. 특정 한 사람을 박아두면 그 사람 휴가 갔을 때 리뷰가 통째로 멈추거든요. 팀으로 걸어두면 깃허브가 알아서 팀원 중 한 명을 골라줍니다.
이 분배 방식도 고를 수 있어요. 팀 리뷰 설정에서 라운드 로빈(round robin) 은 가장 오래전에 요청받은 사람한테 순서대로 돌리고, 로드 밸런스(load balance) 는 최근 30일 리뷰량과 진행 중인 리뷰를 따져서 부담을 균등하게 맞춰줍니다. 팀이 작으면 라운드 로빈, 인원이 많고 편차가 크면 로드 밸런스가 무난해 보입니다. 상태를 "Busy"로 둔 사람은 자동 배정에서 빠지고요.

브랜치 보호 규칙으로 코드 오너 승인 강제하기
여기서 한 번 크게 헷갈렸던 게 있어요. CODEOWNERS만 깔면 머지까지 막아주는 줄 알았는데, 아니더라고요.
CODEOWNERS는 리뷰어를 자동으로 요청할 뿐, 머지 자체를 막지는 못합니다. 승인 없이도 그냥 머지 버튼이 눌려요. 그래서 main 브랜치 보호 규칙에서 "Require a review from Code Owners" 를 켜야 비로소 코드 오너 승인 없는 머지가 차단됩니다.
저는 여기에 "Require approvals"를 함께 걸어서 최소 승인 인원 한 겹을 더 뒀어요. 코드 오너 승인 + 추가 승인, 이렇게 두 겹이면 리뷰 누락으로 인한 사고가 확 줄더라고요.
다만 CODEOWNERS는 PR의 크기나 레이블, 작성자 같은 정황은 전혀 못 읽습니다. 그저 바뀐 파일 경로만 봅니다. "큰 PR이면 시니어 한 명 더 붙여라" 같은 조건부 로직이 필요하면 auto-request-review 같은 깃허브 액션(GitHub Actions)으로 따로 짜야 합니다.

CODEOWNERS 운영하면서 챙긴 것들
한 번 깔고 방치하면 금방 유령 소유자가 생겨요. 조직 개편이나 팀원 이동이 있을 때마다 손으로 갱신해야 합니다. 저는 분기 회고 체크리스트에 "CODEOWNERS 담당 유효성 점검"을 한 줄 넣어뒀어요.
문서(docs/)나 번역 파일까지 강제 승인 대상에 넣으면 오타 수정 하나에도 리뷰 대기가 걸려서 병목이 생깁니다. 이런 경로는 강제 승인에서 빼두는 게 낫다고 봅니다.
참고로 깃랩(GitLab)을 쓰신다면 거의 같은 개념이 있어요. 파일은 루트나 docs/, .gitlab/에 두고, 승인 규칙(Approval Rules)과 묶어서 머지 요청 승인을 강제합니다. 승인 자격이 있는 소유자는 최소 Developer 역할이 필요하고요.
설정 자체는 10분이면 끝나요. 근데 그 10분이 매번 "이거 누가 보지?" 하던 며칠을 통째로 없애줍니다. 리뷰어를 손으로 태그하는 자리가 사라졌다는 게 제일 큰 변화였어요. 손으로 챙기던 걸 파일 한 줄로 옮겨두면, 사람이 깜빡할 여지부터 먼저 사라집니다.
