- Published on
빌드 시스템 & 모노레포 툴 2026 완벽 가이드 - Bazel 8 · Pants 2 · Buck2 · Nx 20 · Turborepo 2 · Moon · Lerna 8 · Rush · pnpm Workspaces 심층 분석
- Authors

- Name
- Youngju Kim
- @fjvbn20031
프롤로그 — 빌드 시스템은 왜 다시 뜨거워졌나
2026년 어느 회사의 플랫폼 회의.
주니어: "JS 모노레포라 Turborepo면 충분하지 않나요?" 시니어: "그럼 백엔드 Go랑 데이터팀 Python은 어떻게 한 번에 빌드해?" 주니어: "...아."
이 짧은 대화에 빌드 시스템의 2026년이 들어있다. 한쪽엔 언어별 도구(cargo, gradle, npm 스크립트)가 있고, 다른 한쪽엔 폴리글랏(Bazel, Buck2, Pants)이 있다. 그리고 그 사이에 Nx, Turborepo, Moon 같은 태스크 러너 + 캐시 계열이 끼어든다.
이 글은 2026년 5월 기준의 전체 지형도를 한 번에 정리한다. Bazel 8의 Bzlmod 기본화, Buck2 OSS 안착, Pants 2의 점유율, Nx 20과 Turborepo 2의 경쟁, Moon의 부상, Gradle 8.10 / Maven 4 / sbt 1.10의 자바·스칼라 진영까지. 그리고 한국·일본 빅테크가 실제로 무엇을 쓰는지까지.
1장 · 2026년 빌드 시스템 지도 — 4개 축으로 나누기
빌드 시스템이라는 단어 하나에 너무 많은 것들이 섞여 있다. 정리하는 가장 쉬운 방법은 두 개의 축으로 나누는 것이다.
| 축 1 / 축 2 | 단일 언어 | 폴리글랏 |
|---|---|---|
| 로컬 캐시만 | cargo, npm, go build | GNU Make, just, Task |
| 원격 캐시/실행 | Gradle + Develocity, Nx Cloud, Turborepo Remote Cache | Bazel, Buck2, Pants |
세로축은 언어 지원 범위, 가로축은 캐시·분산 수준이다. 2026년의 주요 흐름은 오른쪽 아래(폴리글랏 + 원격) 칸이 빠르게 채워지고 있다는 점이다. 그리고 그 칸 안에서도 두 진영이 갈린다 — Bazel/Buck2/Pants 진영의 "선언적 그래프 + 해석적 BUILD 파일" 과 Nx/Turborepo/Moon 진영의 "JSON/YAML 설정 + 영향 받은 프로젝트만 실행".
선택의 출발점은 단순하다.
- 단일 언어 + 작은 팀 → 그 언어 기본 툴 (cargo, gradle, npm).
- 단일 언어 + 큰 모노레포 → 그 언어 기본 + 원격 캐시 (Develocity, Nx Cloud).
- JS/TS 위주 + 다중 패키지 → pnpm workspaces / Nx / Turborepo.
- 폴리글랏 + 큰 규모 → Bazel / Buck2 / Pants.
2장 · 모노레포 vs 폴리레포 — 2026년의 답
이 논쟁은 거의 정리됐다. 답은 "둘 다 쓴다, 다만 모노레포가 기본값이 됐다" 다.
모노레포의 장점: atomic 변경 (한 PR로 라이브러리·서비스·테스트 동시 변경), 단일 의존성 그래프, 공유 인프라(빌드·CI·린트), 검색·리팩터링이 IDE 하나로 끝남.
폴리레포의 장점: 권한 격리, 빌드 시간 격리, 팀별 자율성, 작은 리포의 단순함.
2026년 절충: "한 회사 안에 여러 모노레포". 예컨대 platform-monorepo(인프라·플랫폼), product-monorepo(서비스), data-monorepo(파이프라인). 각각은 모노레포지만 회사 전체로 보면 폴리레포. Google/Meta/Microsoft 일부 조직이 이 패턴을 쓴다. 작은 회사라면 그냥 하나의 모노레포가 답이다.
Trunk-based development + feature flags + change detection(아래 21장) 이 세 가지가 결합되면 모노레포의 단점 대부분이 사라진다.
3장 · Bazel 8 — 폴리글랏 빌드의 표준
Bazel은 Google이 내부 빌드 시스템 Blaze를 OSS로 푼 것이다. 2026년 5월 현재 안정 버전은 Bazel 8.x, LTS는 7.x. 핵심 변화는 Bzlmod(MODULE.bazel) 가 기본이 되고 WORKSPACE 모드가 deprecate 된 것이다.
라이선스는 Apache 2.0. 사용처는 Google, X(구 Twitter), Stripe, Pinterest, Spotify, Snap, Dropbox, Coupang, Mercari 등.
핵심 개념.
BUILD.bazel: 디렉터리당 한 개, 그 디렉터리의 빌드 타겟 선언.WORKSPACE/MODULE.bazel: 리포 루트, 외부 의존성·툴체인 선언.- Starlark: Python 부분집합, 빌드 규칙을 작성하는 언어.
- rules: 언어별 규칙 집합 (
rules_go,rules_python,rules_js,rules_rust,rules_java). - sandbox: 모든 액션을 격리된 디렉터리에서 실행, hermetic 보장.
- remote cache / remote execution: 액션 결과를 해시로 캐시, 결과 없으면 원격 워커에서 실행.
가장 단순한 Go 바이너리 빌드.
# BUILD.bazel
load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
go_library(
name = "hello_lib",
srcs = ["main.go"],
importpath = "example.com/hello",
)
go_binary(
name = "hello",
embed = [":hello_lib"],
)
bazel build //cmd/hello:hello
bazel test //...
bazel query 'deps(//cmd/hello:hello)' | head
Bzlmod 의존성 선언.
# MODULE.bazel
module(name = "myrepo", version = "0.1.0")
bazel_dep(name = "rules_go", version = "0.55.0")
bazel_dep(name = "gazelle", version = "0.41.0")
go_deps = use_extension("@gazelle//:extensions.bzl", "go_deps")
go_deps.from_file(go_mod = "//:go.mod")
use_repo(go_deps, "com_github_pkg_errors")
Bazel의 강점은 그래프 + 캐시 + 원격 실행이 깊게 통합되어 있다는 것이다. 약점은 학습 곡선과 BUILD 파일을 손으로 쓰는 부담 (Gazelle 같은 자동 생성기로 완화).
4장 · Buck2 (Meta) — Rust로 다시 쓴 Buck
Buck1은 Meta가 2013년에 만든 Bazel 사촌. 2023년 Meta는 이를 Rust로 처음부터 다시 쓰고 OSS로 풀었다 — Buck2. 2026년 현재 Meta 사내 빌드의 표준이고, OSS 사용처도 늘었다.
Bazel과의 비교.
| 항목 | Bazel | Buck2 |
|---|---|---|
| 구현 | Java | Rust |
| 설정 언어 | Starlark | Starlark |
| 액션 그래프 | rule 평가 시 결정 | Dynamic Dependencies로 빌드 중 결정 가능 |
| 원격 실행 | RE API (BuildBuddy, EngFlow) | RE API (BuildBuddy, EngFlow) |
| 라이선스 | Apache 2.0 | Apache 2.0 |
| 주요 사용처 | Google, X, Stripe | Meta, Discord |
가장 큰 차이는 dynamic dependencies. Bazel은 BUILD 평가 → action graph → 실행이 분리되어 있어 빌드 도중 그래프가 바뀔 수 없다. Buck2는 일부 노드에서 빌드 중 추가 입력을 선언할 수 있어 OCaml .ml/.mli처럼 컴파일 결과에 의존하는 의존 관계를 자연스럽게 표현한다.
# BUCK
load("@prelude//rules.bzl", "cxx_binary", "cxx_library")
cxx_library(
name = "hello_lib",
srcs = ["hello.cc"],
headers = ["hello.h"],
)
cxx_binary(
name = "hello",
srcs = ["main.cc"],
deps = [":hello_lib"],
)
buck2 build //cpp/hello:hello
buck2 test //...
buck2 cquery 'deps(//cpp/hello:hello)'
Bazel에서 마이그레이션할 가치가 있을까? 보통은 없다. 신규 폴리글랏 모노레포 + Meta 영향권 + 동적 의존성이 정말 필요한 경우에만 고려.
5장 · Pants 2 — Python 모노레포의 사실상 표준
Pants v1은 2014년 Twitter가 만든 Java/Scala 빌드 시스템이었다. v2부터는 Toolchain Labs가 처음부터 다시 만들어 Python 우선 폴리글랏으로 포지셔닝한다. 2026년 5월 안정 버전 Pants 2.27.
라이선스 Apache 2.0, 사용처 Slack, IBM, Pico, Toolchain.
Pants 2의 차별점.
- Python-first:
pytest,mypy,ruff,pyright,black,flake8등 Python 도구 일급 지원. - 자동 의존성 추론: import 그래프를 분석해 BUILD 파일에 deps를 자동 추가.
- Lockfile-aware: PEP 621/pyproject.toml과 lockfile 통합.
- Remote caching: BuildBuddy, EngFlow 호환.
# pants.toml
[GLOBAL]
pants_version = "2.27.0"
backend_packages = [
"pants.backend.python",
"pants.backend.python.lint.ruff",
"pants.backend.python.typecheck.mypy",
"pants.backend.docker",
]
[python]
interpreter_constraints = ["==3.12.*"]
# src/python/myapp/BUILD
python_sources(name="lib")
python_tests(name="tests", dependencies=[":lib"])
pex_binary(name="bin", entry_point="myapp.main:main")
pants tailor :: # BUILD 파일 자동 생성
pants lint test check :: # 전체 lint/test/typecheck
pants --changed-since=main test # 변경된 것만
pants package src/python/myapp:bin
Python 모노레포라면 Pants 2가 거의 디폴트 선택지다. mypy/ruff 캐시까지 처리해 주는 게 결정적이다.
6장 · 원격 실행 백엔드 — BuildBuddy / EngFlow / NativeLink
Bazel·Buck2·Pants 모두 Remote Execution API(REAPI) 라는 gRPC 표준을 따른다. 캐시(action result, CAS)와 실행(worker)을 분리해서, 클라이언트는 캐시 미스면 원격 워커에 실행을 위임한다.
2026년 주요 백엔드.
| 제품 | 회사 | 특징 |
|---|---|---|
| BuildBuddy | BuildBuddy Inc | 오픈코어, UI 좋음, GCP/AWS SaaS + 셀프호스트 |
| EngFlow | EngFlow | 엔터프라이즈 포커스, Spotify·Tesla 도입 |
| NativeLink | TraceMachina | OSS, Rust, 학습 단계지만 빠름 |
| Buildbarn | OSS | 오픈소스, 직접 운영 |
BuildBuddy 설정 예 (.bazelrc).
build --bes_results_url=https://app.buildbuddy.io/invocation/
build --bes_backend=grpcs://remote.buildbuddy.io
build --remote_cache=grpcs://remote.buildbuddy.io
build --remote_executor=grpcs://remote.buildbuddy.io
build --remote_header=x-buildbuddy-api-key=YOUR_API_KEY
이 한 블록만으로 캐시 + 실행 + 빌드 이벤트 스트림(BES, 웹 UI에서 빌드 추적) 이 켜진다. 큰 모노레포에선 빌드 시간이 5~10배 줄어드는 경우가 흔하다.
7장 · Nx 20 — JS/TS 모노레포의 강자
Nx는 Nrwl(현 Nx)가 만든 JS/TS 모노레포 도구다. 2026년 5월 현재 Nx 20.x. 라이선스 MIT, 클라우드 호스팅은 Nx Cloud(유료 SaaS).
Nx의 핵심 가치 두 가지.
- 로컬·원격 캐시: 태스크 결과(빌드, 테스트, 린트 출력)를 해시로 캐시.
- affected: git diff 기반으로 변경 영향을 받은 프로젝트만 실행.
npx create-nx-workspace@latest myorg --preset=ts
cd myorg
nx g @nx/react:app web
nx g @nx/node:app api
nx g @nx/js:lib shared
// nx.json
{
"tasksRunnerOptions": {
"default": {
"runner": "nx-cloud",
"options": {
"cacheableOperations": ["build", "lint", "test", "e2e"],
"accessToken": "YOUR_NX_CLOUD_TOKEN"
}
}
},
"namedInputs": {
"default": ["{projectRoot}/**/*"],
"production": ["default", "!{projectRoot}/**/*.spec.ts"]
}
}
nx run-many -t build # 모든 프로젝트 빌드
nx affected -t test # 변경 영향 프로젝트만 테스트
nx graph # 프로젝트 의존성 그래프 시각화
nx release # 버전·changelog·publish
Nx 20의 새 기능: Atomizer(테스트 분할), Custom Conformance Rules(모노레포 규칙 검사), Self-Healing CI(실패한 e2e를 자동 재시도+분석). Lerna 인수 후 통합이 거의 완료되어 Lerna는 Nx의 별칭에 가깝다.
8장 · Turborepo 2 — Vercel의 JS 모노레포 도구
Turborepo는 2021년 Jared Palmer가 만든 모노레포 빌드 시스템. 2022년 Vercel 인수. 2024년 Turborepo 2.0에서 핵심 부분이 Rust로 다시 쓰였고, 2026년 현재 2.x가 안정.
Nx와의 비교.
| 항목 | Nx | Turborepo |
|---|---|---|
| 철학 | "스마트한" 워크스페이스 (생성기·플러그인·그래프) | "단순한" 태스크 러너 + 캐시 |
| 설정 | nx.json + project.json | turbo.json |
| 코드 생성 | 풍부한 generator | 거의 없음 |
| 캐시 | 로컬 + Nx Cloud | 로컬 + Remote Cache (Vercel 기본 무료, 셀프호스트 가능) |
| 분산 실행 | Nx Agents | 없음 (CI shard로 우회) |
| 언어 | JS/TS 중심, 일부 폴리글랏 | JS/TS 전용 |
| 학습 곡선 | 중상 | 낮음 |
// turbo.json
{
"$schema": "https://turbo.build/schema.json",
"tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": [".next/**", "!.next/cache/**", "dist/**"],
"inputs": ["src/**", "package.json", "tsconfig.json"]
},
"test": { "dependsOn": ["^build"], "outputs": ["coverage/**"] },
"lint": { "outputs": [] },
"dev": { "cache": false, "persistent": true }
}
}
turbo run build # 전체 빌드 (캐시 적중분 skip)
turbo run test --filter=...[origin/main] # 변경분만
turbo run dev --parallel # 개발 서버 병렬
turbo prune --scope=web # 특정 앱만 추려서 Docker 빌드
Turborepo의 가장 큰 무기는 단순함과 Vercel 통합. CI에서 TURBO_TOKEN + TURBO_TEAM 환경변수만 주면 Vercel Remote Cache가 자동으로 켜진다. Next.js·Remix 위주의 팀은 Turborepo가 가장 마찰이 적다.
9장 · Moon (moonrepo) — Rust로 만든 폴리글랏 다크호스
Moon은 2022년 등장한 Rust 기반 빌드 시스템. 만든 사람은 Lerna·Yarn 출신의 Miles Johnson. 라이선스 MIT. JS/TS·Python·Rust·Go·Deno·Bun을 한 도구로 묶는 게 목표.
Nx/Turborepo와 다른 점.
- 언어 무관:
lang.toml로 toolchain (Node, Deno, Bun, Rust, Python) 버전 핀. - Project graph: Bazel처럼 디렉터리 단위, 하지만 설정은 YAML.
- Affected: Nx와 동일한 컨셉.
- Mise 통합: 버전 매니저 Mise와 결합해 toolchain 부트스트랩.
# .moon/workspace.yml
projects:
- 'apps/*'
- 'packages/*'
vcs:
manager: 'git'
defaultBranch: 'main'
runner:
cacheLifetime: '7 days'
archivableTargets: ['build', 'test']
# apps/web/moon.yml
language: 'typescript'
type: 'application'
dependsOn:
- 'shared'
tasks:
build:
command: 'next build'
outputs: ['.next']
test:
command: 'vitest run'
moon run web:build
moon ci # 변경 영향 받은 모든 태스크
moon dep-graph
Moon은 아직 작지만, Rust 속도 + 폴리글랏 + YAML 설정 조합이 매력적이다. Bazel은 부담스럽고 Turborepo는 JS 전용이라 답답한 팀이 노릴 만하다.
10장 · Lerna 8 · Rush · pnpm workspaces — JS 모노레포 3 가지 길
여전히 가벼운 JS 모노레포 옵션이 셋 있다.
Lerna 8: 2022년 Nrwl이 인수해 사실상 Nx의 일부가 됐다. 새 프로젝트라면 그냥 Nx를 직접 쓰는 게 낫지만, 기존 Lerna 리포는 Nx 캐시·affected를 그대로 받을 수 있다.
// lerna.json
{ "version": "independent", "npmClient": "pnpm", "useNx": true }
lerna run build --since=origin/main
lerna publish
Rush (Microsoft Rush Stack): pnpm 위에 강한 정책(엄격한 phantom dependency 차단, 변경 파일 검사) 을 얹은 도구. Microsoft, Microsoft Office Online, Azure 일부에서 사용. 큰 JS 모노레포의 엄격함이 필요할 때.
rush install
rush build
rush change # PR 전 변경 파일 작성
rush publish
pnpm workspaces (9.x): 가장 단순. pnpm-workspace.yaml + package.json의 workspaces 필드. 캐시·affected는 없지만 작은 모노레포(20개 미만 패키지) 에 적합.
# pnpm-workspace.yaml
packages:
- 'apps/*'
- 'packages/*'
pnpm install
pnpm -r build # 모든 워크스페이스 빌드
pnpm --filter=@org/web build # 특정 패키지만
pnpm --filter='...[origin/main]' test # 변경 영향만 (pnpm 9+)
선택 가이드: 작은 팀 + JS 전용 + 단순 선호 → pnpm workspaces. 중대형 + Vercel 친화 → Turborepo. 중대형 + 풍부한 도구·정책 → Nx. 대형 + 엄격 정책 + MS 친화 → Rush.
11장 · 자바·코틀린 진영 — Gradle 8.10 + Maven 4
JVM은 자체 빌드 도구 생태계가 두텁다. 2026년 5월 기준 안정 버전.
Gradle 8.10+: Groovy/Kotlin DSL, 의존성 관리, 멀티 프로젝트. 핵심 캐시 기능 두 가지.
- build cache: 태스크 결과 캐시 (로컬 + 원격).
- configuration cache: 빌드 설정 단계 결과 캐시 (대형 프로젝트에서 수 초~수십 초 절약).
- Develocity(구 Gradle Enterprise): 원격 캐시 + 빌드 스캔 + 테스트 인사이트 SaaS.
// build.gradle.kts
plugins {
java
application
id("org.springframework.boot") version "3.4.1"
}
group = "com.example"
version = "0.1.0"
repositories { mavenCentral() }
dependencies {
implementation("org.springframework.boot:spring-boot-starter-web")
testImplementation("org.springframework.boot:spring-boot-starter-test")
}
tasks.test { useJUnitPlatform() }
./gradlew build --build-cache --configuration-cache
./gradlew :api:test
./gradlew dependencies
Maven 4 (2025년 GA): pom.xml의 안정성을 유지하면서 빌드 그래프, 병렬화, daemon (mvnd)을 강화. Spring 생태계의 거대한 관성으로 여전히 1순위. mvnd(Maven Daemon)를 쓰면 JVM 워밍업이 없어 체감 속도가 Gradle 수준에 근접한다.
./mvnw -T 1C clean install # CPU 코어당 1 스레드 병렬
mvnd -T 1C clean install # daemon 모드
JVM 모노레포라면: 신규 프로젝트는 Gradle + Develocity, 기존/SI 프로젝트는 Maven 4 + mvnd가 합리적이다.
12장 · sbt 1.10 · Mill — Scala 진영
Scala는 별도 글에서도 다루지만 빌드 관점에서 짧게.
sbt 1.10: Scala의 디폴트, incremental 컴파일이 강점. 학습 곡선이 가파르다는 평이 영원한 불만.
// build.sbt
ThisBuild / scalaVersion := "3.6.0"
lazy val root = (project in file("."))
.settings(
name := "myapp",
libraryDependencies ++= Seq(
"org.typelevel" %% "cats-effect" % "3.5.4",
"org.scalatest" %% "scalatest" % "3.2.19" % Test,
)
)
Mill (Lihaoyi): Scala/Java/JS 빌드를 Scala로 기술. sbt보다 명료하고 빠르다는 평. 메인스트림은 아니지만 신규 Scala 팀이 종종 채택.
// build.mill
package build
import mill._, scalalib._
object app extends ScalaModule {
def scalaVersion = "3.6.0"
def ivyDeps = Agg(ivy"org.typelevel::cats-effect:3.5.4")
}
Scala 모노레포가 정말 크면 Bazel + rules_scala 가 사실상 유일한 답이지만, 그 단계까지 가는 팀은 극소수다.
13장 · C/C++ — CMake 3.31 + Ninja 1.12 / Meson / Bazel
C/C++는 빌드 시스템 춘추전국 시대가 가장 오래 갔다. 2026년의 정리.
CMake 3.31 + Ninja 1.12: 사실상 표준. CMake는 빌드 시스템이 아니라 빌드 시스템 생성기(Makefile, Ninja, MSBuild 등 생성). Ninja는 그 결과를 가장 빠르게 실행한다.
# CMakeLists.txt
cmake_minimum_required(VERSION 3.31)
project(myapp CXX)
set(CMAKE_CXX_STANDARD 23)
add_library(mylib STATIC src/foo.cc src/bar.cc)
target_include_directories(mylib PUBLIC include)
add_executable(myapp src/main.cc)
target_link_libraries(myapp PRIVATE mylib)
cmake -S . -B build -G Ninja
cmake --build build -j
ctest --test-dir build
Meson + Ninja: CMake의 사촌, 더 깔끔한 문법(Python 비슷). GNOME, systemd 등에서 사용.
Bazel + rules_cc: 대규모 C/C++ 폴리글랏 모노레포의 답. Google·Stripe·Pinterest의 C++ 서비스가 이 경로.
GNU Make: 여전히 작은 프로젝트의 1순위. 학습 곡선이 가장 평탄.
14장 · Rust — Cargo workspaces + cargo-nextest
Rust 모노레포는 거의 모든 경우 Cargo workspaces 로 충분하다.
# Cargo.toml (root)
[workspace]
resolver = "2"
members = ["crates/*", "services/*"]
[workspace.dependencies]
tokio = { version = "1.42", features = ["full"] }
serde = { version = "1.0", features = ["derive"] }
cargo build --workspace
cargo test --workspace
cargo nextest run --workspace # 더 빠른 테스트 러너
cargo metadata --format-version 1 # 의존성 그래프
원격 캐시가 필요하면 sccache(Mozilla) 가 표준. S3·GCS·Redis·memcached·로컬 디스크 백엔드 지원.
export RUSTC_WRAPPER=sccache
export SCCACHE_BUCKET=my-sccache-bucket
cargo build --release
sccache --show-stats
대규모 폴리글랏에 Rust가 끼면 Bazel + rules_rust 도 선택지지만, 도입 부담이 크다. 회사 단위로 Bazel 표준이 없으면 sccache + Cargo가 사실상 정답.
15장 · Go — go modules + gomonorepo 패턴
Go는 모듈 시스템이 단순해서 모노레포가 자연스럽다. 한 리포에 하나의 go.mod를 두고 internal/로 패키지 경계를 긋는 게 표준.
myrepo/
go.mod
cmd/
api/main.go
worker/main.go
internal/
auth/
storage/
pkg/
publicapi/
go build ./...
go test ./...
go vet ./...
go test -count=1 -run TestFoo ./internal/auth
캐시·원격 실행이 필요하면 Bazel + rules_go + gazelle 조합이 가장 성숙. Gazelle는 BUILD.bazel을 go.mod에서 자동 생성한다.
bazel run //:gazelle
bazel run //:gazelle -- update-repos -from_file=go.mod -to_macro=deps.bzl%go_dependencies
Pinterest·Twitter·Coupang의 Go 서비스가 이 경로다.
16장 · Mise (구 rtx) · just · Task — 가벼운 태스크 러너
빌드 시스템과 별개로, 반복 명령을 짧게 부르는 도구가 있다. 셸 스크립트·Makefile의 후계자들.
Mise (구 rtx): asdf 호환 버전 매니저 + 태스크 러너. Rust로 작성. 한 도구로 Node·Python·Go·Rust·Ruby·Bun 버전을 잠그고 태스크까지 정의.
# mise.toml
[tools]
node = "22"
python = "3.12"
go = "1.23"
[tasks.build]
description = "Build all"
run = "pnpm -r build"
[tasks.test]
description = "Run tests"
depends = ["build"]
run = "pnpm -r test"
mise install # 도구 설치
mise run build
mise run test
just: Make의 현대판. 깔끔한 문법, 의존성 없음.
# justfile
default:
@just --list
build:
pnpm -r build
test: build
pnpm -r test
release version:
git tag v{{version}}
git push origin v{{version}}
Task (taskfile.dev): YAML 기반 태스크 러너, Go로 작성.
# Taskfile.yml
version: '3'
tasks:
build:
cmds:
- pnpm -r build
test:
deps: [build]
cmds:
- pnpm -r test
이 셋은 빌드 그래프 도구가 아니라 명령 단축어다. 대형 모노레포에서도 entry point로 쓰기 좋다 — just ci 가 내부적으로 bazel test //...나 nx affected -t test를 부르는 식.
17장 · 원격 캐시 — Bazel BES vs Nx Cloud vs Turborepo Remote Cache
세 도구의 원격 캐시 방식이 다르다.
| 시스템 | 캐시 키 | 저장 위치 | 백엔드 |
|---|---|---|---|
| Bazel | action hash (입력 + 명령 + 환경) | CAS (Content Addressable Storage) | BuildBuddy, EngFlow, Buildbarn |
| Nx Cloud | task hash (입력 + 명령 + 환경) | Nx Cloud SaaS or self-host | Nx Cloud (proprietary) |
| Turborepo Remote Cache | task hash | Vercel SaaS or self-host | Vercel, Turborepo Server (OSS) |
Bazel의 강점: REAPI 표준, 멀티 백엔드, 캐시 hit이 거의 항상 동작 (sandboxing 덕분).
Nx Cloud의 강점: 웹 UI, 실패한 빌드 인사이트, Nx Agents(분산 실행) 통합.
Turborepo의 강점: 설정이 가장 단순(환경변수 2개), Vercel CI와 자동 통합.
Self-host 옵션이 모두 있지만, 운영 부담은 BuildBuddy/Buildbarn >> Nx Cloud (Helm chart) >> Turborepo Server (Docker 컨테이너 하나).
18장 · Hermetic builds + content-addressed cache — 재현성의 본질
"빌드가 어제는 됐는데 오늘 안 됨" 의 근본 원인은 숨어있는 입력(환경변수, 시스템 라이브러리, 시계, 네트워크). 이걸 해결하는 게 hermetic build와 content-addressed cache.
Hermetic: 빌드 액션의 입력을 명시적으로 선언 (소스 + 도구 + 환경변수), 외부 접근 차단 (sandbox). Bazel·Buck2·Pants가 기본으로 한다.
Content-addressed: 입력 해시가 같으면 출력도 같다고 가정. 입력이 1바이트라도 다르면 새로 빌드, 같으면 캐시 적중.
input_hash = sha256(sources + tools + env + command)
output: stored under output_hash
cache_key: input_hash → output_hash
Nix도 같은 철학이다 (Bazel과 Nix는 다른 도구지만 같은 아이디어). 재현 가능한 CI/CD가 진짜 가능해진다.
19장 · 컨테이너 빌드 — BuildKit / Buildah / Kaniko / Earthly
빌드 시스템 글이 컨테이너로 끝나는 이유: 2026년 모든 서비스가 OCI 이미지로 배포되고, 빌드 시스템 출력 → 컨테이너 이미지 변환이 빌드 파이프라인의 마지막 단계다.
| 도구 | 데몬 필요 | 특징 |
|---|---|---|
| Docker BuildKit | 필요(dockerd) | 표준, 캐시 마운트·SBOM·provenance |
| Buildah / Podman build | 불필요 | 데몬리스, Red Hat |
| Kaniko | 불필요 | 컨테이너 안에서 컨테이너 빌드, K8s CI 친화 |
| Earthly | 데몬 사용 | Dockerfile + Makefile 결합, 캐시 강력 |
| ko (Go 전용) | 불필요 | Go 바이너리를 distroless 이미지로 즉시 패키징 |
| Jib (JVM 전용) | 불필요 | Maven/Gradle 플러그인으로 Docker 없이 이미지 빌드 |
Bazel은 rules_oci(구 rules_docker 후속) 로 OCI 이미지를 hermetic하게 만든다. 이미지 레이어가 입력 해시에 따라 동일하게 재현된다.
# BUILD.bazel
load("@rules_oci//oci:defs.bzl", "oci_image")
oci_image(
name = "api_image",
base = "@distroless_base",
entrypoint = ["/api"],
tars = [":api_layer"],
)
20장 · 글로벌 빅테크의 빌드 시스템
| 회사 | 메인 빌드 |
|---|---|
| Blaze (Bazel 사내판) | |
| Meta | Buck2 |
| Microsoft | Rush + 자체 시스템 |
| Amazon | Brazil (자체) + Cargo / Maven |
| X (구 Twitter) | Bazel (Pants에서 마이그레이션) |
| Bazel (Python/Java/Go) | |
| Stripe | Bazel |
| Spotify | Bazel + EngFlow |
| Snap | Bazel |
| Dropbox | Bazel (Python 일부 Pants) |
| Slack | Pants (Python·TS) |
| Vercel | Turborepo |
| Shopify | Bazel (서비스), nx/turborepo (storefront) |
| GitHub | 자체 + Bazel |
| Discord | Buck2 |
폴리글랏 + 대규모는 Bazel/Buck2/Pants 셋으로 수렴한다.
21장 · 한국·일본 사례
한국
- Coupang: Bazel 광범위 사용. Search·Logistics·iOS/Android 일부 Bazel. C++ 검색 인프라가 Bazel hermetic 위에서 돈다.
- Toss / Viva Republica: 프론트엔드 Turborepo. 백엔드는 Gradle 중심.
- Naver: 광범위. NHN/Naver Pay 일부 Bazel, 프론트엔드 팀 다수 Turborepo·Nx.
- Kakao: Gradle/Maven 중심, 프론트는 Turborepo / pnpm workspaces.
- Woowa Brothers (배민): Gradle + Spring 중심. 프론트는 Nx/Turborepo.
- 당근: pnpm workspaces + Turborepo가 표준. 일부 Nx 도입.
일본
- Mercari: Bazel 광범위. Microservices Go/Java를 Bazel + BuildBuddy.
- LINE Yahoo: Bazel(LY), Gradle(LINE 일부). 검색·광고 인프라는 Bazel.
- CyberAgent / Ameba: Nx + Turborepo. Bazel은 일부 광고 플랫폼.
- Recruit / Indeed: Bazel 광범위. Indeed는 monorepo + Bazel 발표 다수.
- DeNA: Gradle 중심 + 일부 Bazel.
- Rakuten: Gradle + Maven, Bazel 일부.
흥미로운 점: 일본 빅테크의 Bazel 채택률이 한국보다 높다. Mercari·Indeed·LY의 영향.
22장 · 언제 무엇을 쓸 것인가 — 결정 트리
마지막으로 정리.
- 소규모 JS 팀 (~5명, ~10 패키지) →
pnpm workspaces단독. - 중규모 JS/TS 팀 (~30명, ~50 패키지) + Vercel/Next.js 위주 →
Turborepo 2+ Vercel Remote Cache. - 중규모 JS/TS 팀 + 풍부한 도구·정책 필요 →
Nx 20+ Nx Cloud. - JS + Python + Go 폴리글랏 모노레포 →
Moon또는Bazel. - Python 모노레포 (데이터·ML 팀) →
Pants 2. - JVM 신규 →
Gradle 8.10+ Develocity. - JVM 레거시/SI →
Maven 4+mvnd. - Rust 모노레포 →
Cargo workspaces+sccache+cargo-nextest. - Go 모노레포 → 단일
go.mod+ 필요시 Bazel + Gazelle. - C/C++ →
CMake + Ninja(작은 팀) 또는Bazel(대규모). - Scala →
sbt또는Mill, 정말 크면Bazel + rules_scala. - 거대 폴리글랏 + 원격 실행 필수 →
Bazel+ BuildBuddy/EngFlow, 또는Buck2.
가장 흔한 실수는 "유행 따라 Bazel 도입했다가 6개월 뒤 후회" 다. 현재 팀의 빌드 시간이 정말 병목이고, 그 병목이 원격 캐시/실행으로 해결되며, 유지보수 인력이 있다 는 세 조건이 맞을 때만 폴리글랏 빌드 시스템을 고려해라. 그렇지 않으면 그 언어의 기본 도구가 거의 항상 옳다.
참고 자료
- Bazel 공식 문서 — https://bazel.build/
- Bazel Bzlmod (MODULE.bazel) — https://bazel.build/external/module
- Buck2 공식 — https://buck2.build/
- Buck2 GitHub — https://github.com/facebook/buck2
- Pants 2 문서 — https://www.pantsbuild.org/
- Nx 공식 문서 — https://nx.dev/
- Nx Cloud — https://nx.app/
- Turborepo 공식 — https://turbo.build/repo
- Turborepo 2 발표 — https://vercel.com/blog/turborepo-2-0
- Moon (moonrepo) — https://moonrepo.dev/
- Lerna 공식 — https://lerna.js.org/
- Rush Stack — https://rushjs.io/
- pnpm Workspaces — https://pnpm.io/workspaces
- Yarn Workspaces — https://yarnpkg.com/features/workspaces
- Gradle 공식 — https://gradle.org/
- Develocity (구 Gradle Enterprise) — https://gradle.com/develocity/
- Apache Maven — https://maven.apache.org/
- mvnd (Maven Daemon) — https://github.com/apache/maven-mvnd
- sbt — https://www.scala-sbt.org/
- Mill — https://mill-build.org/
- CMake — https://cmake.org/
- Ninja — https://ninja-build.org/
- Meson — https://mesonbuild.com/
- BuildBuddy — https://www.buildbuddy.io/
- EngFlow — https://www.engflow.com/
- NativeLink — https://www.nativelink.com/
- Buildbarn — https://github.com/buildbarn
- Mise (구 rtx) — https://mise.jdx.dev/
- just — https://github.com/casey/just
- Task (taskfile.dev) — https://taskfile.dev/
- Cargo Workspaces — https://doc.rust-lang.org/cargo/reference/workspaces.html
- cargo-nextest — https://nexte.st/
- sccache — https://github.com/mozilla/sccache
- Remote Execution API (REAPI) — https://github.com/bazelbuild/remote-apis
- BuildKit — https://github.com/moby/buildkit
- rules_oci — https://github.com/bazel-contrib/rules_oci
- Mercari Engineering / Bazel — https://engineering.mercari.com/en/blog/
에필로그 — "빌드는 결국 그래프"
빌드 시스템 30년의 핵심을 한 줄로 요약하면 "입력 → 출력의 방향 그래프, 그래프의 노드를 최대한 재사용해라" 다. Make가 했던 일을 Bazel은 sandboxing + 원격 실행으로, Turborepo는 JS만의 단순한 형태로, Pants는 Python 친화적으로 다시 쓰고 있다.
도구는 바뀌어도 "내 빌드의 입력을 정확히 안다, 출력을 캐시한다, 같은 입력이면 다시 안 한다" 는 원칙은 그대로다. 2026년의 폴리글랏 빌드 시스템은 이 원칙을 여러 언어·여러 머신에 걸쳐 적용한다. 작은 팀이라면 그 원칙을 가장 가까이서 구현한 도구(pnpm + Turborepo, Cargo, Gradle)로 충분하고, 큰 팀은 어쩌면 Bazel을 피할 수 없을지도 모른다.
마지막으로 한 가지 — 빌드 시스템의 성공은 "누가 BUILD 파일을 유지보수하느냐" 에 달려 있다. 도구가 아니라 사람의 문제다. Bazel을 도입하든 Nx를 도입하든, 빌드 인프라 전담자 1~2명이 없으면 절반 정도만 성공한다. 그 인력 예산이 없다면, 그 회사에 맞는 빌드 시스템은 더 단순한 도구일 가능성이 높다.