Use Cases (업데이트: 2026. 6. 2.)

Claude Code와 Turborepo로 빠른 Monorepo 빌드 만들기

Claude Code로Turborepo monorepo를 설계하고 tasks, 캐시, CI, 프롬프트와 함정을 정리합니다.

Claude Code와 Turborepo로 빠른 Monorepo 빌드 만들기

Claude Code와 Turborepo를 함께 쓰는 이유

Monorepo는 여러 앱과 공통 package를 하나의 저장소에서 관리하는 방식입니다. 처음에는 apps/webpackages/ui 정도라서 단순하지만, 관리자 앱, 문서 사이트, 공통 유틸리티, 여러 배포 대상이 생기면 빌드 순서와 의존성 경계, CI 시간, 캐시 정책이 곧 문제가 됩니다.

Turborepo는 task graph와 캐시로 이 문제를 정리합니다. Claude Code는 단순히 설정 파일을 만들어 주는 도구가 아니라, 기존 package.json을 읽고 package 간 관계를 설명하며, 누락된 outputs와 위험한 CI 명령을 찾아 주는 리뷰어로 쓸 때 효과가 큽니다.

이 글은 2026년 6월 2일 기준 Turborepo v2를 전제로 합니다. 현재 turbo.json에서는 예전 pipeline이 아니라 tasks를 사용합니다. 실제 적용 전에는 Turborepo configuration, turbo run, Remote Caching, Claude Code memory를 확인하세요.

관련 배경은 monorepo 관리, pnpm workspace와 Claude Code, Claude Code CI/CD 설정과 함께 보면 좋습니다.

목표 구조와 세 가지 사용 사례

목표는 pnpm workspace 위에 Turborepo를 올린 작은 TypeScript monorepo입니다. 처음부터 거대한 shared package를 만들기보다, Claude Code가 이해할 수 있는 책임 단위로 나누는 것이 중요합니다.

flowchart LR
  web["apps/web\n고객용 앱"] --> ui["packages/ui\n공통 UI"]
  admin["apps/admin\n관리 앱"] --> ui
  web --> utils["packages/utils\n공통 함수"]
  admin --> utils
  ui --> tsconfig["packages/tsconfig\nTS 설정"]
  utils --> tsconfig

첫 번째 사례는 고객용 화면과 관리자 화면이 같은 버튼, 폼, 입력 검증을 쓰는 제품입니다. packages/uipackages/utils를 분리하면 Claude Code가 화면 컴포넌트와 업무 함수를 섞는 실수를 줄일 수 있습니다.

두 번째 사례는 콘텐츠 사이트나 문서 플랫폼입니다. 랜딩 페이지, 문서, 내부 대시보드가 한 저장소에 있으면 매 PR마다 전체 빌드를 돌리는 방식이 점점 느려집니다. --affected--filter는 변경된 package와 그 영향을 받는 package만 검증하게 해 줍니다.

세 번째 사례는 매출 전환이 중요한 SaaS입니다. 가격 페이지, 가입 폼, 계정 설정, 관리자 기능은 작은 공통 package를 자주 공유합니다. lint, type-check, test, build가 안정적이면 Claude Code가 CTA나 onboarding을 수정해도 CI가 깨진 공유 코드를 막아 줍니다.

복사해서 시작할 최소 구조

먼저 디렉터리 모양을 고정합니다. 핵심은 package 수가 아니라 책임의 선명도입니다.

acme-monorepo/
  apps/
    web/
      package.json
      src/
    admin/
      package.json
      src/
  packages/
    ui/
      package.json
      src/
    utils/
      package.json
      src/
    tsconfig/
      package.json
      base.json
  pnpm-workspace.yaml
  package.json
  turbo.json
  CLAUDE.md

pnpm-workspace.yaml은 다음처럼 간단하게 둡니다.

packages:
  - "apps/*"
  - "packages/*"

루트 package.json에는 팀이 반복해서 쓰는 Turborepo 명령을 모읍니다. 2026년 6월 기준으로 확인한 공개 버전은 turbo@2.9.16pnpm@11.5.1입니다. 실제 프로젝트에서는 lockfile로 재현성을 보장하세요.

{
  "name": "acme-monorepo",
  "private": true,
  "packageManager": "pnpm@11.5.1",
  "scripts": {
    "dev": "turbo dev",
    "build": "turbo run build",
    "lint": "turbo run lint",
    "test": "turbo run test",
    "type-check": "turbo run type-check",
    "check": "turbo run lint type-check test build",
    "check:affected": "turbo run lint type-check test build --affected"
  },
  "devDependencies": {
    "turbo": "^2.9.16",
    "typescript": "^5.8.3"
  }
}

