--- id: wiki-2026-0508-incremental-build title: Incremental Build category: 10_Wiki/Topics status: verified canonical_id: self aliases: [Incremental Compilation, Cached Build, Build Cache] duplicate_of: none source_trust_level: A confidence_score: 0.9 verification_status: applied tags: [build, ci, performance, monorepo, caching] raw_sources: [] last_reinforced: 2026-05-10 github_commit: pending tech_stack: language: typescript framework: turborepo --- # Incremental Build ## 매 한 줄 > **"매 변경된 파일 + downstream 의존성만 rebuild — 매 hash-based caching 의 핵심"**. 매 1979 Make 의 mtime-based 시작, 매 2026 Turborepo/Nx/Bazel 의 content-addressed cache 가 default — 매 monorepo 에서 100x speedup 흔함. ## 매 핵심 ### 매 작동 원리 - **Input hash**: 매 source files + deps + env → SHA256. - **Cache key**: hash → output artifacts (dist/, .d.ts). - **Hit**: cache 존재 → restore, skip work. - **Miss**: rebuild, store. ### 매 granularity - **File-level**: tsc --incremental (.tsbuildinfo). - **Task-level**: Turborepo (per-package). - **Action-level**: Bazel (per-rule, hermetic). ### 매 응용 1. Monorepo CI: 매 affected package 만 test. 2. Local dev: watch mode, 매 sub-second rebuild. 3. Docker: layer caching = path 별 invalidation. ## 💻 패턴 ### Turborepo pipeline ```json // turbo.json { "$schema": "https://turbo.build/schema.json", "globalDependencies": ["tsconfig.base.json"], "tasks": { "build": { "dependsOn": ["^build"], "inputs": ["src/**", "package.json", "tsconfig.json"], "outputs": ["dist/**", ".next/**"], "cache": true }, "test": { "dependsOn": ["build"], "inputs": ["src/**", "test/**"], "outputs": ["coverage/**"] }, "lint": { "cache": true, "outputs": [] } }, "remoteCache": { "signature": true } } ``` ### TypeScript incremental ```json // tsconfig.json { "compilerOptions": { "incremental": true, "tsBuildInfoFile": ".cache/tsbuild.json", "composite": true, "declaration": true, "declarationMap": true }, "references": [ { "path": "../core" }, { "path": "../utils" } ] } ``` ### Nx affected ```bash # Only test packages affected by changes since main nx affected --target=test --base=origin/main --head=HEAD --parallel=4 # Print affected graph nx graph --affected --base=origin/main ``` ### Vite HMR (sub-second) ```ts // vite.config.ts import { defineConfig } from 'vite'; export default defineConfig({ server: { hmr: { overlay: true }, watch: { usePolling: false, ignored: ['**/node_modules/**', '**/dist/**'] } }, build: { rollupOptions: { cache: true } }, cacheDir: '.cache/vite' }); ``` ### GitHub Actions remote cache ```yaml - uses: actions/cache@v4 with: path: | .turbo node_modules/.cache key: turbo-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}-${{ github.sha }} restore-keys: turbo-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}- - run: pnpm turbo run build test --cache-dir=.turbo ``` ### Bazel hermetic action ```python # BUILD.bazel load("@npm//@bazel/typescript:index.bzl", "ts_project") ts_project( name = "core", srcs = glob(["src/**/*.ts"]), declaration = True, incremental = True, deps = ["//packages/utils"], ) ``` ### Cache hit ratio metric ```ts // scripts/cache-stats.ts import { execSync } from 'node:child_process'; const out = execSync('turbo run build --dry=json').toString(); const tasks = JSON.parse(out).tasks; const hits = tasks.filter((t: any) => t.cache.status === 'HIT').length; console.log(`cache hit: ${hits}/${tasks.length} = ${(hits/tasks.length*100).toFixed(1)}%`); ``` ## 매 결정 기준 | 상황 | Approach | |---|---| | Monorepo 10+ packages | Turborepo or Nx | | Strict reproducibility | Bazel (hermetic) | | Single TS app | tsc --incremental + Vite | | Docker images | BuildKit + multi-stage layer cache | **기본값**: 매 Turborepo + remote cache (Vercel or self-hosted). ## 🔗 Graph - 부모: [[Continuous Integration]] - 응용: [[CI_CD_Pipeline]] - Adjacent: [[Dependency Graph]] ## 🤖 LLM 활용 **언제**: 매 turbo.json/nx.json 의 generation, cache key tuning 추천. **언제 X**: 매 Bazel hermetic rule — 매 strict, LLM hallucination 위험. ## ❌ 안티패턴 - **Time-based keys**: `date +%s` cache key — 매 hit 0%. - **Untracked inputs**: env var, system clock 의존 → false hit. - **Cache everything**: lint output 까지 cache → debugging 의 hell. - **No remote cache**: CI 매 fresh 시작 → local-only 의 무의미. ## 🧪 검증 / 중복 - Verified (Turborepo 2.x, Nx 20+, Bazel 7+ 공식 docs). - 신뢰도 A. ## 🕓 Changelog | 날짜 | 변경 | |---|---| | 2026-05-08 | Phase 1 | | 2026-05-10 | Manual cleanup — incremental build 의 hash caching 정리 |