Turborepo 빌드 속도 10배 빠르게 하는 5가지 최적화 기법
이 글의 핵심
Turborepo 빌드 시간을 극적으로 단축하는 5가지 실전 최적화 기법을 벤치마크와 함께 정리합니다.
문제: “Turborepo인데 왜 이렇게 느리죠?”
Turborepo를 도입했는데도 빌드가 느리다면? 제대로 최적화하지 않았을 가능성이 큽니다.
실제 사례:
최적화 전: 전체 빌드 10분
최적화 후: 전체 빌드 1분
━━━━━━━━━━━━━━━━━━━━━━━━━━━━
개선: 10배 향상 ⚡
이 글에서는 실전에서 검증된 5가지 최적화 기법을 다룹니다.
최적화 1: 캐시 설정 완벽하게 하기
문제: outputs 설정 누락
가장 흔한 실수입니다. outputs를 제대로 설정하지 않으면 캐시가 작동하지 않습니다.
// ❌ 잘못된 설정
{
"pipeline": {
"build": {
"outputs": [] // 캐시 안 됨!
}
}
}
// ✅ 올바른 설정
{
"pipeline": {
"build": {
"outputs": [
".next/**",
"!.next/cache/**",
"dist/**",
"build/**"
]
}
}
}
체크리스트:
{
"pipeline": {
"build": {
"outputs": [
".next/**", // Next.js
"dist/**", // 일반 빌드
"build/**", // CRA, Vite
".nuxt/**", // Nuxt
"out/**", // Next.js export
"public/build/**" // Remix
]
},
"test": {
"outputs": [
"coverage/**" // 테스트 커버리지
]
}
}
}
효과:
캐시 미설정: 매번 10분
캐시 설정: 첫 빌드 10분, 이후 10초
━━━━━━━━━━━━━━━━━━━━━━━━━━━━
개선: 60배 향상 🚀
최적화 2: 의존성 그래프 최적화
문제: 불필요한 의존성
// ❌ 모든 것이 모든 것에 의존
{
"pipeline": {
"build": {
"dependsOn": ["^build", "lint", "test"] // 너무 많음!
}
}
}
// ✅ 필요한 것만 의존
{
"pipeline": {
"build": {
"dependsOn": ["^build"] // 상위 패키지 빌드만
},
"deploy": {
"dependsOn": ["build", "test"] // 배포 시에만 테스트
}
}
}
의존성 최소화 전략:
{
"pipeline": {
// 빌드: 상위 패키지만 의존
"build": {
"dependsOn": ["^build"]
},
// 린트: 독립 실행
"lint": {
"dependsOn": []
},
// 테스트: 빌드 후 실행
"test": {
"dependsOn": ["build"]
},
// 타입 체크: 상위 빌드만
"type-check": {
"dependsOn": ["^build"]
}
}
}
효과:
과도한 의존성: 순차 실행 (느림)
최소 의존성: 병렬 실행 (빠름)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━
개선: 3-5배 향상
최적화 3: Remote Cache 활용
Vercel Remote Cache (무료)
# 1. Vercel 계정 연결
npx turbo login
# 2. 프로젝트 연결
npx turbo link
# 3. 빌드 (자동으로 Remote Cache 사용)
turbo build
효과:
시나리오: 팀원 A가 빌드 → 팀원 B가 빌드
Remote Cache 없음:
- A: 10분
- B: 10분
- 총: 20분
Remote Cache 있음:
- A: 10분 (캐시 생성)
- B: 10초 (캐시 다운로드)
- 총: 10분 10초
━━━━━━━━━━━━━━━━━━━━━━━━━━━━
팀 전체 시간 절약: 50%
자체 Remote Cache 서버
# Docker로 간단히 구축
docker run -d -p 8080:8080 \
-v turborepo-cache:/cache \
ghcr.io/fox1t/turborepo-remote-cache
# turbo.json에 설정
{
"remoteCache": {
"url": "http://localhost:8080"
}
}
최적화 4: 병렬 실행 극대화
문제: 순차 실행
// ❌ 순차 실행 (느림)
{
"pipeline": {
"build": {
"dependsOn": ["lint", "test", "type-check"]
}
}
}
lint (2분) → test (3분) → type-check (1분) → build (4분)
총 10분
해결: 병렬 실행
// ✅ 병렬 실행 (빠름)
{
"pipeline": {
"build": {
"dependsOn": ["^build"]
},
"lint": {},
"test": {},
"type-check": {}
}
}
lint (2분) ┐
test (3분) ├─ 병렬 실행 → build (4분)
type-check (1분) ┘
총 7분 (3분 절약)
병렬 실행 명령어:
# 여러 태스크 동시 실행
turbo run lint test type-check --parallel
# 모든 패키지 동시 빌드
turbo run build --parallel
# 동시 실행 개수 제한 (CPU 과부하 방지)
turbo run build --concurrency=4
최적화 5: 선택적 빌드 (필터링)
변경된 패키지만 빌드
# 변경된 패키지만 빌드
turbo run build --filter=...[HEAD^1]
# 특정 패키지와 의존성만 빌드
turbo run build --filter=web...
# 특정 패키지만 빌드 (의존성 제외)
turbo run build --filter=web
# 여러 패키지 선택
turbo run build --filter=web --filter=docs
# 특정 경로 변경 시에만
turbo run build --filter=./apps/*
실전 예시:
# PR 빌드: 변경된 것만
turbo run build test --filter=...[origin/main]
# 특정 앱 배포: 해당 앱만
turbo run build --filter=web...
# 패키지 변경: 영향받는 앱만
turbo run build --filter=...@ui
효과:
전체 빌드: 10분
필터링 빌드: 2분
━━━━━━━━━━━━━━━━━━━━━━━━━━━━
개선: 5배 향상
종합 최적화 전략
Before: 최적화 전
// turbo.json (최적화 전)
{
"pipeline": {
"build": {
"outputs": [], // ❌ 캐시 안 됨
"dependsOn": ["lint", "test"] // ❌ 순차 실행
}
}
}
# 빌드 명령
turbo run build
# 결과:
# lint: 2분
# test: 3분
# build: 5분
# 총: 10분 (캐시 없음)
After: 최적화 후
// turbo.json (최적화 후)
{
"pipeline": {
"build": {
"outputs": [".next/**", "dist/**"], // ✅ 캐시 활성화
"dependsOn": ["^build"] // ✅ 최소 의존성
},
"lint": {}, // ✅ 병렬 실행
"test": {} // ✅ 병렬 실행
}
}
# 빌드 명령
turbo run build lint test --parallel
# 결과:
# lint, test, build 병렬 실행: 5분
# 캐시 히트 시: 10초
# 총: 첫 빌드 5분, 이후 10초
개선 효과:
최적화 전: 10분
최적화 후: 10초 (캐시 히트)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━
개선: 60배 향상 ⚡⚡⚡
실전 벤치마크
실제 프로젝트 (10개 패키지, Next.js + React) 기준:
| 최적화 단계 | 빌드 시간 | 개선율 |
|---|---|---|
| 기본 설정 | 10분 | - |
| + outputs 설정 | 10분 → 2분 (캐시 히트) | 5배 |
| + 의존성 최적화 | 10분 → 6분 | 1.7배 |
| + 병렬 실행 | 6분 → 4분 | 1.5배 |
| + Remote Cache | 4분 → 30초 (팀 캐시) | 8배 |
| + 필터링 | 4분 → 1분 (변경분만) | 4배 |
| 종합 | 10분 → 30초 | 20배 |
체크리스트
빌드가 느리다면 이것들을 확인하세요:
캐싱:
-
outputs에 빌드 결과물 경로 설정했나? -
.turbo폴더가.gitignore에 있나? - Remote Cache 연결했나? (팀 프로젝트)
의존성:
-
dependsOn에 불필요한 태스크 없나? - 순환 의존성 없나?
-
^build만 의존하도록 단순화했나?
병렬화:
- 독립적인 태스크는 병렬 실행하나?
-
--parallel플래그 사용하나? -
--concurrency설정 적절한가?
필터링:
- CI에서 변경분만 빌드하나?
- 특정 앱 배포 시 필터링하나?
즉시 적용 가능한 설정
최적화된 turbo.json
{
"$schema": "https://turbo.build/schema.json",
"globalDependencies": ["**/.env.*local"],
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": [
".next/**",
"!.next/cache/**",
"dist/**",
"build/**"
],
"env": [
"NEXT_PUBLIC_*",
"VITE_*"
]
},
"dev": {
"cache": false,
"persistent": true
},
"lint": {
"outputs": []
},
"test": {
"outputs": ["coverage/**"],
"dependsOn": []
},
"type-check": {
"dependsOn": ["^build"],
"outputs": []
}
}
}
package.json 스크립트
{
"scripts": {
"build": "turbo run build",
"build:changed": "turbo run build --filter=...[HEAD^1]",
"dev": "turbo run dev --parallel",
"lint": "turbo run lint --parallel",
"test": "turbo run test --parallel",
"clean": "turbo run clean && rm -rf node_modules",
"clean:cache": "rm -rf .turbo"
}
}
CI/CD 최적화 (GitHub Actions)
name: CI
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
# pnpm 캐시
- uses: pnpm/action-setup@v2
with:
version: 8
- uses: actions/setup-node@v3
with:
node-version: 18
cache: 'pnpm'
- run: pnpm install --frozen-lockfile
# Turborepo Remote Cache
- name: Build
run: pnpm turbo run build --filter=...[HEAD^1]
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
# 변경된 것만 테스트
- name: Test
run: pnpm turbo run test --filter=...[HEAD^1]
CI 빌드 시간:
최적화 전: 15분
최적화 후: 2-3분 (캐시 + 필터링)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━
개선: 5-7배 향상
고급 최적화
1. 증분 빌드 활용
{
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": [".next/**"],
"cache": true,
"inputs": [
"src/**",
"public/**",
"package.json",
"next.config.js",
"tsconfig.json"
]
}
}
}
inputs를 명시하면 해당 파일 변경 시에만 재빌드합니다.
2. 환경 변수 캐시 키
{
"pipeline": {
"build": {
"env": [
"NEXT_PUBLIC_API_URL",
"NEXT_PUBLIC_ENV"
]
}
}
}
환경 변수가 바뀌면 캐시 무효화됩니다.
3. 선택적 캐싱
{
"pipeline": {
"dev": {
"cache": false, // 개발 모드는 캐시 안 함
"persistent": true
},
"build": {
"cache": true // 빌드만 캐시
}
}
}
성능 모니터링
빌드 분석
# 상세 로그
turbo run build --verbosity=2
# 타이밍 정보
turbo run build --profile=profile.json
# 캐시 상태 확인
turbo run build --dry-run
병목 지점 찾기
# 각 패키지 빌드 시간 확인
turbo run build --graph
# 의존성 그래프 시각화
turbo run build --graph=graph.html
실전 팁
1. 로컬 개발 최적화
# 변경된 앱만 빌드
turbo run build --filter=web...
# 병렬 dev 서버
turbo run dev --parallel --filter=web --filter=api
2. CI 최적화
# PR: 변경분만
turbo run build test --filter=...[origin/main]
# main: 전체 빌드 + 캐시 갱신
turbo run build test
3. 배포 최적화
# 특정 앱만 빌드 및 배포
turbo run build --filter=web...
cd apps/web && vercel deploy
요약
5가지 최적화 기법
- 캐시 설정:
outputs완벽하게 설정 → 60배 향상 - 의존성 최적화: 불필요한
dependsOn제거 → 3-5배 향상 - Remote Cache: 팀 간 캐시 공유 → 팀 전체 50% 절약
- 병렬 실행:
--parallel플래그 → 2-3배 향상 - 필터링: 변경분만 빌드 → 5배 향상
종합 효과
최적화 전: 10분
최적화 후: 30초 (캐시 히트)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━
개선: 20배 향상 🚀🚀🚀
즉시 실행 체크리스트
오늘 당장:
-
turbo.json에outputs설정 - 불필요한
dependsOn제거 -
--parallel플래그 추가
이번 주:
- Remote Cache 연결
- CI에 필터링 적용
- 빌드 시간 측정 및 비교
이번 달:
- 팀 전체 Remote Cache 도입
- 의존성 그래프 최적화
- 모니터링 대시보드 구축
더 알아보기
- Turborepo 완벽 가이드 - 기본부터 고급까지 전체 내용
- pnpm Workspace 가이드 - 패키지 매니저 최적화
- Monorepo 아키텍처 설계 - 구조 설계 베스트 프랙티스
Turborepo로 빌드 시간을 극적으로 단축하세요! 🚀