거대한 리팩터링 PR을 안전하게 쪼개는 법: 프론트엔드 팀의 브랜치·커밋 전략 실전

"이 PR 리뷰할 수 있는 사람이 있을까요" — 거대 PR이 만드는 조용한 재앙
우리 프론트엔드 팀이 처음으로 컴포넌트 라이브러리 전면 교체를 결정했을 때, 첫 번째 반응은 의외로 낙관적이었습니다. "모달만 먼저 바꾸고, 그 다음에 폼, 그 다음에 테이블 순서로 가면 되지 않을까?" 하는 식이었습니다. 3주 후, 우리는 refactor/ui-library 브랜치가 main과 무려 200개 커밋 차이가 난다는 사실을 발견했습니다. 그 브랜치를 리베이스하려고 시도했다가 conflict 해소에만 이틀이 걸렸고, 결국 팀은 그 브랜치를 포기하고 처음부터 다시 PR을 쪼개는 작업을 시작했습니다. 잃어버린 것은 코드가 아니라 시간과 팀의 에너지였습니다.
이 글은 그 경험에서 체계화한 전략을 담고 있습니다. 거대한 리팩터링 PR이 왜 조직에 실질적인 비용을 만드는지, 그리고 Parallel Change(Expand-Contract), Strangler Fig, git worktree, Atomic Commit, Draft PR 체크리스트까지 실전에서 검증한 도구들을 조합해 안전하게 PR을 쪼개는 방법을 단계적으로 설명합니다. 테스트 전략은 Vitest + MSW 통합 테스트 가이드와 함께 읽으면 더 완성된 그림이 됩니다.
1. "이 PR 너무 큰데요" — 거대 PR이 조직에 만드는 실제 비용
GitHub에서 파일 변경이 50개, diff 라인이 2,000줄을 넘는 PR을 받아본 팀이라면 공통적인 경험을 압니다. 리뷰어는 PR을 열었다가 스크롤을 한두 번 내리고 "나중에 봐야지"라고 탭을 닫습니다. 그리고 그 PR은 사흘, 일주일, 길게는 2주를 방치됩니다.
비용은 여러 차원에서 누적됩니다. 첫째, 리뷰 대기 시간입니다. 코드 리뷰 실태 조사에 따르면 PR 크기가 400줄을 초과하면 리뷰어의 결함 발견율이 급격히 떨어집니다. 리뷰어가 집중력을 유지할 수 있는 코드의 양에는 물리적 한계가 있습니다.
둘째, revert 위험입니다. 거대한 PR이 병합된 후 프로덕션에서 문제가 발생하면, git revert는 수백 개의 파일 변경을 한 번에 되돌려야 합니다. 이 과정에서 의도하지 않은 사이드 이펙트가 발생하고, 종종 revert 그 자체가 새로운 버그를 만듭니다.
셋째, 컨텍스트 손실입니다. PR이 오래 방치될수록 작성자 본인도 코드의 의도를 잊어갑니다. 리뷰어의 질문에 "잠깐만요, 왜 이렇게 했는지 확인해봐야 할 것 같아요"라는 응답이 나온다면 이미 컨텍스트가 손실된 것입니다. 리뷰 사이클이 늘어날수록 병합 전까지 main과의 괴리가 커지는 악순환도 함께 심화됩니다.
넷째, CI/CD 파이프라인 병목입니다. 거대한 PR은 빌드·테스트 시간이 길어지고, conflict가 생길 때마다 전체 파이프라인을 다시 실행해야 합니다. 이 오버헤드가 팀의 배포 속도를 실질적으로 제한합니다.
2. 리팩터링의 두 종류: 동작을 바꾸지 않는 Tidy vs. 동작을 바꾸는 Feature
거대 PR 문제를 해결하기 전에 리팩터링의 성격을 구분하는 것이 선행되어야 합니다. Kent Beck은 공개 자료 Tidy First?에서 코드 변경을 크게 두 범주로 나눕니다. 동작을 바꾸지 않는 정리(Tidy) 와 동작을 변경하는 기능 변경(Feature) 입니다.
이 구분은 PR 분리 전략의 핵심 원칙이 됩니다. 동작을 바꾸지 않는 Tidy 작업 — 변수명 변경, 파일 이동, 함수 추출, 코드 포매팅 — 은 별도 PR로 먼저 병합합니다. 동작을 바꾸는 작업은 그 위에서 진행합니다. 이 두 종류를 한 PR에 섞으면 리뷰어는 "이 변경이 동작에 영향을 주는가?"라는 질문을 매 diff 라인마다 되물어야 합니다. 인지 부하가 기하급수적으로 늘어납니다.
실무에서 이 구분이 무너지는 흔한 패턴은 이렇습니다. 파일을 이동하면서 동시에 함수 시그니처를 바꾸고, 바꾼 김에 로직도 조금 손보는 것입니다. 각각의 변경 자체는 작지만 조합된 결과물은 리뷰어가 이해하기 어려운 거대한 PR이 됩니다.
따라서 리팩터링 PR을 계획할 때 가장 먼저 해야 할 작업은 변경 목록을 Tidy와 Feature로 분류하는 것입니다. Tidy 항목은 선행 PR로, Feature 항목은 후속 PR로 예약합니다. 이 목록이 없으면 나중에 설명할 Draft PR 체크리스트도 의미가 없습니다.
3. Parallel Change(Expand-Contract) 3단계 — 새 인터페이스 추가 → 호출부 이전 → 구 인터페이스 삭제
Martin Fowler의 Parallel Change는 호환되지 않는 인터페이스 교체를 안전하게 수행하는 3단계 패턴입니다. Expand(확장), Migrate(이전), Contract(수축)의 흐름으로, 각 단계를 독립된 PR로 병합할 수 있기 때문에 거대 PR 분리의 핵심 기법이 됩니다.
아래 예제는 LegacyModal을 새로운 Modal로 교체하는 실제 시나리오입니다. LegacyModal은 show prop으로 가시성을 제어했고, 새 Modal은 isOpen과 onClose 조합으로 제어하는 방식을 채택합니다.
// ── STEP 1: Expand ──────────────────────────────────────────────
// PR #1: 새 Modal 컴포넌트를 추가한다. 기존 LegacyModal은 건드리지 않는다.
// src/components/ui/Modal.tsx
interface ModalProps {
isOpen: boolean;
onClose: () => void;
title: string;
children: React.ReactNode;
size?: 'sm' | 'md' | 'lg';
}
export function Modal({ isOpen, onClose, title, children, size = 'md' }: ModalProps) {
if (!isOpen) return null;
return (
<dialog
open={isOpen}
aria-modal="true"
aria-labelledby="modal-title"
className={'modal modal--' + size}
onClose={onClose}
>
<div className="modal__header">
<h2 id="modal-title">{title}</h2>
<button type="button" aria-label="닫기" onClick={onClose}>✕</button>
</div>
<div className="modal__body">{children}</div>
</dialog>
);
}
// ── STEP 2: Migrate ──────────────────────────────────────────────
// PR #2: 호출부를 LegacyModal → Modal로 순차 교체.
// 이 PR은 UI 동작 변경이 없어야 한다. 타입 시그니처 매핑만 변경.
// Before (src/features/order/OrderCancelDialog.tsx)
// <LegacyModal show={isCancelOpen} title="주문 취소">...</LegacyModal>
// After
// <Modal isOpen={isCancelOpen} onClose={() => setIsCancelOpen(false)} title="주문 취소">
// ...
// </Modal>
// ── STEP 3: Contract ─────────────────────────────────────────────
// PR #3: LegacyModal 파일 삭제 + import 정리
// 이 PR에서 처음으로 코드가 실제로 줄어든다.
// src/components/ui/LegacyModal.tsx → 삭제
// src/components/ui/index.ts → LegacyModal export 제거
각 PR은 단독으로 revert 가능합니다. Expand PR을 병합한 후 문제가 생기면 Contract PR 없이 해당 PR만 revert하면 됩니다. Migrate PR이 너무 크다면 파일·피처 단위로 더 쪼갤 수 있습니다. 이것이 Parallel Change의 핵심 가치입니다. 어떤 단계에서 멈춰도 시스템이 정상 작동합니다.
4. Strangler Fig를 컴포넌트 단위로 적용하기 — <LegacyModal> ↔ <Modal> 공존 전략
Martin Fowler의 Strangler Fig Application 패턴은 레거시 시스템을 한 번에 교체하는 대신, 새 시스템이 레거시를 점진적으로 감싸다가 최종적으로 대체하는 전략입니다. 원래는 대규모 시스템 마이그레이션을 위한 개념이지만, 프론트엔드 컴포넌트 단위에도 그대로 적용됩니다.
컴포넌트 단위 Strangler Fig의 핵심은 라우터 레이어 또는 Wrapper 컴포넌트를 스위치로 쓰는 것입니다. 새 컴포넌트와 레거시 컴포넌트가 공존하는 기간 동안 호출부는 어떤 구현을 쓸지 선택할 수 있어야 합니다.
실무에서 이 공존 기간을 명확하게 제한하는 것이 중요합니다. 공존을 "임시 상태"로 명문화하지 않으면 수개월 뒤에도 두 컴포넌트가 같은 코드베이스에서 혼재하는 상황이 됩니다. 우리 팀은 새 컴포넌트를 추가할 때 기존 컴포넌트 파일 상단에 @deprecated JSDoc과 함께 삭제 예정 마일스톤을 명시합니다.
// src/components/ui/LegacyModal.tsx
/**
* @deprecated Modal 컴포넌트로 교체 중입니다.
* 마이그레이션 가이드: src/components/ui/Modal.tsx
* 삭제 예정: Sprint 38 (2026-06-13)
* 추적 이슈: https://github.com/waylog/waylog-app/issues/4821
*/
export function LegacyModal({ show, title, children }: LegacyModalProps) {
// ... 기존 구현 유지
}
@deprecated 태그는 TypeScript Language Server가 인식해 IDE에서 취소선으로 표시합니다. 개발자가 실수로 새 파일에서 LegacyModal을 임포트하려 할 때 경고를 받을 수 있습니다. ESLint의 no-restricted-imports 규칙과 조합하면 린트 단계에서 새 LegacyModal 사용을 강제로 차단하는 것도 가능합니다.
Strangler Fig 전략이 Parallel Change와 다른 점은 교체 대상의 그레뉼래리티입니다. Parallel Change는 인터페이스 수준의 전환에 초점을 맞추고, Strangler Fig는 시스템·모듈·컴포넌트 수준의 점진적 대체에 초점을 맞춥니다. 컴포넌트 라이브러리 전체를 교체하는 시나리오라면 두 패턴을 함께 씁니다. 라이브러리 단위로 Strangler Fig를 설계하고, 각 컴포넌트의 인터페이스 교체는 Parallel Change로 처리합니다.
5. git worktree로 리팩터링 브랜치와 기능 브랜치를 동시에 작업하기
리팩터링 작업을 하다가 긴급한 버그 수정 요청이 들어오거나, 다른 기능 브랜치의 코드 리뷰를 해야 하는 상황은 일상적입니다. 브랜치를 git stash로 저장하고 git checkout으로 전환하는 방식은 컨텍스트 전환 비용이 높고, node_modules 재설치나 빌드 캐시 무효화가 생기기도 합니다.
Git 공식 문서의 git-worktree는 하나의 Git 저장소에서 여러 작업 디렉토리를 동시에 체크아웃할 수 있게 해줍니다. 각 worktree는 독립적인 파일시스템 경로를 가지므로, 두 브랜치를 동시에 열어두고 IDE 창 두 개에서 각각 작업하는 것이 가능합니다.
# ── git worktree 기반 병렬 작업 흐름 ────────────────────────────────
# 현재 위치: ~/projects/waylog-app (main 브랜치)
# 리팩터링 브랜치를 별도 디렉토리로 체크아웃
git worktree add ../waylog-refactor refactor/modal
# worktree 목록 확인
git worktree list
# /Users/choi/projects/waylog-app a3f2c1d [main]
# /Users/choi/projects/waylog-refactor 7b9e4f2 [refactor/modal]
# 리팩터링 작업
cd ../waylog-refactor
pnpm install # worktree별 node_modules는 독립적으로 관리됨
# 변경 내용을 의미 단위로 스테이징 — git add -p 사용
# -p(--patch) 플래그는 변경된 hunk를 하나씩 검토하며 선택적으로 스테이징
git add -p src/components/ui/Modal.tsx
# 인터랙티브 hunk 선택지:
# y - 이 hunk를 스테이지에 추가
# n - 이 hunk는 건너뜀
# s - hunk를 더 작게 쪼갬
# e - 에디터에서 직접 수정
# q - 종료
# Atomic commit 메시지 예시 (Conventional Commits 형식)
git commit -m "feat(ui): Modal 컴포넌트 초기 구현 — isOpen/onClose 기반 제어
- dialog 네이티브 엘리먼트 사용으로 접근성 향상 (WAI-ARIA 1.2)
- size prop으로 sm/md/lg 세 가지 크기 지원
- aria-modal, aria-labelledby 자동 주입
관련 이슈: #4821
단계: Parallel Change Expand (1/3)"
# 긴급 버그 수정이 필요한 경우 — 원본 저장소에서 작업 (worktree 전환 없이)
cd ~/projects/waylog-app
git checkout -b hotfix/order-submit-crash
# ... 버그 수정 ...
git push origin hotfix/order-submit-crash
# 리팩터링 완료 후 worktree 제거
git worktree remove ../waylog-refactor
git worktree를 쓸 때 주의할 점은 같은 브랜치를 두 worktree에서 동시에 체크아웃하는 것은 불가능합니다. 또한 worktree 디렉토리를 삭제한 후 git worktree prune 명령으로 정리하지 않으면 Git이 해당 worktree를 여전히 추적합니다.
6. 커밋 단위를 PR 리뷰 단위로 설계하기 — Atomic Commit, git add -p, 의미 단위 squash
좋은 PR의 시작은 좋은 커밋입니다. 커밋 단위가 의미 있게 설계되면, PR을 커밋 단위로 리뷰하는 것이 가능해집니다. 리뷰어는 전체 diff를 한꺼번에 소화하는 대신, 커밋을 시간순으로 따라가며 변경의 흐름을 이해할 수 있습니다.
Atomic Commit의 원칙은 단순합니다. 하나의 커밋은 하나의 논리적 단위 변경만 포함해야 합니다. "작업하다 저장"식 커밋 — "wip", "fix", "temp" — 은 리뷰 단위가 될 수 없습니다. 이런 커밋들은 PR 제출 전 git rebase -i로 의미 단위로 squash합니다.
반면 지나친 squash도 문제입니다. 3단계 Parallel Change를 하나의 커밋으로 squash하면 단계별 revert가 불가능해집니다. 우리 팀의 규칙은 이렇습니다. "독립적으로 revert할 수 있는 최소 단위"가 하나의 커밋입니다.
git add -p(patch 모드)는 파일 단위가 아닌 hunk 단위로 스테이징을 선택할 수 있게 해줍니다. 같은 파일에서 두 가지 논리적 변경을 동시에 작업했다면, git add -p로 각각 다른 커밋에 나눠 담을 수 있습니다. 5번 섹션의 셸 스크립트에서 -p 플래그 사용 예시를 이미 보셨을 것입니다.
PR 단위 설계에서도 같은 원칙이 적용됩니다. 아래 표는 리팩터링 PR 분리 전략 세 가지의 트레이드오프를 정리합니다.
| 전략 | 병합 단위 | revert 용이성 | 리뷰 부하 | 기능 출시 속도 | 추천 상황 |
|---|---|---|---|---|---|
| Big-Bang PR | 전체 리팩터링 1개 | 어려움 (사이드 이펙트) | 매우 높음 | 즉시 (병합 후) | 규모가 매우 작을 때만 |
| Parallel Change | Expand→Migrate→Contract 3개 PR | 각 단계별 독립 revert | 중간 | 점진적 | 인터페이스 교체 시 |
| Feature Flag | 플래그 on/off로 제어 | 즉시 off 가능 | 낮음 | 플래그 활성화 시 | A/B 실험, 사용자 단계적 노출 |
Feature Flag 전략은 병합과 기능 출시를 분리한다는 장점이 있지만, 플래그 관리 인프라(LaunchDarkly 등)가 필요하고 플래그 해제를 빠뜨리면 Dead Code가 누적됩니다. 순수한 코드 구조 리팩터링에 Feature Flag를 쓰는 것은 오버엔지니어링에 해당하는 경우가 많습니다.
7. Draft PR + 체크리스트 템플릿으로 리뷰어 인지 부하 줄이기
PR Description은 코드보다 더 중요한 커뮤니케이션 도구입니다. 리뷰어가 PR을 열기 전에 "이 PR에서 내가 무엇을 확인해야 하는가"를 명확히 알 수 있어야 합니다. 아래는 우리 팀이 리팩터링 PR에 사용하는 Description 템플릿입니다.
## Why (배경과 목적)
기존 LegacyModal 컴포넌트는 show prop으로 가시성을 제어하는 방식으로,
네이티브 dialog 엘리먼트를 활용하지 않아 접근성 요구사항(WAI-ARIA 1.2)을
충족하지 못했습니다. 스크린리더 사용자 이슈 #4756이 이 문제를 트리거했습니다.
## What (변경 범위)
- [x] Modal 컴포넌트 신규 추가 (src/components/ui/Modal.tsx)
- [x] OrderCancelDialog 호출부 LegacyModal → Modal 교체
- [x] ProductImageModal 호출부 교체
- [ ] CartSummaryModal 호출부 교체 (다음 PR에서 진행)
이 PR에서 변경하지 않는 것: LegacyModal 구현 자체, 기존 스타일시트
## Migration Plan (3단계 Parallel Change)
| 단계 | PR | 상태 |
|------|----|------|
| Expand: 새 Modal 추가 | #4901 (이 PR) | 진행 중 |
| Migrate: 호출부 이전 (Part 1) | #4912 | 예정 |
| Contract: LegacyModal 삭제 | #4935 | 예정 |
## Reviewer Checklist (리뷰어 확인 사항)
- [ ] Modal props 타입이 기존 LegacyModal의 사용 패턴을 커버하는가
- [ ] aria-modal, aria-labelledby 속성이 올바르게 연결되어 있는가
- [ ] size prop의 스타일이 디자인 시스템 토큰과 일치하는가
- [ ] 교체된 호출부에서 onClose 핸들러가 누락되지 않았는가
## Rollback Plan
이 PR은 Parallel Change Expand 단계입니다.
LegacyModal은 그대로 유지되므로 병합 후 문제 발생 시
이 PR만 revert하면 이전 상태로 완전 복구됩니다.
후속 Migrate/Contract PR이 아직 병합되지 않았다면 revert 사이드 이펙트 없음.
## 관련 링크
- 이슈: #4756 (스크린리더 접근성 버그)
- 추적 이슈: #4821 (LegacyModal 전체 교체 Epic)
- 디자인 시스템 명세: [Figma 링크]
- 테스트 결과: [CI 링크]
Draft PR은 코드가 완성되지 않았을 때도 활용합니다. Migrate 단계 작업 중 리뷰어에게 방향성 피드백을 받고 싶을 때, Draft PR로 열어두고 Discussion을 먼저 진행합니다. 이렇게 하면 코드가 완성된 후 방향이 잘못됐다는 피드백을 받는 재작업 비용을 줄일 수 있습니다. 비주얼 리그레션 테스트 연동 전략은 Playwright 비주얼 리그레션 CI 가이드를 참고합니다.
8. Feature Flag 없이 안전하게 병합하는 조건 — 코드 경로 격리, 타입 호환, 테스트 커버리지 게이트
Feature Flag 인프라 없이 리팩터링 PR을 안전하게 병합하려면 세 가지 조건을 충족해야 합니다.
첫째, 코드 경로 격리입니다. 새 컴포넌트와 레거시 컴포넌트의 코드 경로가 서로 간섭하지 않아야 합니다. Parallel Change의 Expand 단계가 이 조건을 자연스럽게 만족합니다. 새 Modal이 추가되더라도 기존 LegacyModal을 호출하는 코드는 변경이 없으므로 런타임 동작 변화가 없습니다.
둘째, 타입 호환입니다. Migrate 단계의 PR에서 호출부를 교체할 때, TypeScript 컴파일러 에러가 없어야 병합 조건을 통과합니다. 타입 시스템이 이 게이트 역할을 수행하게 설계하는 것이 중요합니다. LegacyModal의 show: boolean을 Modal의 isOpen: boolean으로 매핑하는 과정에서 prop 이름만 다르고 타입이 동일하다면 TypeScript가 보장합니다. 타입이 불일치하거나 필수 prop이 누락되면 컴파일 단계에서 실패합니다.
셋째, 테스트 커버리지 게이트입니다. CI 파이프라인에서 커버리지 임계값을 설정하고, 교체된 컴포넌트 호출부에 대한 테스트가 그린(Green)을 유지해야 병합이 허용됩니다. 통합 테스트가 없다면 TDD 프론트엔드 가이드를 참고해 선행 테스트를 작성합니다.
안전 병합의 안티패턴도 명확합니다. 가장 흔한 것은 "일단 병합하고 나중에 고치자"는 접근입니다. Migrate 단계가 절반만 완료된 상태에서 Contract PR을 병합하면, 아직 교체되지 않은 호출부들이 삭제된 LegacyModal을 import하려다 런타임 에러를 냅니다. Contract PR은 반드시 모든 Migrate PR이 병합된 후에 열어야 합니다.
또 다른 안티패턴은 Migrate PR 안에서 컴포넌트 내부 로직을 함께 수정하는 것입니다. 호출부 교체(Migrate)와 동작 변경(Feature)이 같은 PR에 섞이면, 문제 발생 시 원인이 교체 때문인지 로직 변경 때문인지 알 수 없습니다. 섹션 2에서 설명한 Tidy / Feature 구분 원칙이 여기서 다시 적용됩니다.
9. 모노레포에서 크로스 패키지 리팩터링 PR 순서 설계 — 의존성 역방향 순서로 병합
단일 패키지 리팩터링과 달리, 모노레포에서 여러 패키지에 걸친 리팩터링은 PR 병합 순서가 잘못되면 패키지 간 타입 불일치나 런타임 오류가 발생합니다.
핵심 원칙은 의존성 그래프의 역방향 순서로 PR을 병합하는 것입니다. A → B → C 의존성이 있을 때(A가 B를 사용하고 B가 C를 사용), 인터페이스 변경은 C → B → A 순서로 병합합니다. 가장 하위에 있는 패키지(의존성이 없는 패키지)의 새 인터페이스가 먼저 배포되어야, 상위 패키지들이 그것을 참조할 수 있습니다.
예를 들어 디자인 시스템 모노레포에서 @waylog/tokens → @waylog/ui → @waylog/admin 의존 구조라면:
- PR #1:
@waylog/tokens에 새 색상 토큰 추가 (Expand) - PR #2:
@waylog/ui의 컴포넌트가 새 토큰을 참조하도록 업데이트 (Migrate) - PR #3:
@waylog/admin의 로컬 오버라이드 코드 정리 (Contract) - PR #4:
@waylog/tokens에서 구 토큰 삭제 (Contract)
PR #4를 PR #3보다 먼저 병합하면 @waylog/admin이 아직 존재하는 구 토큰을 참조하고 있어 빌드가 깨집니다. Turborepo나 Nx를 쓰는 환경이라면 affected 그래프를 시각화해 이 순서를 자동으로 확인하는 것이 도움이 됩니다.
모노레포 크로스 패키지 리팩터링에서 자주 생기는 또 다른 문제는 패키지 A의 PR과 패키지 B의 PR이 서로를 기다리는 순환 의존입니다. 이런 경우 두 패키지를 임시로 하나의 PR에 묶어 병합하고, 이후 각각으로 분리하는 방법을 쓰기도 합니다. 하지만 이는 최후의 수단입니다. 순환 의존이 등장한다는 것 자체가 패키지 경계 설계를 재검토해야 한다는 신호입니다.
10. 결론: 리팩터링 PR은 기술이 아니라 팀 커뮤니케이션 설계다
코드 품질을 높이는 리팩터링 자체는 기술적 작업입니다. 하지만 그것을 팀이 안전하게 병합할 수 있는 PR로 만드는 것은 커뮤니케이션 설계의 문제입니다. 리뷰어가 무엇을 확인해야 하는지, 각 PR이 실패하면 어떻게 revert하는지, 병합 순서가 왜 이 순서인지 — 이 모든 정보가 PR Description과 커밋 메시지에 담겨야 합니다. 코드 품질만큼 맥락의 품질이 중요합니다.
3주짜리 브랜치를 버리고 처음부터 다시 쪼갰던 그 경험은 고통스러웠지만, 그 이후 우리 팀은 리팩터링 작업을 시작하기 전에 반드시 "이것을 몇 개의 PR로 쪼갤 것인가"를 설계 문서로 정리하는 습관을 만들었습니다. 그 설계 문서가 곧 첫 번째 Draft PR의 Description이 됩니다.
실무 체크리스트 — 거대 PR을 쪼개기 전에 확인할 5가지
- 변경 목록을 Tidy와 Feature로 분류했는가? Tidy 변경은 선행 PR로, Feature 변경은 후행 PR로 분리한다.
- Parallel Change 3단계(Expand → Migrate → Contract)로 나눌 수 있는가? 인터페이스 교체라면 이 흐름이 거의 항상 적용된다.
- 각 PR을 독립적으로 revert할 수 있는가? 병합 후 어떤 PR을 revert해도 시스템이 동작해야 한다.
- 모노레포라면 의존성 역방향 순서로 병합 계획을 세웠는가? 하위 패키지 → 상위 패키지 순서를 문서화한다.
- PR Description에 Why / What / Migration Plan / Reviewer Checklist / Rollback Plan이 모두 포함되어 있는가? 리뷰어가 코드를 보기 전에 컨텍스트를 완전히 이해할 수 있어야 한다.