이 글은 누구를 위한 것인가
- 모노레포 빌드 시간이 너무 길어 개선이 필요한 팀
- Turborepo 파이프라인과 캐시 전략을 설계하는 개발자
- CI/CD에서 원격 캐시를 활용해 빌드 속도를 높이려는 팀
들어가며
모노레포에서 패키지가 늘어날수록 빌드 시간은 선형적으로 증가한다. Turborepo는 태스크 의존성 그래프를 분석해 병렬 실행하고, 변경되지 않은 패키지의 결과를 캐시해서 증분 빌드를 실현한다.
이 글은 bluefoxdev.kr의 Turborepo 최적화 가이드 를 참고하여 작성했습니다.
1. Turborepo 파이프라인 설계
[Turborepo 핵심 개념]
Task Pipeline:
- 패키지 간 태스크 의존성 정의
- "^build": 의존 패키지의 build 먼저 실행
- "build": 같은 패키지 내 다른 태스크
Cache 동작:
입력(inputs) 해시 → 캐시 히트 → 결과 복원
inputs: 소스 파일, 환경변수, package.json
outputs: 빌드 결과물 (dist/, .next/ 등)
원격 캐시:
로컬 캐시 (~/.turbo) → 팀 공유 불가
원격 캐시 (Vercel/자체 서버) → CI + 팀원 공유
[모노레포 구조]
apps/
web/ Next.js 앱
api/ Express API
admin/ React 관리자
packages/
ui/ 공유 UI 컴포넌트
config/ ESLint, TypeScript 설정
utils/ 공유 유틸리티
schemas/ Zod 스키마 공유
[의존성 그래프 예시]
web → ui, utils, schemas
api → utils, schemas
admin → ui, utils
turbo build → ui/build, utils/build 병렬
→ schemas/build (utils 완료 후)
→ web/build, api/build, admin/build 병렬
2. Turborepo 구현
// turbo.json - 루트 설정
{
"$schema": "https://turbo.build/schema.json",
"globalDependencies": [
".env",
"tsconfig.json"
],
"globalEnv": [
"NODE_ENV",
"VERCEL_ENV"
],
"tasks": {
"build": {
"dependsOn": ["^build"],
"inputs": [
"$TURBO_DEFAULT$",
".env.local"
],
"outputs": [
"dist/**",
".next/**",
"!.next/cache/**"
]
},
"test": {
"dependsOn": ["^build"],
"inputs": [
"src/**",
"tests/**",
"vitest.config.ts"
],
"outputs": [
"coverage/**"
],
"cache": true
},
"lint": {
"inputs": [
"src/**",
".eslintrc.*",
"eslint.config.*"
],
"outputs": []
},
"typecheck": {
"dependsOn": ["^build"],
"inputs": [
"src/**",
"tsconfig.json"
],
"outputs": []
},
"dev": {
"cache": false,
"persistent": true
}
}
}
# pnpm-workspace.yaml
packages:
- "apps/*"
- "packages/*"
// package.json (루트)
{
"name": "my-monorepo",
"private": true,
"scripts": {
"build": "turbo build",
"dev": "turbo dev",
"lint": "turbo lint",
"test": "turbo test",
"typecheck": "turbo typecheck",
"clean": "turbo clean && rm -rf node_modules"
},
"devDependencies": {
"turbo": "^2.0.0"
},
"packageManager": "pnpm@9.0.0"
}
// packages/ui/package.json
{
"name": "@myapp/ui",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"import": "./dist/index.mjs",
"require": "./dist/index.js",
"types": "./dist/index.d.ts"
}
},
"scripts": {
"build": "tsup src/index.ts --format esm,cjs --dts",
"dev": "tsup src/index.ts --format esm,cjs --dts --watch"
}
}
# 원격 캐시 설정 (Vercel)
# 1. Vercel 원격 캐시 연결
npx turbo login
npx turbo link
# 2. 자체 원격 캐시 서버 (Ducktape)
# .env
TURBO_TOKEN=your-token
TURBO_TEAM=your-team
TURBO_API=https://your-cache-server.com
# 3. CI에서 원격 캐시 활용 (GitHub Actions)
# .github/workflows/ci.yml
name: CI
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 2 # 변경 감지를 위해
- uses: pnpm/action-setup@v4
with:
version: 9
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'pnpm'
- run: pnpm install --frozen-lockfile
# 변경된 패키지만 필터링
- name: Build affected packages
run: pnpm turbo build --filter="...[HEAD^1]"
- name: Test affected packages
run: pnpm turbo test --filter="...[HEAD^1]"
- name: Lint & Typecheck all
run: pnpm turbo lint typecheck
# turbo 주요 CLI 옵션
# 특정 앱만 빌드 (의존성 포함)
turbo build --filter=web
# 변경된 패키지만 (Git 기반)
turbo build --filter="...[HEAD^1]"
# 특정 패키지와 그 의존자들
turbo build --filter="...@myapp/ui"
# 캐시 무시하고 강제 재빌드
turbo build --force
# 병렬 실행 수 제한 (메모리 부족 시)
turbo build --concurrency=4
# 태스크 그래프 시각화
turbo build --graph=graph.svg
# 실행 요약 출력
turbo build --summarize
마무리
Turborepo의 핵심 가치는 두 가지다. 첫째, "dependsOn": ["^build"]로 패키지 간 빌드 순서를 자동 관리하고 병렬 실행으로 최적화한다. 둘째, 원격 캐시로 CI와 팀원 간 빌드 결과를 공유해 "내 로컬에서 빌드된 결과를 CI가 재사용"한다. --filter="...[HEAD^1]"로 PR의 변경 영향권 패키지만 빌드/테스트해서 CI 시간을 극적으로 단축한다.