넓게 만들고 좁게 출시하기: AI 시대에 PR 경계를 언제 정할 것인가
지난 분기에 우리 팀에서 실제로 있었던 일이다. 인증 흐름 하나 바꾸는 작업을 RFC로 먼저 쪼갰다. 이슈 6개, PR 6개. 3번째 PR을 머지하고 나서야 "아, 2번에서 만든 인터페이스로는 프론트에서 못 쓴다"는 걸 알았다. 앞의 두 개를 되돌리고 다시 만들었다. 리뷰어 시간까지 합치면 이틀은 날아갔다.
GeekNews에 올라온 "넓게 만들고, 좁게 배포하라(Build Wide, Ship Narrow)"는 딱 이 문제를 건드린다. 핵심 주장은 간단하다. 무엇을 만들지는 구현 전에 정하되, 어떻게 PR로 쪼갤지는 구현이 끝난 뒤에 정하자.
왜 우리는 코드도 없는데 PR 경계를 먼저 정해왔나
전통적 흐름은 RFC 작성 → 작은 이슈 분해 → 순차 구현이었다. 합리적으로 보이지만 근본적 문제가 있다. 아직 코드가 한 줄도 없는, 정보가 가장 부족한 시점에 구조적 경계를 결정해야 한다는 것이다.
- 어떤 부분이 실제로 분리 가능한지 모른다
- 각 부분의 복잡도를 추측만 할 수 있다
- 뒷 단계에서 발견한 문제가 앞 설계를 뒤집을지 알 수 없다
그런데도 왜 미리 쪼갰나? 원문은 이렇게 답한다. 완성된 브랜치를 나중에 작은 리뷰 단위로 다시 푸는 일이 너무 비쌌기 때문이다. 사전 분해는 구현을 쉽게 하려던 게 아니라 "사람이 리뷰할 수 있는 크기"를 확보하려던 현실적 타협이었다.
AI가 바꾼 건 딱 이 비용이다. 원문이 꼽는 싸진 것 세 가지: 구현, 설계 검토, 그리고 완성된 작업의 재분해. 일주일간 얽혀 만든 브랜치를 작은 PR 여러 개로 다시 나누는 게 프롬프트로 처리 가능한 수준이 됐다는 것이다.
반대로 안 싸진 것도 명확하다. 코드 리뷰의 판단 부분(이 코드가 여기 있는 게 맞나, 이 API가 6개월 뒤에도 괜찮나)과 제품 검증이다. 봇이 PR을 승인하는 것과 사람이 코드를 이해하는 것은 다르다는 문장이 이 글의 핵심이라고 본다.
동작 원리: 작업 브랜치는 '버려도 되는 연습장'
전체 흐름은 다섯 단계다.
- 설계 검토 — 계획에 실제 결정이 충분히 들어갈 때까지. 새 설계면 명세를 코드보다 먼저 커밋
- Build Wide — 브랜치 하나에서 백엔드/프론트 가로질러 끝까지 구현
- 데모 먼저 — PR 열기 전에 Slack 영상이나 프리뷰 배포로 제품 피드백
- Ship Narrow — 완성된 코드가 드러낸 경계로 작은 PR 재구성
- 정리 PR — 기존 코드 삭제는 마지막 별도 PR
여기서 인프라 하는 사람이 가장 거부감 느낄 지점이 2번이다. 원문 저자는 리팩터링 하나에서 하루 동안 수십 개 파일을 고치며 커밋 12개 이상을 만들었다고 한다. Git 이력 깔끔하게 관리하던 사람 입장에선 끔찍하다.
근데 전제가 다르다. 이 작업 브랜치의 이력은 최종 기록으로 남지 않는다. 최종 PR은 전부 main에서 새로 만든다. 작업 브랜치는 버리는 연습장이고, 커밋은 "위험한 시도 전 되돌아갈 지점" 용도다. 구현 중 필요한 이력과 리뷰어가 읽어야 할 이력을 분리하는 것이다.
실제 분해는 이런 식으로 간다. 원문의 5개 PR 사례:
# 작업 브랜치(feat/wide)에서 기능 완성 후, main 기준으로 PR별 worktree 생성
git worktree add ../pr1-backend-a -b pr/backend-endpoint-a main
git worktree add ../pr2-backend-b -b pr/backend-endpoint-b main
# 프론트는 실제 의존성이 있으므로 해당 백엔드 PR 위에 스택
git worktree add ../pr3-front-a -b pr/frontend-view-a pr/backend-endpoint-a
# 삭제만 담당하는 마지막 PR
git worktree add ../pr5-cleanup -b pr/remove-legacy-path main
git worktree list
/home/dev/app a1b2c3d [main]
/home/dev/pr1-backend-a a1b2c3d [pr/backend-endpoint-a]
/home/dev/pr2-backend-b a1b2c3d [pr/backend-endpoint-b]
/home/dev/pr3-front-a e4f5g6h [pr/frontend-view-a]
/home/dev/pr5-cleanup a1b2c3d [pr/remove-legacy-path]
독립적인 백엔드 엔드포인트 2개는 각각 main에서 직접 분기, 프론트 뷰 2개는 자기가 쓰는 백엔드 위에 스택, 마지막 PR은 기존 경로 삭제만. 원문에 따르면 이 마지막 삭제 PR에서 제거한 코드가 새 기능 전체가 추가한 코드보다 수백 줄 더 많았다고 한다.
이 사례가 말하는 게 뭐냐면, "기존 코드 삭제 PR"은 사전 분해로는 절대 못 만드는 단위라는 것이다. 새 경로를 전부 만들고 동작을 확인해야만 무엇을 지울 수 있는지 알 수 있으니까.
실무 관점: 어디에 쓰고 어디서 터지는가
잘 맞는 작업은 원문 기준으로 세 가지다. 백엔드/프론트를 가로지르는 기능, 끝까지 해보기 전엔 구조를 모르는 리팩터링, 사전에 경계를 정하려면 추측만 가능한 작업.
덜 맞는 작업이 더 중요하다. 인프라 하는 입장에선 여기가 지뢰밭이다.
- 운영 순서가 제약인 마이그레이션과 스키마 변경 — 어떤 변경을 먼저 적용해야 하는지가 실제 제약이다. 사후에 예쁘게 쪼개봐야 배포 순서는 여전히 강제된다
- 처음부터 나눌 위치가 명백한 작업 — 굳이 왕복할 이유가 없다
- 첫 단계만 따로는 절대 배포 못 하는 기능 — 사후 분해로 얻는 건 점진적 리뷰뿐이다
세 번째 항목은 원문도 솔직하게 인정한다. 예시로 든 5개 PR은 글 쓰는 시점에도 전부 열려 있었고, 결국 같은 날 다 머지될 수도 있다고 한다. PR을 잘 나눴다고 자동으로 점진 배포가 되는 게 아니다. "리뷰하기 쉬움"과 "독립적으로 머지할 가치가 있음"은 다른 조건이고, 에이전트가 도와줄 수 있는 건 앞의 것뿐이다.
흔한 함정 1: 편하다고 스택하기
원문 규칙은 명확하다. 실제 의존성이 있을 때만 스택한다. 단순히 편해서 스택하면 아래 PR에 리뷰 피드백이 들어오는 순간 위쪽 브랜치 전부를 연쇄 리베이스해야 한다. 실무에서 이렇게 터진다.
$ git rebase pr/backend-endpoint-a
Auto-merging src/api/client.ts
CONFLICT (content): Merge conflict in src/api/client.ts
error: could not apply e4f5g6h... feat: add frontend view A
hint: Resolve all conflicts manually, mark them as resolved with
hint: "git add/rm <conflicted_files>", then run "git rebase --continue".
Could not apply e4f5g6h... feat: add frontend view A
3~4단 스택에서 이게 나면 아래에서부터 순서대로 다 풀어야 한다. 실제 의존성이 없는데 편의로 쌓아둔 거면 그냥 손해다. 원문은 최초 분해를 수행한 채팅 세션을 유지해두면 그 컨텍스트로 위쪽 브랜치 업데이트도 다시 맡길 수 있다고 하는데, 이건 도구 의존적인 얘기라 각자 환경에서 검증이 필요해 보인다.
흔한 함정 2: 데모를 건너뛰기
이게 실무에서 가장 많이 생략되는 단계인데, 사실 이 방식의 절반이다. 코드 리뷰 단계에서 "UI가 헷갈린다", "이 API 프론트에서 쓰기 불편하다"는 피드백이 나오면 리뷰어 시간과 개발자 시간이 동시에 날아간다. 데모 단계에서 나오면 코드 구조를 크게 바꿔도 부담이 없다.
인프라 관점에서 이건 PR 프리뷰 환경이 있냐 없냐의 문제로 직결된다. 없으면 이 워크플로 자체가 안 돌아간다. CI에서 PR 브랜치별 임시 환경을 띄우는 파이프라인이 선행 조건이라고 보면 된다.
# .github/workflows/preview.yml (핵심만)
on:
push:
branches: ['feat/**'] # PR 열기 전 작업 브랜치도 대상
jobs:
preview:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Deploy preview
run: |
NS="pv-$(echo $GITHUB_REF_NAME | tr '/' '-' | cut -c1-40)"
kubectl create ns "$NS" --dry-run=client -o yaml | kubectl apply -f -
helm upgrade --install app ./chart -n "$NS" \
--set image.tag=$GITHUB_SHA --set ingress.host=$NS.preview.example.com
echo "https://$NS.preview.example.com"
포인트는 on.push.branches에 PR이 아닌 작업 브랜치를 넣었다는 것이다. Build Wide 단계에선 PR이 아직 없으니까. 다만 네임스페이스 무한 증식은 막아야 한다. TTL 붙여서 자동 정리하는 job은 별도로 필요하다.
흔한 함정 3: 분해를 AI에 맡기고 안 읽기
원문이 강조하는 부분인데, PR로 나누는 과정 자체가 작성자 본인의 첫 코드 리뷰다. diff를 머릿속에 담을 수 있는 크기로 하나씩 확인하는 과정이 "AI가 쓴 코드를 그냥 배포한 것"과 "이해하고 배포한 것"을 가른다. 여기서 분해까지 통째로 자동화하고 넘기면 그냥 2,000줄 PR을 다섯 조각 낸 것에 불과하다.
그리고 오해하기 쉬운 게, Build Wide가 "모든 PR이 작아진다"는 뜻이 아니다. 새 데이터 모델, 새 API 표면, 신뢰 경계가 걸린 백엔드 PR은 여전히 무겁고 무거워야 한다. 목표는 균일한 크기가 아니라 리뷰어의 주의를 실제 위험이 있는 곳에 몰아주는 구조다.
대안 — 안 바꿔도 되는 경우
기존 트렁크 기반 개발 + 피처 플래그 조합이 이미 잘 돌고 있고, 도메인이 안정적이라 경계가 자명하다면 굳이 갈아탈 이유가 없다. 이 방식이 값을 하는 건 "해보기 전엔 모르겠는" 작업이다. 반대로 팀에 프리뷰 환경도 없고 리뷰가 며칠씩 밀리는 상황이면, 이 워크플로 도입보다 리뷰 SLA부터 잡는 게 순서다.
정리
한 줄로: 구현 전에 추측으로 하던 PR 경계 결정을, 실제 코드가 존재하는 시점까지 미루는 것. 구조적 판단을 없애는 게 아니라 뒤로 옮기는 거다. 설계를 건너뛰라는 얘기가 절대 아니고, 오히려 원문 저자는 편집기 열기 전에 계획을 세우고 새 설계면 명세를 문서 PR로 먼저 머지해 팀이 이의 제기할 기회를 준다고 한다.
이런 팀에 권한다.
- AI 코딩 에이전트를 이미 일상적으로 쓰고 있는데 PR이 자꾸 비대해지는 팀
- 풀스택 기능 하나 만드는 데 이슈를 5~6개로 쪼개다가 중간에 설계가 뒤집힌 경험이 있는 팀
- PR 프리뷰 환경이 이미 있거나, 만들 여력이 있는 팀
반대로 스키마 마이그레이션이 주 업무거나, 배포 순서가 규제로 정해져 있는 도메인이면 얻는 게 별로 없다. 그리고 도입한다면 프리뷰 환경 → 데모 문화 → 분해 자동화 순서로 가는 걸 권한다. 마지막 것부터 손대면 그냥 큰 PR을 잘게 썬 것으로 끝난다.