Back to skills
SkillHub ClubShip Full StackFull Stack

monorepo-architect

**MONOREPO ARCHITECT**: '모노레포', '워크스페이스', '패키지 구조', '폴더 구조', 'shared', '공유 코드', '의존성', 'turborepo', 'pnpm' 요청 시 자동 발동. pnpm-workspace.yaml/turbo.json/packages/** 작업 시 자동 적용. 의존성 방향 규칙 검증.

Packaged view

This page reorganizes the original catalog entry around fit, installability, and workflow context first. The original raw source lives below.

Stars
0
Hot score
74
Updated
March 20, 2026
Overall rating
C0.8
Composite score
0.8
Best-practice grade
C62.8

Install command

npx @skill-hub/cli install monicajeon28-gmcruise-monorepo-architect
monorepoworkspacedependency-managementproject-structurepnpm

Repository

monicajeon28/GMcruise

Skill path: .claude/skills/monorepo-architect

**MONOREPO ARCHITECT**: '모노레포', '워크스페이스', '패키지 구조', '폴더 구조', 'shared', '공유 코드', '의존성', 'turborepo', 'pnpm' 요청 시 자동 발동. pnpm-workspace.yaml/turbo.json/packages/** 작업 시 자동 적용. 의존성 방향 규칙 검증.

Open repository

Best for

Primary workflow: Ship Full Stack.

Technical facets: Full Stack.

Target audience: everyone.

License: Unknown.

Original source

Catalog source: SkillHub Club.

Repository owner: monicajeon28.

This is still a mirrored public skill entry. Review the repository before installing into production workflows.

What it helps with

  • Install monorepo-architect into Claude Code, Codex CLI, Gemini CLI, or OpenCode workflows
  • Review https://github.com/monicajeon28/GMcruise before adding monorepo-architect to shared team environments
  • Use monorepo-architect for development workflows

Works across

Claude CodeCodex CLIGemini CLIOpenCode

Favorites: 0.

Sub-skills: 0.

Aggregator: No.

Original source / Raw SKILL.md

---
name: monorepo-architect
description: "**MONOREPO ARCHITECT**: '모노레포', '워크스페이스', '패키지 구조', '폴더 구조', 'shared', '공유 코드', '의존성', 'turborepo', 'pnpm' 요청 시 자동 발동. pnpm-workspace.yaml/turbo.json/packages/** 작업 시 자동 적용. 의존성 방향 규칙 검증."
allowed-tools:
  - Read
  - Glob
  - Grep
---

# Monorepo Architect v2.0 - Unified Monorepo Structure Guide

**Proactive Monorepo Guardian** - 모노레포 구조 관련 작업 시 자동으로 규칙 적용

## 자동 발동 조건

```yaml
Auto_Trigger_Conditions:
  File_Patterns:
    - "pnpm-workspace.yaml, lerna.json"
    - "turbo.json, nx.json"
    - "packages/**/*, apps/**/*"
    - "tsconfig.base.json, tsconfig.paths.json"

  Keywords_KO:
    - "모노레포, 모노 레포, 워크스페이스"
    - "패키지 구조, 폴더 구조, 프로젝트 구조"
    - "공유 코드, shared, 공통 모듈"
    - "의존성, 디펜던시, import 경로"
    - "turborepo, lerna, nx, pnpm"
    - "패키지 추가, 새 패키지"

  Keywords_EN:
    - "monorepo, workspace, workspaces"
    - "package structure, folder structure"
    - "shared code, common module"
    - "dependency, import path"
    - "turborepo, lerna, nx, pnpm"

  Path_Patterns:
    - "@monorepo/, @packages/, @apps/"
    - "packages/shared, packages/common"
```

## 선택적 문서 로드 전략

```yaml
Document_Loading_Strategy:
  Always_Load:
    - "core/structure.md"           # 폴더 구조 (항상)
    - "quick-reference/checklist.md" # 체크리스트 (항상)

  Context_Specific_Load:
    New_Package: "templates/package-template.md"
    Dependency_Analysis: "core/dependency-rules.md"
    Build_Pipeline: "patterns/turborepo.md"
    TypeScript_Config: "patterns/typescript-config.md"
    Shared_Code: "core/shared-package.md"
```

## Quick Reference

### 5 Core Principles

```yaml
1. Shared First: "중복 코드는 packages/shared로"
2. Dependency Direction: "의존성은 항상 packages → apps 방향"
3. Independent Deployability: "각 app은 독립 배포 가능"
4. Consistent Tooling: "전체 프로젝트 동일 도구 사용"
5. Clear Boundaries: "패키지 간 명확한 경계"
```

### Instant Checklist

```markdown
## 새 코드 작성 시
- [ ] 이 코드가 여러 app에서 사용될까? → packages/shared로
- [ ] 의존성 방향이 올바른가? (packages → apps)
- [ ] 적절한 위치에 있는가?

