- Published on
코드 리뷰와 Merge 파이프라인의 현대 — PR·Merge Queue·Stacked PRs·Monorepo·AI Review·Trunk-Based·Husky·Semgrep 심층 가이드 (2025)
- Authors

- Name
- Youngju Kim
- @fjvbn20031
코드 리뷰는 우리 직업의 절반이다 — 왜 거의 안 얘기하나
엔지니어가 하루에 PR 3~5개를 리뷰한다고 치자. 주 5일, 연 50주면 연간 1,000개에 달한다. 새로운 기능을 만드는 시간보다 남의 코드를 읽고 판단하고 제안하는 시간이 더 많을 수도 있다. 그런데 우리는 코드 리뷰에 대해 놀라울 정도로 적게 말한다. "리뷰 받는 게 무서워서 PR 크기를 키운다", "승인 못 받고 2주 묵힌다", "리뷰어가 못 찾은 버그로 장애 남", "AI 리뷰가 자동 스팸" 같은 일이 매일 일어난다.
2025년 그 풍경이 격변하고 있다. Cursor·Copilot Review·CodeRabbit·Greptile 같은 AI 리뷰어가 "1차 리뷰는 기계가" 시대를 열었고, Graphite·Sapling·Jujutsu가 Stacked PRs를 주류 워크플로로 끌어올렸으며, Merge Queue가 GitHub 기본 기능이 되었다. Monorepo 도구(Nx·Turborepo·Moon·Bazel·Buck2)도 세대 교체됐다. Trunk-based Development가 "이상"에서 "기본"으로 움직였다.
이 글은 그 2025년 코드 리뷰와 Merge 파이프라인을 낱낱이 본다.
이 글은 앞선 Platform Engineering과 Observability 글의 연장선이다. 플랫폼이 "셀프 서비스 제공"이라면, 코드 리뷰는 "코드 변경의 품질 게이트"다.
1부. PR의 사회학 — 블로커가 되지 않는 법
1.1 리뷰가 괴로운 진짜 이유
- 크기가 너무 크다 — 1,000줄 PR은 누구도 제대로 못 봄
- 맥락이 부족 — 왜 이 변경이 필요한지 본문에 안 적혀 있음
- 감정이 실린다 — "이렇게 하면 안 됩니다"는 공격처럼 읽힘
- 비동기 핑퐁 — 한 번 주고받기에 24시간씩 → 1주일 묵힘
1.2 좋은 PR의 4요소
- 작은 단위 — 400줄 이하 권장 (연구상 결함 탐지 효율 최대 지점)
- 한 가지 변경 — 리팩토링과 기능을 섞지 말기
- 맥락을 본문에 — "무엇을, 왜, 어떻게 테스트했는가"
- Self-review 먼저 — 열자마자 작성자가 먼저 코멘트 달기
1.3 좋은 리뷰어의 4원칙 (Google Code Review Guide 요약)
- 원저자의 의도를 이해하고 평가 — 완벽보다 "전보다 나은가"
- 원칙 기반 코멘트 — "I prefer"가 아니라 "이 코드베이스 규칙상"
- 질문으로 시작 — 단정 대신 "이게 의도한 건가요?"
- Blocking vs Nit 구분 —
nit:,question:,blocking:접두사
1.4 Conventional Comments
블로킹 의도와 강도를 접두사로 표시해 감정을 제거.
praise: 테스트 케이스 꼼꼼해요
nitpick: 이름이 더 명확할 수 있음 - userId → userIdentifier
suggestion: 이 부분을 util로 빼면 재사용이 쉬울 것 같습니다
issue: 이 상태에서 race condition 가능 - TOCTOU
thought: 나중에 A/B 테스트가 필요할까요?
question: 이 리트라이 횟수가 3인 이유가 있나요?
2부. Code Owner와 Reviewer Assignment
2.1 CODEOWNERS 파일
# /auth/** 는 security 팀이 필수 리뷰
/auth/** @org/security
/packages/payments/** @org/payments-team
/infrastructure/terraform/** @org/platform
*.md @org/docs
- GitHub·GitLab 기본 지원
- Branch protection과 결합해 "필수 승인자 자동 요청"
2.2 자동 리뷰어 선정 도구
- Pullrequest rotation — GitHub 팀 기반 라운드로빈
- ReviewBot, Toast — 커스텀 알고리즘(경험·현재 부하 고려)
- Graphite Merge — 리뷰어 추천
- 내부 툴: "코드 변경된 파일의 최근 커밋자"를 자동 제안 (Facebook mention_bot 원조)
2.3 리뷰 부하 평형
- 한 시니어에 리뷰가 몰리면 그 시니어가 생산 병목
- 분산: Pair reviewer 강제, 주니어 1 + 시니어 1
- 대시보드로 "열린 리뷰 수" 가시화
3부. Merge Queue — 2024~2025 기본값
3.1 문제
- PR A와 B가 각각 main 기반으로 CI 통과
- A가 먼저 머지
- B는 사실 새 main에서 깨질 수 있음(silent semantic conflict)
- → 머지 후에야 CI 실패 발견
3.2 Merge Queue가 하는 일
- 머지 요청을 큐에 담음
- 큐 헤드에서 "현재 main + 이 PR"을 시뮬레이션 빌드
- 통과하면 실제 머지
- 실패하면 작성자에게 돌려보냄
Google·Facebook이 내부적으로 10년 넘게 써온 방식. 2023년 GitHub 네이티브, 2024년 기본 권장.
3.3 도구들
- GitHub Merge Queue (2023 GA) — 기본 선택
- Mergify — Python 규칙 기반, 복잡한 정책 가능
- Aviator — stacked PR + merge queue
- Graphite — 통합 제품(Stacked PR + Merge + Code Review)
- Bors NG / Trainium — OSS 대안
3.4 Batched Merge
대형 모노레포는 한 번에 여러 PR을 배치. 실패하면 이분 탐색(bisect)으로 원인 PR 골라내기. Meta·Google 수준에서만 필요.
4부. Stacked PRs — 대형 변경을 작게 쪼개는 법
4.1 문제
- 큰 기능을 만들다 보면 한 번에 2,000줄이 쌓임
- 리뷰어가 못 봄 → 대충 승인 → 버그
4.2 해결
변경을 여러 PR로 쌓아서(stack) 각각 작게. 앞 PR이 머지되면 다음 PR이 자동으로 main에 rebase.
main ← PR1 (스키마 추가) ← PR2 (API 추가) ← PR3 (UI 추가)
각 PR을 독립적으로 리뷰. 다만 스택 관리가 수동이면 rebase 지옥.
4.3 도구들
- Graphite (gt) — 업계에서 가장 많이 사용. TypeScript/Python 생태계 지배적
- Sapling (Meta 2022 OSS) — Mercurial 기반, Meta 내부 도구
- Jujutsu (jj) (Google 2023 OSS) — Git 호환, 차세대 후보로 주목
- Spr (Facebook 과거 툴) — CLI
- ghstack (PyTorch 팀)
- git-branchless — 개인용
4.4 Jujutsu가 주목받는 이유
- Git 레포와 호환되며 위에 덮어 씀
- "first-class conflict" — merge conflict가 commit 상태로 남음
- 거대한 revset 쿼리 (
jj log -r 'ancestors(@)') - operation log로 undo 완벽
- 2025년 Google 사내 주력화 계획, 외부 관심 폭발
5부. Monorepo vs Polyrepo — 2025년 결론
5.1 언제 Monorepo가 이기나
- 내부 의존성이 강한 여러 서비스/라이브러리
- 동시 변경(스키마 + API + 클라이언트)이 흔함
- 표준 도구 통일이 중요한 조직 규모
5.2 언제 Polyrepo가 이기나
- 서비스 간 독립성이 정말 높음
- 팀이 완전히 분리됨
- 빌드 도구 일관화 비용이 너무 큼
5.3 실전 합의
Google/Meta는 10만명 monorepo. 스타트업은 "작게 시작 → 크기 커지면 monorepo로 병합"이 흔함. 2024~2025 트렌드는 "서비스는 monorepo, 오픈소스 라이브러리만 별도 레포".
5.4 Monorepo 필수 조건
- 빠른 빌드 캐시 (Remote Cache)
- Affected Detection — 변경된 프로젝트만 빌드/테스트
- Merge Queue — 대용량 PR 병렬 머지
- Code Owner 자동 라우팅
- 규모별 Git 관리 — Git partial clone, Git LFS, VFS
6부. 모노레포 빌드 도구 — Nx·Turborepo·Moon·Bazel·Buck2·Pants·Lerna 종말
6.1 JavaScript/TypeScript 중심
- Nx — Nx Cloud, Graph UI, 플러그인 풍부
- Turborepo (Vercel) — 빠름, 간단, pnpm + Next.js 팀 특화
- Moon (Rust) — 언어 중립 지향, Turbo 경쟁
- Lerna — 공식 deprecated 후 Nx 팀이 인수해서 유지
6.2 언어 중립
- Bazel — Google, 최고 성능·최고 학습 비용
- Buck2 (Meta 2023 OSS, Rust) — Bazel보다 빠르게
- Pants (Twitter) — Python 프로젝트에 특히 적합
- Please — 소형팀
- Earthly — Dockerfile 유사 DSL로 재현 가능 빌드
6.3 선택 트리
- pure JS/TS monorepo, ≤ 50 패키지 → Turborepo
- JS/TS + UI 팀 + 플러그인 필요 → Nx
- 언어가 여럿(JS/Go/Rust), 중형 → Moon
- 초대형, 빌드 엔지니어링 투자 가능 → Bazel 또는 Buck2
- Python 중심 → Pants
6.4 Remote Cache
모든 도구의 공통 승부처. Turborepo/Nx는 Vercel/Nx Cloud, Bazel은 BuildBuddy/Remote Build Execution, Buck2는 자체 프로토콜. 팀에서 5분 걸리던 CI가 30초로 줄어든 사례는 대부분 Remote Cache 덕분.
7부. AI 코드 리뷰 — 2024~2025 폭발
7.1 AI가 잘하는 것
- Null safety, off-by-one, 미사용 변수 같은 정적 분석 범주
- "이 함수 이름이 모호함" 같은 스타일
- 반복되는 패턴 인식 ("이 코드베이스에서는 X를 쓰는데 Y를 썼습니다")
- 테스트 케이스 제안
- 유닛 테스트 커버리지 예상
7.2 AI가 못하는 것
- 아키텍처 수준 판단
- 비즈니스 맥락 ("이 변경이 고객에게 의미 있는가")
- 팀 암묵적 관행
- 보안 컨텍스트(외부 입력이 어디까지 신뢰되는가)
7.3 도구들
- CodeRabbit — PR에 자동 요약 + 코멘트, 무료 오픈소스 모드
- Greptile — 코드베이스 전체 RAG 기반 context-aware 리뷰
- Ellipsis — "PR 설명 자동 생성"이 특히 강함
- Cursor BugBot — Cursor 사용자용
- Copilot Review (GitHub 2024~) — Copilot Enterprise
- Qodo (Codium) — Python/JS 테스트 자동 생성 강점
- Sourcery — Python 리팩토링
- Graphite AI — 스택된 PR에 대한 리뷰
7.4 AI 리뷰 도입 실무 팁
- 일주일 PR에 자동 코멘트만 → 팀이 가치 있는 코멘트 비율을 측정
- 스팸 비율 5% 넘으면 조정 필요 (false positive가 리뷰 문화를 오염시킴)
- AI 승인 권한은 주지 않기 — 인간 리뷰 필수
- PR 요약(summary)만 AI로 쓰는 조직도 많음(가장 안전한 시작점)
8부. Trunk-based Development
8.1 정의
- 모든 엔지니어가 main 근처에서(수시간 내 수명) 작업
- Long-lived feature branch 금지
- 기능을 feature flag로 숨김 (LaunchDarkly/Statsig)
- main은 항상 deployable
8.2 왜 이기나
- Merge conflict가 적어짐
- 배포 주기가 짧아짐 → MTTR 감소
- 지속 통합이 의미 있어짐
- DORA 지표 elite 수준의 필수 조건
8.3 Git Flow의 사망
- nvie/gitflow(2010)가 2020년 작성자에 의해 사실상 deprecated 선언
- GitLab Flow, GitHub Flow가 더 단순한 대안
- 여전히 엔터프라이즈 금융권은 release branch 유지
8.4 Feature Flag 기반 개발
- "배포 = 릴리스"가 분리됨
- A/B 테스트·Gradual Rollout·Kill Switch 하나로
- 관리 안 된 flag가 flag debt — 6개월 이상된 flag 정리 의식
9부. Git 기술 — Rebase·Squash·Linear History
9.1 Merge vs Rebase 논쟁
- Merge commit — 이력 보존, 시간 흐름 그대로
- Rebase — 선형적, bisect에 친화적
- Squash and Merge — PR 하나를 commit 하나로
9.2 팀별 정책
- Linus Torvalds/Linux 커널: rebase 권장, linear
- GitHub 기본: Squash and Merge (가장 단순)
- Google: linear history, rebase 전제
- Meta: Mercurial → Sapling, rebase 네이티브
9.3 공용 브랜치에서 force-push 금지
git rebase 후 git push --force는 팀 공용 브랜치에서 재앙. --force-with-lease로 방어. 최근 GitHub은 Protected Branch에서 force-push 기본 차단.
9.4 Conventional Commits
feat(auth): add passkey support
fix(payments): handle stripe timeout
refactor(db): extract repository interface
chore: bump deps
docs: update README
- 자동 changelog 생성
- semantic-release로 버전 bump
- 팀 규율과 도구 생태계 결합
10부. Pre-commit·Pre-push 훅 — 로컬에서 CI 빠르게
10.1 Hook 매니저
- Husky — JS 생태계 표준, Node 전용
- lefthook — Go 바이너리, 언어 중립, 빠름
- pre-commit (Python) — 언어 중립, 전세계 사용
- Soft-serve, cog — 새 실험
10.2 필수 훅 세트
repos:
- repo: local
hooks:
- id: lint
name: eslint
entry: pnpm lint --fix
language: system
- id: typecheck
entry: pnpm typecheck
language: system
- id: test
entry: pnpm test:affected
language: system
- id: secrets
entry: gitleaks protect --staged
language: system
10.3 훅이 싫어지는 이유와 해결
- 느려서
--no-verify로 우회 → 훅이 10초 안에 끝나야 함 pnpm test:affected같은 변경 파일 전용- staged 파일만 format:
lint-staged - 빌드/테스트는 pre-push에만, commit에는 format/lint만
11부. 정적 분석 — Semgrep·SonarQube·CodeQL·ESLint
11.1 Semgrep
- 언어 중립 패턴 매칭 (YAML 규칙)
- Supply-chain, Secrets, Security 규칙 세트
- Semgrep Cloud Platform (SaaS) — 2023~2024 급성장
11.2 SonarQube / SonarCloud
- Quality Gate, Technical Debt, Coverage
- 엔터프라이즈 표준
- "Clean as You Code" 정책이 최근 권장
11.3 GitHub CodeQL
- 데이터플로우 기반 취약점 분석
- OSS 레포 무료, private 조직 유료
- SQL처럼 쿼리 작성 가능
11.4 ESLint·Biome·Oxlint
- ESLint — JS/TS 표준, 하지만 느림
- Biome (2023 Rome fork) — Rust, linter + formatter 통합, 10~50배 빠름
- Oxlint — Rust, Biome 경쟁, ESLint 규칙 호환
- dprint — Rust, 언어 중립 포매터
11.5 보안 특화
- gitleaks — 커밋 전 secret 탐지
- trufflehog — 깊은 스캔
- Syft, Grype — SBOM + 취약점
- Trivy — 컨테이너·IaC 스캔
12부. CI 속도 — PR 머지가 10분 이하여야 하는 이유
12.1 Compound effect
- CI 1시간 → 개발자가 context switch
- 10분 → "다음 태스크 시작 전에 기다림" 가능
- 5분 이하 → flow 유지
12.2 개선 전략
- Remote Cache (Nx/Turborepo/Bazel)
- Parallel Matrix (5 shard로 테스트 분할)
- Affected-only 빌드
- Docker layer cache
- Warm runner (Depot, BuildJet, Namespace, Blacksmith, RunsOn) — GitHub Actions ARM/X86 + NVMe cache
- Earthly / Docker BuildKit multi-stage
12.3 Flaky Test
- 최악의 생산성 킬러
- 재시도 메커니즘 + 격리 추적
- Trunk.io flaky test detection — 30일 실패율 기반 자동 스킵
- Test retries는 일시방편, 근본 원인 추적 필수
13부. 실전 — 팀 크기별 코드 리뷰 파이프라인
13.1 5명 팀
- GitHub Flow + Squash and Merge
- CODEOWNERS 최소, 필수 리뷰어 1명
- pre-commit hook + ESLint/Prettier
- CI: GitHub Actions + Turborepo
- AI 리뷰: CodeRabbit free
13.2 50명 팀
- Merge Queue 필수
- AI 리뷰 유료 (CodeRabbit/Greptile)
- 내부 Runbook: "PR 400줄 이하 권장"
- 보안 스캔: Semgrep CI + gitleaks
- 모노레포면 Nx + Remote Cache
13.3 500명+
- Graphite/Aviator로 stacked PR 표준화
- Buck2/Bazel Remote Execution
- CodeQL + SonarQube 엔터프라이즈
- Trunk-based + Feature Flag 강제
- 전담 "Dev Productivity" 팀
14부. 체크리스트 12 · 안티패턴 10
✅ 체크리스트 12
- 평균 PR 크기가 400줄 이하인가?
- PR 머지까지 P50 24시간 이하인가?
- CODEOWNERS가 최신이고 작동하는가?
- Merge Queue가 붙어 있어 semantic conflict가 차단되는가?
- Stacked PRs가 팀 일반 워크플로인가?
- Conventional Commits가 적용되는가?
- pre-commit/pre-push 훅이 빠르고(10초 이내) 유용한가?
- Biome/Oxlint 같은 빠른 linter로 전환했는가?
- CI가 평균 10분 이하에 끝나는가?
- Flaky test 탐지/격리 시스템이 있는가?
- AI 리뷰어 스팸 비율이 5% 이하인가?
- Trunk-based + Feature Flag가 표준인가?
⚠️ 안티패턴 10
- 1,000줄 PR을 "검토"했다고 서명
- PR 제목·본문 비어 있음
- Long-lived feature branch 1개월 이상
--no-verify로 훅 습관적 우회- AI 리뷰 승인만 받고 머지
- Merge Queue 없이 동시 머지 → silent conflict
- Flaky test를
if (retryCount < 3)로 덮음 - CODEOWNERS 미관리 → 자동 리뷰어가 유령 계정
- 팀 전체의 리뷰가 한 시니어에게 집중
- Rebase/Squash 정책이 팀 내 불일치 → 이력 혼란
다음 글 예고 — "엔지니어링 블로그의 시대: 기술 글쓰기·RFC·ADR·Design Doc·블로그 운영·커뮤니케이션" — 글을 잘 쓰는 엔지니어가 이기는 이유
코드 리뷰를 얘기했다면 다음은 기술 글쓰기다. RFC, ADR, Design Doc, 사내 위키, 외부 블로그. 글 잘 쓰는 엔지니어는 영향 반경이 10배다.
- Amazon 6-pager 문화 — 왜 파워포인트를 금지했는가
- Design Doc 템플릿 — Google/Stripe 공개본
- RFC 프로세스 — Rust·Ember·IETF 비교
- ADR (Architecture Decision Record)
- Internal Wiki — Notion·Confluence·Outline·GitBook
- Engineering Blog 운영 — Stripe·Shopify·Uber·Airbnb 스타일
- Changelog과 Release Notes
- Slack/Email 비동기 커뮤니케이션
- LLM 시대의 글쓰기 — AI를 도구로 쓰면서 목소리 지키기
- 테크 인플루언서의 경제학 — 글 하나가 커리어를 바꾸는 순간
코드는 결국 사람들 사이에서 살아남는다. 다음 글에서 그 생존 전략을 본다.