모노레포와 가벼운 도구 선택
모노레포와 가벼운 도구 선택
모노레포 (monorepo) 는 여러 프로젝트의 소스코드를 하나의 버전 관리 저장소에 모아 두는 방식. 이 글은 모노레포의 정의 · 역사 · pnpm workspace 같은 가벼운 구성으로 어디까지 가능한지 · Nx · Turborepo · Lerna · Bazel · Rush 같은 풀스케일 도구가 풀어 주는 문제.
1. 모노레포에 대한 이야기
단일 저장소에 여러 패키지 · 서비스를 두는 코드 구성. 반대 개념은 polyrepo (저장소 분리).
대표 사례:
- Google — 단일 저장소 (약 20 억 줄, 사내 도구 Piper) 에서 대다수 프로덕트를 빌드. 사내 빌드 시스템 Blaze 를 외부에 공개한 것이 Bazel (2015).
- Meta (Facebook) — Mercurial 기반의 거대한 모노레포 운영. 빌드 도구 Buck / Buck2 를 공개.
- Microsoft — Windows 같은 거대 코드베이스를 Git 위에서 다루기 위해 Git 가상 파일시스템 (VFS for Git → Scalar) 과 Rush 를 만듦.
2. 실용적 가치
- 여러 패키지에 걸친 변경을 한 PR 로 묶을 수 있음.
- 내부 라이브러리를 npm 등록 없이 상대 경로 / 워크스페이스 로 참조.
- 도구 설정 (
tsconfig· lint 규칙 · CI) 을 한 곳에서 관리.
대신 저장소가 커지면 다음이 어려워짐:
- 어느 패키지가 바뀌었는지 알아내고 영향받는 것만 빌드 / 테스트하기.
- 매번 처음부터 빌드하지 않도록 결과 캐시 공유.
- 패키지 간 의존 그래프 를 그려 올바른 순서로 작업 실행.
이 셋이 풀스케일 모노레포 도구가 해결하는 핵심.
3. pnpm workspace 만으로
pnpm-workspace.yaml 과 "workspace:*" 프로토콜만으로 가능:
- 워크스페이스 의존성 해소 (내부 패키지 참조).
pnpm -r <script>로 모든 패키지에 같은 스크립트 실행.pnpm --filter <name>으로 특정 패키지만 실행.
가능하지 않은 것:
- 의존 그래프 기반 점진 빌드 (바뀐 것만 다시).
- 원격 캐시 (다른 머신 · CI 와 캐시 공유).
- 작업 그래프 시각화 · 코드 생성기.
작은 모노레포 (패키지 5 ~ 10 개, CI 시간이 견딜 만한 수준) 에서는 pnpm workspace 만으로 충분히 운영하는 사례가 많음.
4. 풀스케일 도구
Lerna (2015) — 원래는 npm 패키지 모노레포를 한 번에 퍼블리시하기 위한 도구. 2022 년 Nx 를 만든 Nrwl 로 메인테이너십 이전. 현재는 Nx 의 일부 기능을 흡수한 형태로 유지.
Turborepo (2021) — Jared Palmer 가 시작했고 같은 해 Vercel 이 인수. 이후 Rust 로 재작성. 핵심은 태스크 파이프라인 정의와 로컬 · 원격 캐시. turbo.json 에 태스크 의존 관계를 적으면 변경된 패키지와 영향받는 패키지만 골라 실행하고 결과를 해시 키로 캐시. Vercel 계정과 연결하면 원격 캐시 공유.
Nx (Nrwl, 2017) — Angular CLI 를 만든 사람들이 시작. 태스크 그래프 · 원격 캐시 · 코드 생성기 · 실행 추상을 함께 제공. Turborepo 보다 기능 표면이 넓고 그만큼 학습 곡선이 가파름. JS / TS 외 .NET · Java 플러그인도.
Bazel (Google, 2015 OSS) — 사내 Blaze 를 공개. 언어 비종속, 정확한 입력 추적, 결정적 (reproducible) 빌드, 강력한 원격 캐시 · 원격 실행. 설정 (BUILD 파일) 이 명시적이라 작은 팀에는 비용이 크지만, 거대 모노레포에서는 효과가 분명.
Rush (Microsoft, 2017) — Microsoft 의 대규모 JS / TS 모노레포 경험에서 출발. PNPM / npm / Yarn 어느 것이든 백엔드로 쓸 수 있고, 정책 (policies) 과 변경 로그 워크플로 (rush change) 가 강함.
5. 자주 쓰는 모양
작은 모노레포의 pnpm workspace:
# pnpm-workspace.yaml
packages:
- "apps/*"
- "packages/*"
// apps/web/package.json
{
"name": "web",
"dependencies": {
"@org/ui": "workspace:*"
}
}
pnpm install # 전체
pnpm --filter web dev # web 만
pnpm -r build # 전 패키지 build
Turborepo 도입 시 추가:
// turbo.json
{
"$schema": "https://turbo.build/schema.json",
"tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**", ".next/**"]
},
"test": { "dependsOn": ["build"] }
}
}
6. 자주 걸리는 자리
문제가 작은데 도구가 큰 경우 — 패키지 두세 개에 Bazel 을 도입하면 BUILD 파일 유지 비용이 더 크다는 평가.
원격 캐시가 항상 이득은 아님 — 캐시 무효화 키 (입력 해시) 가 부정확하면 캐시 적중률이 낮거나 잘못 적중해 디버깅을 어렵게.
Lerna 의 인수 이후 정체성이 바뀜 — 오래된 가이드 (2018 ~ 2020) 는 동작이 다를 수 있음.
Turborepo 의 원격 캐시는 기본이 Vercel 계정 — 자체 호스팅이 필요하면 별도 서버 (oss turborepo-remote-cache) 가 필요.
모든 패키지가 강결합 — 폴더가 함께 있어도 의존 방향은 단방향으로 유지하는 편이 유지보수에 유리.
하고픈 말
작은 ~ 중간 모노레포는 pnpm workspace 만으로 충분히 운영 가능합니다. Turborepo · Nx 같은 풀스케일 도구는 패키지 간 의존 그래프와 캐시가 절실해진 시점에 도입할 자리. 모노레포의 진짜 가치는 함께 변경하기 쉬운 것이지, 도구 자체가 아닙니다.
Next
- python-uv
- env-and-secrets
pnpm Workspaces · Turborepo 공식 문서 · Nx 공식 문서 · Bazel 공식 문서 · Rush 공식 문서 · Lerna 공식 문서 · monorepo.tools · Why Google Stores Billions of Lines of Code in a Single Repository (CACM, 2016) 를 참고합니다.