거대한 PR로 인한 대기 시간을 없애기 위해 큰 변경을 작은 의존 PR로 쪼개는 스택드 PR 워크플로우의 개념, 장점, 그리고 GitHub와 Graphite로 구현하는 방법을 알아봅니다.
URL: https://graphite.com/blog/stacked-prs
이런 상황을 상상해 보세요. 방금 엄청나게 큰 기능을 끝냈습니다. 수십 개 파일에 걸친 수백 줄의 코드. 데이터베이스도 바꾸고, 프런트엔드도 바꾸고, 서버 로직도 바꿨습니다. 마침내 거대한 풀 리퀘스트(PR)를 리뷰로 올렸는데… 막혔습니다. 이 메가 PR이 리뷰되고 머지되기 전까지는 다음 중요한 작업으로 넘어갈 수 없습니다.
그래서 선택지는 둘 다 별로인 두 가지뿐입니다.
이건 가상의 문제가 아닙니다. 모놀리식 PR에 갇힌 팀들이 겪는 현실입니다. 하지만 더 나은 방법이 있습니다. Meta 같은 회사의 최고의 엔지니어링 팀들은 개발 속도를 높이기 위해 수년간 스택드 PR(stacked PRs)을 사용해 왔습니다. 스태킹(stacking)은 큰 변경을 서로 위에 쌓을 수 있는 작고 관리하기 쉬운 PR로 나눔으로써, 개발을 막히지 않게(unblocked) 해줍니다.
한 번이라도 막히지 않는 워크플로우를 경험하면, 다시는 예전 방식으로 돌아가고 싶지 않을 겁니다. 스태킹이 어떻게 동작하는지, 그리고 왜 고성과 팀의 새로운 표준이 되었는지 자세히 알아봅시다.
스택드 풀 리퀘스트는 레고 블록을 쌓는 것과 같습니다. 단단한 기반인 main 브랜치에서 시작합니다. 그 위에 첫 번째 조각을 올립니다. 작고, 집중된 코드 변경입니다. 첫 번째 조각이 리뷰되는 동안, 두 번째 조각을 올리기 시작할 수 있습니다. 각 조각은 바로 이전 조각 위에 쌓이기 때문에, 단계마다 승인을 기다리지 않고도 복잡한 것을 만들 수 있습니다.
이것이 스태킹의 핵심 아이디어입니다. 큰 기능을 매우 작은, 서로 의존하는 풀 리퀘스트들의 연속으로 쪼개는 것입니다. 각 작은 PR은 독립적으로 테스트되고, 리뷰되고, 머지될 수 있어 코드 리뷰 프로세스에서 당신을 완전히 해방시켜 줍니다.
관점을 잡기 위해, 이커머스 결제(checkout) 플로우 예시를 들어 보겠습니다. 수백 줄 코드가 들어 있는 하나의 거대한 PR 대신, 결제 플로우를 여러 개의 작은 PR로 나눌 수 있습니다.
리뷰를 기다리지 않고 각 컴포넌트를 작업할 수 있습니다. PR 2는 PR 1에서 분기하고, PR 3은 PR 2에서 분기하는 식입니다. 이렇게 하면 다음 기능을 만들기 위해 코드가 main에 머지되길 기다릴 필요가 없어집니다.
PR이 작기 때문에 리뷰는 더 빠르고 더 꼼꼼해집니다. 그 결과 버그는 줄고, 지연도 줄어든 채로 코드가 머지됩니다.
스택드 PR 워크플로우를 도입하면 더 빠르게 배포할 수 있게 해주는 핵심 이점들이 생깁니다. 이 이점들을 경험하면, 기존 방식은 믿기 어려울 정도로 느리게 느껴질 겁니다.
스택드 PR 워크플로우에서는 작은 변경으로 첫 번째 PR을 열고 리뷰에 올린 뒤, 즉시 그 위에 다음 브랜치를 만들어 작업을 이어갑니다. 이 접근은 개발 병목을 제거하고 중단 없는 워크플로우를 보장합니다. 기다리는 대신 만들기에 집중할 수 있습니다.
이커머스 예시로 보면:
매 단계에서 승인(approval)을 기다리지 않고 계속 구축할 수 있습니다. 더 이상 1,000줄짜리 PR 하나 때문에 몇 시간, 심지어 며칠씩 막히지 않습니다.
변경을 분리하면 디버깅이 더 단순해집니다. 거대한 올인원 PR이 결제 게이트웨이를 깨뜨렸다면, 20개 파일에 걸친 수백 개 편집을 뒤져 원인을 찾는 건 악몽입니다.
스택드 PR에서는 문제의 원인이 거의 확실히 방금 만든 마지막 작은 변경에 있습니다. 각 PR이 200줄 미만이라면 무엇이 잘못됐는지 찾기 쉽고 되돌리기도 쉽습니다. 결제 플로우 전체를 깨뜨리는 하나의 거대한 변경 대신, 문제를 쉽게 식별하고 고칠 수 있는 작은 점진적 컴포넌트들을 갖게 됩니다.
솔직히 말해 1,000줄짜리 풀 리퀘스트를 리뷰하고 싶은 사람은 없습니다. 그런 PR은 자주 지연되거나, 대충 승인(러버스탬프)되곤 합니다.
스태킹을 하면 리뷰가 집중적이고 효율적으로 바뀝니다. PR이 50개 파일이 아니라 5개 파일만 건드리면, 리뷰어는 범위를 쉽게 이해할 수 있고 더 빠르고 더 높은 품질의 피드백으로 이어집니다. 스택드 세계에서는 결제 버튼, 폼 UI, 주문 처리 로직이 각각 별도의 PR이 됩니다. 각각이 훨씬 소화하기 쉬워져 전체 리뷰 시간이 줄어듭니다.
스택드 워크플로우로 옮기는 것은 변화일 수 있지만, 팀이 익숙해지면 생산성 향상은 상당합니다. 개발자는 리뷰를 기다리는 대신 코드를 작성하는 데 집중하게 됩니다.
전환에는 팀워크가 필요합니다. 시작하는 방법은 다음과 같습니다.
스태킹을 위해 만들어진 도구를 통합하면 전환이 훨씬 빨라질 수 있습니다. Graphite는 커맨드 라인에서 스택을 생성하고 관리하도록 도와주며, 뒤에서 복잡한 부분을 자동화합니다.
기술적으로는 GitHub만으로도 PR을 스택할 수 있지만, 워크플로우는 수동이고 오류가 나기 쉽습니다. 차이를 보기 위해, 부모 PR 하나와 여기에 의존하는 자식 PR 두 개로 나눈 “다크 모드(dark mode)” 기능을 만들어 봅시다.
먼저 초기 부모 PR을 만듭니다.
Terminal
bash// 시작점은 main 브랜치 git checkout -b dark-mode-toggle // 토글 UI 컴포넌트를 구현한 다음 파일을 추가합니다. git add DarkModeToggle.js git commit -m "Feature: Add dark mode toggle UI" // PR 생성 git push -u origin dark-mode-toggle
다음으로 스타일용 PR 하나와 트랜지션용 PR 하나, 이렇게 두 개의 자식 PR을 위에 쌓습니다.
Terminal
bash// 부모 PR을 체크아웃 git checkout dark-mode-toggle // 자식 PR 생성 git checkout -b dark-mode-styles // 다크 모드 스타일과 스타일링 요소 구현 git add DarkModeStyles.css git commit -m "Feature: Styling dark mode" git push origin dark-mode-styles // 두 번째 자식 PR 생성 // dark-mode-styles(첫 번째 자식)로 체크아웃된 상태에서 git checkout -b dark-mode-transitions // 트랜지션 구현 git add DarkModeTransitions.js git commit -m "Feature: Add theme transitions" git push origin dark-mode-transitions
이 수동 프로세스는 즉시 문제를 만듭니다. 리뷰어가 부모 브랜치(dark-mode-toggle)에 변경 요청을 하면, 업데이트를 전파하기 위해 모든 자식 브랜치를 수동으로 리베이스해야 합니다.
Terminal
bash// 부모 브랜치 체크아웃 git checkout dark-mode-toggle // 리뷰어가 부모 브랜치에 코멘트를 남김 // 당신이 변경하고 부모 브랜치에 코드를 푸시함 git add DarkModeToggle.js git commit -am "BugFix: Fix padding based on feedback" git push origin dark-mode-toggle // 이제 상위 변경을 반영해야 함 // 업데이트된 부모 위로 자식 브랜치들을 수동 리베이스 git checkout dark-mode-styles git rebase dark-mode-toggle // 두 번째 자식을 리베이스하고, 모든 브랜치에 대해 계속 반복 git checkout dark-mode-transitions git rebase dark-mode-styles
GitHub 워크플로우는 번거롭습니다. 당신이 책임져야 하는 것은:
이 오버헤드는 코드 작성에 집중하지 못하게 만듭니다. 이제 Graphite가 어떻게 이를 자동화하는지 보겠습니다.
Graphite CLI를 사용하면 브랜치만 만들면 되고, 의존성 관리는 Graphite가 처리합니다. 설치한 뒤 저장소로 이동하면 첫 번째 브랜치를 만들 수 있습니다.
Terminal
bash// 다음 커맨드는 // 현재 있는 브랜치에서 변경 내용을 기반으로 새 브랜치를 만들기 때문에 // 그 전에 파일을 수정해 두세요. gt create -am "Feature: Add dark mode toggle UI"
이 명령은 새 브랜치를 만들고 스테이징된 변경을 커밋합니다. 기능의 다음 부분을 스택하려면, 같은 명령을 다시 실행하면 됩니다. Graphite가 현재 브랜치 위에 새 브랜치를 자동으로 스택합니다.
Terminal
bash// 토글 UI 브랜치에 있는 동안 변경을 만든 다음 // 아래 명령으로 변경을 포함한 새 자식 브랜치를 생성 gt create -am "Feature: Styling dark mode"
부모 브랜치에 대한 피드백을 반영해야 할 때는 gt modify 명령 한 번으로 브랜치를 업데이트하고, 모든 자식을 자동으로 다시 스택(restack)합니다. 수동 리베이스가 필요 없습니다.
Terminal
bash// 리뷰 코멘트를 받은 브랜치를 체크아웃 // 필요한 파일을 수정한 다음 // 아래 명령을 실행 $ gt modify -a [01-04-Feature_Add_dark_mode_toggle_UI f24cf74] Feature: Add dark mode toggle UI 1 file changed, 1 insertion(+) Restacked 01-04-Feature_Styling_dark_mode on 01-04-Feature_Add_dark_mode_toggle_UI. Restacked 01-04-Feature_Add_theme_transitions on 01-04-Feature_Styling_dark_mode.
제출할 준비가 되면, gt submit이 전체 스택을 묶어 원격으로 푸시하고, 올바른 의존성을 가진 모든 PR을 엽니다.
Terminal
bash$ gt submit 🥞 Validating that this Graphite stack is ready to submit... ✔ How would you like to proceed? > Continue with empty branches ✏️ Preparing to submit PRs for the following branches... ▸ 01-04-Feature_Add_dark_mode_toggle_UI (Create) ✔ Title ... Feature: Add dark mode toggle UI ✔ Body > Skip (leave empty) ✔ Submit > Create Draft Pull Request ▸ 01-04-Feature_Styling_dark_mode (Create) ✔ Title ... ✔ Body > Skip (leave empty) ✔ Submit > Create Draft Pull Request ▸ 01-04-Feature_Add_theme_transitions (Create) ✔ Title ... ✔ Body > Skip (leave empty) ✔ Submit > Create Draft Pull Request 📨 Pushing to remote and creating/updating PRs...
CLI는 새 PR의 URL들을 출력하고, Graphite는 리뷰어가 의존 브랜치들을 자동으로 머지하도록 허용하기도 합니다. 전체 과정이 간소화됩니다.
차이는 명확합니다. GitHub는 모든 의존성을 직접 관리하고 리베이스해야 합니다. Graphite는 이 조율을 자동화하여 Git의 기계적인 부분이 아니라 코드에 집중할 수 있게 해줍니다.
스택드 풀 리퀘스트는 모놀리식 PR에 비해 근본적인 개선입니다. 개발자를 막힘 없이 만들고, 코드 품질을 높이며, 더 빠른 혁신으로 이어집니다. 거대한 PR이 만드는 병목을 제거함으로써, 팀은 몰입과 생산성의 상태에 도달할 수 있습니다.
모놀리식 PR을 뒤로할 준비가 되었나요? CLI를 설치하고 몇 분 만에 스태킹을 시작하세요.