## 새 패키지 추가 시
- [ ] package.json에 name, version, main 설정?
- [ ] tsconfig.json이 base를 extends?
- [ ] 루트 workspace에 등록?
```

### Standard Structure (Quick View)

```
project-root/
├── apps/
│   ├── backend/         # NestJS
│   ├── web/             # Next.js
│   └── mobile/          # Expo
├── packages/
│   ├── shared/          # Types, Utils, Validation
│   ├── ui/              # Shared UI Components
│   ├── api-client/      # API Client (auto-generated)
│   └── config/          # Shared configs
├── tools/               # Build scripts
├── pnpm-workspace.yaml
├── turbo.json
└── tsconfig.base.json
```

### Dependency Direction

```
✅ 허용: apps/* → packages/*
❌ 금지: packages/* → apps/*
❌ 금지: apps/backend → apps/web
```

## 문서 구조

```
monorepo-architect/
├── SKILL.md                        # 이 파일 (라우터)
├── core/
│   ├── structure.md                # 폴더 구조 상세
│   ├── shared-package.md           # shared 패키지 설계
│   └── dependency-rules.md         # 의존성 규칙
├── templates/
│   ├── package-template.md         # 새 패키지 템플릿
│   └── app-configs.md              # 앱별 설정 템플릿
├── patterns/
│   ├── turborepo.md                # Turborepo 설정
│   └── typescript-config.md        # TypeScript 설정
└── quick-reference/
    ├── checklist.md                # 체크리스트
    └── anti-patterns.md            # 안티패턴
```

## 사용 방법

### 1. 자동 발동 (Proactive)
모노레포 구조, pnpm workspace, import 경로 관련 작업 시 자동 발동

### 2. 명시적 호출
```
"새 패키지 추가하려면 어디에 만들어야 해?"
"이 코드를 shared로 옮겨야 할까?"
"의존성 방향이 맞는지 확인해줘"
```

---

**Version**: 2.0.0
**Quality Target**: 90%
**Related Skills**: clean-code-mastery, api-first-design


---

## Referenced Files

> The following files are referenced in this skill and included for context.

### core/structure.md

```markdown
# Monorepo Folder Structure

## Standard Structure

```
project-root/
├── apps/                        # 애플리케이션들
│   ├── backend/                 # NestJS 백엔드
│   │   ├── src/
│   │   │   ├── modules/         # 기능별 모듈
│   │   │   │   ├── users/
│   │   │   │   │   ├── users.module.ts
│   │   │   │   │   ├── users.controller.ts
│   │   │   │   │   ├── users.service.ts
│   │   │   │   │   ├── dto/
│   │   │   │   │   └── entities/
│   │   │   │   ├── auth/
│   │   │   │   └── orders/
│   │   │   ├── common/          # 백엔드 공통
│   │   │   │   ├── guards/
│   │   │   │   ├── filters/
│   │   │   │   ├── interceptors/
│   │   │   │   └── decorators/
│   │   │   ├── config/
│   │   │   ├── app.module.ts
│   │   │   └── main.ts
│   │   ├── test/
│   │   ├── package.json
│   │   └── tsconfig.json
│   │
│   ├── web/                     # Next.js 웹
│   │   ├── src/
│   │   │   ├── app/             # App Router
│   │   │   │   ├── (auth)/
│   │   │   │   ├── (dashboard)/
│   │   │   │   ├── api/
│   │   │   │   ├── layout.tsx
│   │   │   │   └── page.tsx
│   │   │   ├── components/
│   │   │   │   ├── ui/
│   │   │   │   ├── forms/
│   │   │   │   └── layouts/
│   │   │   ├── hooks/
│   │   │   ├── lib/
│   │   │   ├── stores/
│   │   │   └── styles/
│   │   ├── public/
│   │   ├── package.json
│   │   └── tsconfig.json
│   │
│   └── mobile/                  # Expo 모바일
│       ├── src/
│       │   ├── app/             # Expo Router
│       │   │   ├── (tabs)/
│       │   │   ├── (auth)/
│       │   │   └── _layout.tsx
│       │   ├── components/
│       │   ├── hooks/
│       │   ├── lib/
│       │   └── stores/
│       ├── assets/
│       ├── package.json
│       └── tsconfig.json
│
├── packages/                    # 공유 패키지들
│   ├── shared/                  # 핵심 공유 코드
│   │   ├── src/
│   │   │   ├── types/
│   │   │   ├── utils/
│   │   │   ├── validation/
│   │   │   ├── constants/
│   │   │   └── index.ts
│   │   ├── package.json
│   │   └── tsconfig.json
│   │
│   ├── ui/                      # 공유 UI 컴포넌트
│   │   ├── src/
│   │   │   ├── Button/
│   │   │   └── index.ts
│   │   ├── package.json
│   │   └── tsconfig.json
│   │
│   ├── api-client/              # API 클라이언트
│   │   ├── src/
│   │   │   ├── generated/
│   │   │   └── index.ts
│   │   └── package.json
│   │
│   └── config/                  # 공유 설정
│       ├── eslint/
│       ├── typescript/
│       └── package.json
│
├── tools/                       # 빌드 도구
│   ├── scripts/
│   └── package.json
│
├── .github/workflows/
├── pnpm-workspace.yaml
├── turbo.json
├── package.json
├── tsconfig.base.json
└── eslint.config.mjs
```

## Directory Purposes

```yaml
apps/:
  backend/:
    purpose: "NestJS API 서버"
    contains:
      - "REST/GraphQL API"
      - "비즈니스 로직"
      - "데이터베이스 접근"

  web/:
    purpose: "Next.js 웹 애플리케이션"
    contains:
      - "React 컴포넌트"
      - "페이지/라우팅"
      - "웹 전용 훅"

  mobile/:
    purpose: "Expo 모바일 애플리케이션"
    contains:
      - "React Native 컴포넌트"
      - "모바일 네비게이션"

packages/:
  shared/:
    purpose: "모든 앱에서 공유하는 핵심 코드"
    contains:
      - "타입 정의"
      - "유틸리티 함수"
      - "검증 스키마 (Zod)"
      - "상수"
    should_not_contain:
      - "React 컴포넌트"
      - "플랫폼 특정 코드"

  ui/:
    purpose: "공유 UI 컴포넌트"
    note: "웹/모바일 공유 가능한 경우만"

  api-client/:
    purpose: "타입 안전한 API 클라이언트"
    generated_from: "backend의 Swagger 스펙"

  config/:
    purpose: "공유 설정 (ESLint, TypeScript)"

tools/:
  purpose: "개발 도구 및 스크립트"
```

## Code Placement Decision Tree

```
새 코드 작성 시:

1. 여러 앱에서 사용?
   ├── Yes → 플랫폼 독립적?
   │         ├── Yes → packages/shared
   │         └── No (React) → packages/ui
   └── No → 해당 앱 내부

2. 타입/인터페이스?
   ├── 공유 → packages/shared/src/types/
   └── 앱 전용 → 해당 앱의 types/

3. 유틸리티 함수?
   ├── 공유 → packages/shared/src/utils/
   └── 앱 전용 → 해당 앱의 lib/
```

```

### quick-reference/checklist.md

```markdown
# Monorepo Quick Checklist

## Setup Checklist

```markdown
## 새 모노레포 설정 시

### 기본 구조
- [ ] pnpm-workspace.yaml 생성
- [ ] turbo.json 설정
- [ ] tsconfig.base.json 생성
- [ ] 루트 package.json 설정
- [ ] eslint.config.mjs 설정 (의존성 규칙 포함)

### 디렉토리 생성
- [ ] apps/ 디렉토리
- [ ] packages/ 디렉토리
- [ ] tools/ 디렉토리

### packages/shared 설정
- [ ] package.json (name: @myapp/shared)
- [ ] tsconfig.json (extends base)
- [ ] src/index.ts (barrel export)
- [ ] types/, utils/, validation/, constants/

### 각 앱 설정
- [ ] apps/backend (NestJS)
- [ ] apps/web (Next.js)
- [ ] apps/mobile (Expo)
- [ ] 각 앱 tsconfig.json
- [ ] 각 앱 package.json

### CI/CD
- [ ] .github/workflows/ci.yml
- [ ] 각 앱별 deploy workflow
```

## New Code Placement

```markdown
## 새 코드 위치 결정

### 질문 체크리스트
1. 여러 앱에서 사용되는가?
   - Yes → 2번으로
   - No → 해당 앱 내부

2. 플랫폼 독립적인가?
   - Yes → packages/shared
   - No (React 필요) → packages/ui 또는 앱 내부

3. UI 컴포넌트인가?
   - Yes + 공유 → packages/ui
   - Yes + 앱 전용 → 해당 앱의 components/

4. 타입/인터페이스인가?
   - 공유 → packages/shared/src/types/
   - 앱 전용 → 해당 앱의 types/

5. 유틸리티 함수인가?
   - 공유 → packages/shared/src/utils/
   - 앱 전용 → 해당 앱의 lib/
```

## New Package Checklist

```markdown
## 새 패키지 추가 시

### 필수 파일
- [ ] package.json
  - [ ] name: "@myapp/{name}"
  - [ ] version: "1.0.0"
  - [ ] main: "dist/index.js"
  - [ ] types: "dist/index.d.ts"
  - [ ] files: ["dist"]
  - [ ] scripts: build, dev, clean

- [ ] tsconfig.json
  - [ ] extends: "../../tsconfig.base.json"
  - [ ] outDir: "./dist"
  - [ ] rootDir: "./src"

- [ ] src/index.ts (barrel export)

### 워크스페이스 등록
- [ ] pnpm-workspace.yaml에 포함 확인
- [ ] pnpm install 실행

### Turborepo 설정
- [ ] turbo.json에 빌드 파이프라인 추가 (필요시)
```

## Dependency Check

```markdown
## 의존성 방향 확인

### 허용됨 ✅
- apps/* → packages/*
- packages/ui → packages/shared
- packages/api-client → packages/shared

### 금지됨 ❌
- packages/* → apps/*
- apps/backend → apps/web
- apps/web → apps/mobile
- apps/mobile → apps/backend
- packages/shared → packages/ui
```

## Import Order

```markdown
## Import 순서

1. External (node_modules)
   import { useState } from 'react';
   import { z } from 'zod';

2. Workspace (@myapp/*)
   import { User } from '@myapp/shared';
   import { Button } from '@myapp/ui';

3. Internal (@/*)
   import { useAuth } from '@/hooks/useAuth';

4. Relative (./)
   import styles from './Component.module.css';
```

## Quick Commands

```bash
# 전체 빌드
pnpm build

# 특정 앱 개발
pnpm dev:web
pnpm dev:backend
pnpm dev:mobile

# 특정 패키지만 빌드
pnpm --filter @myapp/shared build

# 의존성 설치
pnpm install

# 캐시 클리어
pnpm clean

# 린트
pnpm lint
```

```

### templates/package-template.md

```markdown
# Package Templates

## New Package Script

```bash
#!/bin/bash
# tools/scripts/create-package.sh

PACKAGE_NAME=$1

if [ -z "$PACKAGE_NAME" ]; then
  echo "Usage: pnpm create:package <package-name>"
  exit 1
fi

mkdir -p "packages/$PACKAGE_NAME/src"

# package.json
cat > "packages/$PACKAGE_NAME/package.json" << EOF
{
  "name": "@myapp/$PACKAGE_NAME",
  "version": "1.0.0",
  "main": "dist/index.js",
  "types": "dist/index.d.ts",
  "files": ["dist"],
  "scripts": {
    "build": "tsc",
    "dev": "tsc --watch",
    "clean": "rm -rf dist",
    "test": "jest"
  },
  "devDependencies": {
    "typescript": "^5.3.0"
  }
}
EOF

# tsconfig.json
cat > "packages/$PACKAGE_NAME/tsconfig.json" << EOF
{
  "extends": "../../tsconfig.base.json",
  "compilerOptions": {
    "outDir": "./dist",
    "rootDir": "./src"
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}
EOF

# index.ts
cat > "packages/$PACKAGE_NAME/src/index.ts" << EOF
// @myapp/$PACKAGE_NAME
// Export your package contents here

export {};
EOF

echo "Created package: packages/$PACKAGE_NAME"
echo "Run 'pnpm install' to update workspace"
```

## App Package.json Templates

### Backend (NestJS)

```json
{
  "name": "@myapp/backend",
  "version": "1.0.0",
  "private": true,
  "scripts": {
    "build": "nest build",
    "dev": "nest start --watch",
    "start": "node dist/main",
    "start:prod": "node dist/main",
    "lint": "eslint \"{src,test}/**/*.ts\"",
    "test": "jest",
    "test:watch": "jest --watch",
    "test:cov": "jest --coverage",
    "test:e2e": "jest --config ./test/jest-e2e.json"
  },
  "dependencies": {
    "@myapp/shared": "workspace:*",
    "@nestjs/common": "^10.0.0",
    "@nestjs/core": "^10.0.0",
    "@nestjs/platform-express": "^10.0.0"
  },
  "devDependencies": {
    "@nestjs/cli": "^10.0.0",
    "@nestjs/testing": "^10.0.0",
    "typescript": "^5.3.0"
  }
}
```

### Web (Next.js)

```json
{
  "name": "@myapp/web",
  "version": "1.0.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint",
    "test": "jest"
  },
  "dependencies": {
    "@myapp/shared": "workspace:*",
    "@myapp/ui": "workspace:*",
    "@myapp/api-client": "workspace:*",
    "next": "^14.0.0",
    "react": "^18.2.0",
    "react-dom": "^18.2.0"
  },
  "devDependencies": {
    "@types/react": "^18.2.0",
    "typescript": "^5.3.0"
  }
}
```

### Mobile (Expo)

```json
{
  "name": "@myapp/mobile",
  "version": "1.0.0",
  "private": true,
  "main": "expo-router/entry",
  "scripts": {
    "dev": "expo start",
    "android": "expo start --android",
    "ios": "expo start --ios",
    "build": "eas build",
    "lint": "eslint . --ext .ts,.tsx",
    "test": "jest"
  },
  "dependencies": {
    "@myapp/shared": "workspace:*",
    "@myapp/ui": "workspace:*",
    "@myapp/api-client": "workspace:*",
    "expo": "~49.0.0",
    "expo-router": "~2.0.0",
    "react": "18.2.0",
    "react-native": "0.72.0"
  },
  "devDependencies": {
    "@types/react": "~18.2.0",
    "typescript": "^5.3.0"
  }
}
```

## Workspace Configuration

### pnpm-workspace.yaml

```yaml
packages:
  - 'apps/*'
  - 'packages/*'
  - 'tools'