내부 package는 workspace:*를 사용합니다. 이는 같은 workspace 안의 package를 사용하라는 뜻이며, 실수로 registry의 동명 package를 받는 일을 막습니다.

{
  "name": "@acme/ui",
  "version": "0.1.0",
  "private": true,
  "type": "module",
  "main": "./dist/index.js",
  "types": "./dist/index.d.ts",
  "scripts": {
    "build": "tsc -p tsconfig.json",
    "lint": "eslint src --max-warnings=0",
    "type-check": "tsc -p tsconfig.json --noEmit",
    "test": "vitest run"
  },
  "devDependencies": {
    "@acme/tsconfig": "workspace:*",
    "typescript": "^5.8.3",
    "vitest": "^3.1.0",
    "eslint": "^9.25.0"
  }
}

turbo.json에는 tasks와 outputs를 명시한다

루트의 turbo.json은 저장소의 작업 계약서입니다. tasks 안의 각 키는 package별 package.json에 있는 같은 이름의 script와 연결됩니다. ^build는 현재 package를 빌드하기 전에 의존 package의 build를 먼저 끝낸다는 뜻입니다.

{
  "$schema": "https://turborepo.dev/schema.json",
  "globalDependencies": ["pnpm-lock.yaml", "tsconfig.base.json", ".env.example"],
  "globalEnv": ["NODE_ENV"],
  "tasks": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": ["dist/**", ".next/**", "!.next/cache/**", "out/**"]
    },
    "lint": {
      "dependsOn": ["^build"],
      "outputs": []
    },
    "type-check": {
      "dependsOn": ["^build"],
      "outputs": []
    },
    "test": {
      "dependsOn": ["build"],
      "outputs": ["coverage/**"]
    },
    "dev": {
      "cache": false,
      "persistent": true
    }
  }
}

outputs는 캐시에서 복원해도 되는 결과물입니다. 너무 좁으면 캐시 히트가 의미 없고, 너무 넓으면 임시 파일과 프레임워크 내부 캐시까지 저장됩니다. Next.js라면 .next/cache/**는 제외하는 쪽이 보통 안전합니다.

앱마다 산출물이 다르면 package 내부에 turbo.json을 둘 수 있습니다. 배열 필드는 기본적으로 루트 설정을 대체하므로, 기존 값을 유지하며 추가하려면 $TURBO_EXTENDS$를 첫 항목에 둡니다.

{
  "extends": ["//"],
  "tasks": {
    "build": {
      "outputs": ["$TURBO_EXTENDS$", ".next/**", "!.next/cache/**"],
      "env": ["NEXT_PUBLIC_API_URL"]
    }
  }
}

CI와 검증 명령을 고정한다

Turborepo의 장점은 CI에서 크게 드러납니다. 다만 --affected는 Git 비교 이력이 필요합니다. GitHub Actions에서 얕은 checkout을 쓰면 모든 package가 변경된 것처럼 보일 수 있으므로 fetch-depth: 0을 사용합니다.

name: turbo-ci

on:
  pull_request:
  push:
    branches: [main]

permissions:
  contents: read

jobs:
  verify:
    runs-on: ubuntu-latest
    env:
      TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
      TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - uses: pnpm/action-setup@v4
        with:
          version: 11.5.1
      - uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: pnpm
      - run: pnpm install --frozen-lockfile
      - run: pnpm turbo run lint type-check test build --affected
      - run: pnpm turbo run build --dry=json > turbo-plan.json
      - uses: actions/upload-artifact@v4
        if: always()
        with:
          name: turbo-plan
          path: turbo-plan.json

Remote Cache를 쓴다면 로컬에서 pnpm dlx turbo login, pnpm dlx turbo link를 실행하고, CI에는 TURBO_TOKENTURBO_TEAM을 secret으로 넘깁니다. 로그도 artifact처럼 다뤄질 수 있으므로 API key, 고객 정보, session token을 build script에서 출력하지 않는 규칙도 필요합니다.

자주 쓰는 명령은 CLAUDE.md에 적어 두면 Claude Code가 매번 추측하지 않습니다.

pnpm turbo run build --dry=json
pnpm turbo run build --filter=@acme/web...
pnpm turbo run test --filter=...[origin/main]
pnpm turbo run lint --filter=!./apps/docs
pnpm turbo run build --cache=local:rw,remote:r
pnpm turbo run build --force

Claude Code에 줄 프롬프트

Claude Code에는 경계, 금지 사항, 검증 명령을 함께 줍니다. 그래야 단순 생성이 아니라 monorepo 리뷰에 가까운 결과가 나옵니다.

이 저장소는 Turborepo v2 + pnpm workspace monorepo입니다.

경계:
- apps/* 는 배포 가능한 애플리케이션입니다.
- packages/ui 는 시각 컴포넌트만 가집니다.
- packages/utils 는 프레임워크 독립 함수만 가집니다.
- packages/* 는 apps/* 에 의존하면 안 됩니다.
- turbo.json 은 pipeline이 아니라 tasks를 사용해야 합니다.

작업:
1. package.json과 turbo.json을 읽고 task dependency graph를 설명하세요.
2. 캐시 outputs가 빠졌거나 의심스러운 곳을 지적하세요.
3. 수정이 필요하면 최소한의 안전한 diff로 변경하세요.
4. 마지막에 아래 명령을 실행하고 결과를 보고하세요.

pnpm turbo run lint type-check test build --affected
pnpm turbo run build --dry=json

이 템플릿은 Claude Code가 불필요하게 큰 리팩터링을 하는 일을 줄입니다. packages/ui에 HTTP 호출을 넣거나, packages/utils에서 Next.js를 import하거나, 전체 빌드만 CI 전략으로 제안하는 문제도 줄일 수 있습니다.

자주 생기는 함정

첫째, 오래된 pipeline 예제를 복사하는 일입니다. Turborepo v2는 tasks를 사용합니다. 마이그레이션 중이라면 Claude Code에 공식 문서와 대조해 변경 키를 설명하게 하세요.

둘째, 캐시 범위를 너무 넓게 잡는 일입니다. node_modules/**, 임시 로그, 배포 산출물이 아닌 내부 캐시는 outputs에 넣지 않는 편이 안전합니다.

셋째, 얕은 Git 이력에서 --affected를 쓰는 일입니다. base와 head가 checkout에 없으면 올바른 package 집합을 계산할 수 없습니다.

넷째, Claude Code에 한 번에 너무 많은 작업을 맡기는 일입니다. Turborepo 도입, root script 정리, CI 연결을 나누고, package 분리나 ESLint 업그레이드는 별도 PR로 처리하는 편이 원인 추적이 쉽습니다.

다섯째, 공통화를 과하게 하는 일입니다. 거대한 packages/shared는 작은 변경도 전역 변경처럼 만듭니다. ui, utils, contracts, tsconfig처럼 이름과 책임이 좁은 package가 더 오래 갑니다.

수익 흐름과 다음 단계

Turborepo는 속도 도구이면서 품질 장치입니다. 콘텐츠 사이트는 빌드 실패 시 발행을 멈출 수 있고, SaaS는 가격 페이지, 가입, 계정 설정, 관리자 기능을 한 흐름에서 검증할 수 있습니다. 팀 개발에서는 CI 시간이 줄어드는 만큼 review 대기도 줄어듭니다.

ClaudeCodeLab은 CLAUDE.md, package 경계, CI 명령, review prompt, rollout rule을 팀 운영 방식으로 만드는 작업을 도울 수 있습니다. 혼자 학습한다면 monorepo 관리CI/CD 설정을 이어서 보세요. 팀 적용을 빠르게 하고 싶다면 training / consultation에서 시작할 수 있습니다.

실제로 적용해 본 결과

Masa가 Vite 앱 두 개와 공유 UI package 하나로 된 작은 저장소에 이 패턴을 적용했을 때, 첫 실행은 모든 task가 실행됐고 이후 실행에서는 build와 type-check에서 캐시 히트를 확인했습니다. 처음에는 outputs가 너무 넓어 프레임워크 내부 캐시까지 CI artifact에 들어갔고 확인이 번거로웠습니다. 최종적으로는 이 글처럼 v2 tasks, 좁은 outputs, affected 검증, 파일 변경 전 경계 리뷰를 요구하는 Claude Code 프롬프트가 가장 안정적이었습니다.

#Claude Code #Turborepo #monorepo #빌드 도구 #성능
무료

무료 PDF: Claude Code 치트시트

이메일을 입력하면 명령, 리뷰 습관, 안전한 워크플로를 정리한 PDF를 받을 수 있습니다.

개인정보를 안전하게 관리하며 스팸을 보내지 않습니다.

Masa

작성자 소개

Masa

Claude Code 실무 워크플로와 팀 도입을 검증하는 엔지니어입니다.