```

### Root package.json

```json
{
  "name": "myapp-monorepo",
  "private": true,
  "packageManager": "[email protected]",
  "engines": {
    "node": ">=20.0.0",
    "pnpm": ">=8.0.0"
  },
  "scripts": {
    "dev": "turbo run dev",
    "build": "turbo run build",
    "test": "turbo run test",
    "lint": "turbo run lint",
    "clean": "turbo run clean && rm -rf node_modules",

    "dev:backend": "pnpm --filter @myapp/backend dev",
    "dev:web": "pnpm --filter @myapp/web dev",
    "dev:mobile": "pnpm --filter @myapp/mobile dev",

    "generate:api": "pnpm --filter @myapp/api-client generate",
    "db:migrate": "pnpm --filter @myapp/backend db:migrate"
  },
  "devDependencies": {
    "turbo": "^1.11.0",
    "typescript": "^5.3.0"
  }
}
```

```

### core/dependency-rules.md

```markdown
# Dependency Direction Rules

## Dependency Direction Diagram

```
┌─────────────────────────────────────────────────────────────────┐
│                     의존성 방향 규칙                             │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   ✅ 허용된 방향 (위에서 아래로)                                 │
│                                                                 │
│   apps/backend ──────┐                                         │
│   apps/web ──────────┼──► packages/shared                      │
│   apps/mobile ───────┘    packages/ui                          │
│                           packages/api-client                   │
│                           packages/config                       │
│                                                                 │
│   packages/ui ───────────► packages/shared                     │
│   packages/api-client ───► packages/shared                     │
│                                                                 │
├─────────────────────────────────────────────────────────────────┤
│   ❌ 금지된 방향                                                │
│                                                                 │
│   packages/* ──X──► apps/*                                     │
│   apps/backend ──X──► apps/web                                 │
│   apps/web ──X──► apps/mobile                                  │
│   apps/mobile ──X──► apps/backend                              │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘
```

## Import Rules

```yaml
Allowed_Imports:
  apps_backend:
    can_import:
      - "@myapp/shared"
      - "@myapp/config"
      - "node_modules packages"
    cannot_import:
      - "@myapp/ui"          # 백엔드에서 UI 불필요
      - "@myapp/api-client"  # 백엔드가 API 서버임
      - "apps/web/*"
      - "apps/mobile/*"

  apps_web:
    can_import:
      - "@myapp/shared"
      - "@myapp/ui"
      - "@myapp/api-client"
      - "@myapp/config"
    cannot_import:
      - "apps/backend/*"
      - "apps/mobile/*"

  apps_mobile:
    can_import:
      - "@myapp/shared"
      - "@myapp/ui"
      - "@myapp/api-client"
      - "@myapp/config"
    cannot_import:
      - "apps/backend/*"
      - "apps/web/*"

  packages_shared:
    can_import:
      - "node_modules only"
    cannot_import:
      - "Any @myapp/* package"
      - "Any apps/*"
```

## ESLint Configuration

```javascript
// eslint.config.mjs (루트)
import * as importPlugin from 'eslint-plugin-import';

export default [
  {
    plugins: {
      'import': importPlugin,
    },
    rules: {
      'import/no-restricted-paths': ['error', {
        zones: [
          // packages는 apps를 import할 수 없음
          {
            target: './packages',
            from: './apps',
            message: 'packages는 apps를 import할 수 없습니다.',
          },
          // apps 간 import 금지
          {
            target: './apps/backend',
            from: './apps/web',
            message: 'backend는 web을 import할 수 없습니다.',
          },
          {
            target: './apps/backend',
            from: './apps/mobile',
          },
          {
            target: './apps/web',
            from: './apps/mobile',
          },
          {
            target: './apps/mobile',
            from: './apps/web',
          },
        ],
      }],

      // 순환 의존성 방지
      'import/no-cycle': ['error', { maxDepth: Infinity }],
    },
  },
];
```

## Path Alias Convention

```yaml
Workspace_Packages:
  format: "@myapp/{package-name}"
  examples:
    - "@myapp/shared"
    - "@myapp/ui"
    - "@myapp/api-client"

App_Internal:
  format: "@/*"
  scope: "각 앱 내부에서만"
  examples:
    - "@/components/Button"
    - "@/lib/utils"

Import_Order:
  1. External: "react, next, zod"
  2. Workspace: "@myapp/*"
  3. Internal: "@/*"
  4. Relative: "./"

Example: |
  // 1. External
  import { useState } from 'react';
  import { z } from 'zod';

  // 2. Workspace
  import { User, formatDate } from '@myapp/shared';
  import { Button } from '@myapp/ui';

  // 3. Internal
  import { useAuth } from '@/hooks/useAuth';

  // 4. Relative
  import styles from './UserCard.module.css';
```

## Relative Import Rule

```yaml
Rule: "같은 패키지 내에서만 상대 경로 사용"

Good: |
  // apps/web/src/components/Button.tsx
  import { cn } from '../lib/utils';  // 같은 앱 내

Bad: |
  // apps/web/src/components/Button.tsx
  import { User } from '../../../packages/shared/src/types';  // 금지!

Correct: |
  import { User } from '@myapp/shared';  // 워크스페이스 별칭 사용
```

```

### patterns/turborepo.md

```markdown
# Turborepo Configuration

## turbo.json

```json
{
  "$schema": "https://turbo.build/schema.json",
  "globalDependencies": [
    ".env",
    "tsconfig.base.json"
  ],
  "pipeline": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": ["dist/**", ".next/**", "build/**"],
      "env": ["NODE_ENV"]
    },
    "dev": {
      "cache": false,
      "persistent": true
    },
    "lint": {
      "dependsOn": ["^build"],
      "outputs": []
    },
    "test": {
      "dependsOn": ["build"],
      "outputs": ["coverage/**"],
      "env": ["NODE_ENV"]
    },
    "clean": {
      "cache": false
    },

    // 특정 앱 파이프라인
    "@myapp/backend#build": {
      "dependsOn": ["@myapp/shared#build"],
      "outputs": ["dist/**"]
    },
    "@myapp/web#build": {
      "dependsOn": ["@myapp/shared#build", "@myapp/ui#build"],
      "outputs": [".next/**"]
    },
    "@myapp/mobile#build": {
      "dependsOn": ["@myapp/shared#build", "@myapp/ui#build"],
      "outputs": ["build/**"]
    },

    // shared는 다른 것에 의존 없음
    "@myapp/shared#build": {
      "outputs": ["dist/**"]
    },

    // api-client 생성은 캐시 안 함
    "@myapp/api-client#generate": {
      "cache": false
    }
  }
}
```

## Build Order

```
┌─────────────────────────────────────────────────────────────────┐
│                     빌드 순서 (자동)                             │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   Stage 1 (병렬):                                               │
│   ├── packages/shared                                          │
│   └── packages/config                                          │
│                                                                 │
│   Stage 2 (packages/shared 완료 후, 병렬):                      │
│   ├── packages/ui                                              │
│   └── packages/api-client                                      │
│                                                                 │
│   Stage 3 (모든 packages 완료 후, 병렬):                        │
│   ├── apps/backend                                             │
│   ├── apps/web                                                 │
│   └── apps/mobile                                              │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘
```

## Key Pipeline Concepts

### dependsOn

```yaml
"^build": "의존하는 패키지들의 build 먼저 실행"
"build": "같은 패키지의 build 먼저"
"@myapp/shared#build": "특정 패키지의 build 먼저"
```

### outputs

```yaml
purpose: "캐시할 결과물 지정"
examples:
  - "dist/**"      # TypeScript 빌드 결과
  - ".next/**"     # Next.js 빌드 결과
  - "build/**"     # Expo 빌드 결과
  - "coverage/**"  # 테스트 커버리지
```

### cache

```yaml
"cache": false  # 항상 새로 실행 (dev, clean)
"cache": true   # 기본값, 변경 없으면 캐시 사용
```

### persistent

```yaml
"persistent": true  # dev 서버처럼 계속 실행되는 작업
```

## Common Commands

```bash
# 전체 빌드
pnpm build

# 특정 앱만 빌드
pnpm --filter @myapp/web build

# 전체 개발 서버
pnpm dev

# 변경된 패키지만 빌드
turbo run build --filter=...[HEAD^1]

# 캐시 무시하고 빌드
turbo run build --force

# 그래프 보기
turbo run build --graph
```

```

### patterns/typescript-config.md

```markdown
# TypeScript Configuration

## Base Configuration

```json
// tsconfig.base.json (루트)
{
  "compilerOptions": {
    // 기본 설정
    "target": "ES2022",
    "lib": ["ES2022"],
    "module": "NodeNext",
    "moduleResolution": "NodeNext",

    // 엄격 모드
    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,
    "noUncheckedIndexedAccess": true,

    // 출력 설정
    "declaration": true,
    "declarationMap": true,
    "sourceMap": true,

    // 상호 운용성
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "forceConsistentCasingInFileNames": true,
    "skipLibCheck": true,

    // 경로 매핑
    "baseUrl": ".",
    "paths": {
      "@myapp/shared": ["packages/shared/src"],
      "@myapp/shared/*": ["packages/shared/src/*"],
      "@myapp/ui": ["packages/ui/src"],
      "@myapp/ui/*": ["packages/ui/src/*"],
      "@myapp/api-client": ["packages/api-client/src"],
      "@myapp/config": ["packages/config"]
    }
  },
  "exclude": [
    "node_modules",
    "dist",
    "build",
    ".next",
    ".expo",
    "coverage"
  ]
}
```

## App-specific Configurations

### Backend (NestJS)

```json
// apps/backend/tsconfig.json
{
  "extends": "../../tsconfig.base.json",
  "compilerOptions": {
    "module": "CommonJS",
    "moduleResolution": "Node",
    "outDir": "./dist",
    "rootDir": "./src",
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist", "test"]
}
```

### Web (Next.js)

```json
// apps/web/tsconfig.json
{
  "extends": "../../tsconfig.base.json",
  "compilerOptions": {
    "module": "ESNext",
    "moduleResolution": "Bundler",
    "jsx": "preserve",
    "plugins": [{ "name": "next" }],
    "paths": {
      "@/*": ["./src/*"],
      "@myapp/shared": ["../../packages/shared/src"],
      "@myapp/shared/*": ["../../packages/shared/src/*"],
      "@myapp/ui": ["../../packages/ui/src"],
      "@myapp/api-client": ["../../packages/api-client/src"]
    }
  },
  "include": ["src/**/*", "next-env.d.ts", ".next/types/**/*.ts"],
  "exclude": ["node_modules"]
}
```

### Mobile (Expo)

```json
// apps/mobile/tsconfig.json
{
  "extends": "../../tsconfig.base.json",
  "compilerOptions": {
    "module": "ESNext",
    "moduleResolution": "Bundler",
    "jsx": "react-jsx",
    "paths": {
      "@/*": ["./src/*"],
      "@myapp/shared": ["../../packages/shared/src"],
      "@myapp/shared/*": ["../../packages/shared/src/*"],
      "@myapp/ui": ["../../packages/ui/src"],
      "@myapp/api-client": ["../../packages/api-client/src"]
    }
  },
  "include": ["src/**/*", "expo-env.d.ts"],
  "exclude": ["node_modules"]
}
```

### Shared Package

```json
// packages/shared/tsconfig.json
{
  "extends": "../../tsconfig.base.json",
  "compilerOptions": {
    "outDir": "./dist",
    "rootDir": "./src",
    "declaration": true,
    "declarationMap": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist", "**/*.test.ts"]
}
```

## Path Alias Summary

| Alias | Points To | Used In |
|-------|-----------|---------|
| `@myapp/shared` | packages/shared/src | All apps |
| `@myapp/ui` | packages/ui/src | web, mobile |
| `@myapp/api-client` | packages/api-client/src | web, mobile |
| `@/*` | ./src/* | Each app internally |

## Module Resolution

| Package Type | module | moduleResolution |
|--------------|--------|------------------|
| NestJS | CommonJS | Node |
| Next.js | ESNext | Bundler |
| Expo | ESNext | Bundler |
| Packages | NodeNext | NodeNext |

```

### core/shared-package.md

```markdown
# Shared Package Design

## What Goes in shared/

### Types

```typescript
// packages/shared/src/types/user.ts

export interface User {
  id: string;
  email: string;
  name: string;
  role: UserRole;
  createdAt: Date;
  updatedAt: Date;
}

export type UserRole = 'user' | 'admin' | 'super-admin';

// API 요청/응답 타입
export interface CreateUserRequest {
  email: string;
  name: string;
  password: string;
}

export interface UserResponse {
  id: string;
  email: string;
  name: string;
  role: UserRole;
  createdAt: string;  // ISO string for JSON
}
```

### Validation (Zod)

```typescript
// packages/shared/src/validation/user.schema.ts

import { z } from 'zod';

export const emailSchema = z
  .string()
  .email('유효한 이메일 형식이 아닙니다')
  .max(255);

export const passwordSchema = z
  .string()
  .min(8, '비밀번호는 최소 8자 이상')
  .max(72)
  .regex(
    /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/,
    '대소문자와 숫자를 포함해야 합니다'
  );

export const createUserSchema = z.object({
  email: emailSchema,
  name: z.string().min(1).max(100),
  password: passwordSchema,
});

// 타입 추론
export type CreateUserInput = z.infer<typeof createUserSchema>;
```

### Utils

```typescript
// packages/shared/src/utils/date.ts

export function formatDate(date: Date | string, locale = 'ko-KR'): string {
  const d = typeof date === 'string' ? new Date(date) : date;
  return d.toLocaleDateString(locale, {
    year: 'numeric',
    month: 'long',
    day: 'numeric',
  });
}

export function isExpired(date: Date | string): boolean {
  const d = typeof date === 'string' ? new Date(date) : date;
  return d.getTime() < Date.now();
}
```

### Constants

```typescript
// packages/shared/src/constants/index.ts

export const USER_ROLES = ['user', 'admin', 'super-admin'] as const;

export const API_ENDPOINTS = {
  USERS: '/api/users',
  AUTH: '/api/auth',
  ORDERS: '/api/orders',
} as const;

export const PAGINATION = {
  DEFAULT_PAGE: 1,
  DEFAULT_LIMIT: 20,
  MAX_LIMIT: 100,
} as const;
```

### Barrel Export

```typescript
// packages/shared/src/index.ts

// Types
export * from './types/user';
export * from './types/order';
export * from './types/common';

// Validation
export * from './validation/user.schema';
export * from './validation/order.schema';

// Utils
export * from './utils/date';
export * from './utils/format';

// Constants
export * from './constants';
```

## Package Configuration

```json
// packages/shared/package.json
{
  "name": "@myapp/shared",
  "version": "1.0.0",
  "main": "dist/index.js",
  "types": "dist/index.d.ts",
  "files": ["dist"],
  "scripts": {
    "build": "tsc",
    "dev": "tsc --watch",
    "clean": "rm -rf dist"
  },
  "dependencies": {
    "zod": "^3.22.0"
  }
}
```

```json
// packages/shared/tsconfig.json
{
  "extends": "../../tsconfig.base.json",
  "compilerOptions": {
    "outDir": "./dist",
    "rootDir": "./src",
    "declaration": true,
    "declarationMap": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}
```

## Using Shared in Apps

### Backend (NestJS)

```typescript
import {
  User,
  CreateUserInput,
  createUserSchema,
} from '@myapp/shared';

@Injectable()
export class UsersService {
  async create(input: unknown): Promise<User> {
    const validated = createUserSchema.parse(input);
    return this.usersRepository.save(validated);
  }
}
```

### Web (Next.js)

```typescript
import { User, formatDate } from '@myapp/shared';

export default function UsersPage() {
  const users: User[] = await fetchUsers();

  return (
    <ul>
      {users.map(user => (
        <li key={user.id}>
          {user.name} - {formatDate(user.createdAt)}
        </li>
      ))}
    </ul>
  );
}
```

### Mobile (Expo)

```typescript
import { User, formatDate } from '@myapp/shared';

export function ProfileScreen({ user }: { user: User }) {
  return (
    <View>
      <Text>{user.name}</Text>
      <Text>가입일: {formatDate(user.createdAt)}</Text>
    </View>
  );
}
```

```

monorepo-architect | SkillHub