Nx Monorepo
nx-best-practices 스킬 완전 가이드.
폴더 구조, 태그와 경계, TypeScript 프로젝트 참조, 플러그인 executor, 명령어 표면.
이 페이지는 .agents/skills/monorepo/nx-best-practices/SKILL.md의 내용을 토대로 합니다. Agent에게 Nx 관련 작업을 시킬 때 이 스킬이 자동으로 로드됩니다.

2계층 폴더 구조

GS Retail Nx 모노레포의 표준 레이아웃입니다. 앱과 라이브러리를 명확히 분리합니다.

workspace-root/ ├── apps/ # 배포 가능한 애플리케이션 │ ├── web/ # Next.js app │ ├── admin/ # 어드민 Next.js app │ └── mobile/ # React Native (별도 scope) │ ├── libs/ # 공유 라이브러리 (scope/type 2계층) │ ├── web/ # scope: web │ │ ├── feature-checkout/ # type: feature │ │ ├── data-access-cart/ # type: data-access │ │ ├── ui-button/ # type: ui │ │ └── util-format/ # type: util │ ├── shared/ # scope: shared (cross-app) │ │ ├── ui-design-system/ │ │ └── util-date/ │ └── admin/ # scope: admin │ ├── packages/ # npm 배포용 패키지만 (외부 게시) │ ├── nx.json ├── pnpm-workspace.yaml └── tsconfig.base.json

타입 접미사 규칙

접미사설명예시
feature-*라우트/페이지 수준 스마트 컴포넌트. 다른 lib 조합.feature-checkout
data-access-*TanStack Query 훅, API 클라이언트, Zustand 스토어data-access-cart
ui-*프레젠테이셔널 컴포넌트 (앱 로직 없음)ui-button
util-*순수 함수 유틸리티, React 없음util-format

태그와 모듈 경계

각 라이브러리의 project.jsonscope 태그type 태그를 반드시 붙입니다.

// libs/web/feature-checkout/project.json
{
  "name": "web-feature-checkout",
  "tags": ["scope:web", "type:feature"]
}
scope:web scope:shared scope:admin type:feature type:data-access type:ui type:util

@nx/enforce-module-boundaries 설정

ESLint 규칙으로 경계를 강제합니다. .eslintrc.json 또는 eslint.config.mjs에 적용:

{
  "@nx/enforce-module-boundaries": ["error", {
    "allow": [],
    "depConstraints": [
      // 같은 scope끼리만 의존 가능
      {
        "sourceTag": "scope:web",
        "onlyDependOnLibsWithTags": ["scope:web", "scope:shared"]
      },
      {
        "sourceTag": "scope:admin",
        "onlyDependOnLibsWithTags": ["scope:admin", "scope:shared"]
      },
      // 타입 방향성: feature → data-access → util
      {
        "sourceTag": "type:feature",
        "onlyDependOnLibsWithTags": ["type:feature", "type:data-access", "type:ui", "type:util"]
      },
      {
        "sourceTag": "type:data-access",
        "onlyDependOnLibsWithTags": ["type:data-access", "type:util"]
      },
      {
        "sourceTag": "type:ui",
        "onlyDependOnLibsWithTags": ["type:ui", "type:util"]
      },
      {
        "sourceTag": "type:util",
        "onlyDependOnLibsWithTags": ["type:util"]
      }
    ]
  }]
}
방향성 핵심: featuredata-accessutil. 역방향 import는 lint 에러. uiutil만 의존 가능 (feature/data-access 금지).

TypeScript 프로젝트 참조

Nx 워크스페이스에서 경로 별칭(path alias) 대신 TypeScript 프로젝트 참조를 우선합니다. 증분 빌드(incremental build)를 활성화해 빌드 속도를 높입니다.

// libs/web/util-format/tsconfig.json
{
  "extends": "../../../tsconfig.base.json",
  "compilerOptions": {
    "composite": true,          // 프로젝트 참조 필수
    "incremental": true,        // 증분 빌드
    "declaration": true,
    "declarationMap": true
  }
}

// tsconfig.base.json (workspace root)
{
  "compilerOptions": {
    "paths": {
      "@myorg/web/util-format": ["libs/web/util-format/src/index.ts"]
    }
  }
}

// 프로젝트 참조 추가 (tsconfig.json 참조 배열)
{
  "references": [
    { "path": "../../../libs/web/util-format" }
  ]
}
경로 별칭(@myorg/*)은 IDE/런타임에, TS 프로젝트 참조는 tsc --build에 활용됩니다. 둘 다 설정하되 참조를 정규 방식으로 사용하세요.

플러그인 Executor

규칙: 프레임워크 네이티브 executor(@nx/next, @nx/react)를 원시 nx:run-commands보다 우선합니다.

// apps/web/project.json — 올바른 방식
{
  "targets": {
    "build": {
      "executor": "@nx/next:build",    // ✅ 프레임워크 플러그인
      "options": { "outputPath": "dist/apps/web" }
    },
    "serve": {
      "executor": "@nx/next:server",   // ✅
      "options": { "buildTarget": "web:build" }
    }
  }
}

// ❌ 피해야 할 방식
{
  "targets": {
    "build": {
      "executor": "nx:run-commands",
      "options": { "commands": ["next build"] }  // raw command
    }
  }
}
nx:run-commands는 Nx가 캐시 키를 올바르게 계산할 수 없어 빌드 캐시 효율이 떨어집니다.

명령어 표면

명령어설명
nx graph프로젝트 의존성 그래프 시각화 (브라우저 열림)
nx affected -t build변경된 프로젝트와 그 영향 받는 프로젝트만 빌드
nx affected -t test변경된 프로젝트만 테스트
nx affected -t lint변경된 프로젝트만 lint
nx run-many -t build모든 프로젝트 빌드 (병렬)
nx run-many -t lint test모든 프로젝트 lint + test
nx g @nx/next:app web새 Next.js 앱 생성
nx g @nx/react:lib web/ui-button새 React 라이브러리 생성
nx migrate latestNx 버전 업그레이드 (마이그레이션 자동 실행)
nx reset캐시 초기화 (빌드 이상할 때)
nx show project web --web특정 프로젝트의 targets 상세 보기

affected 활용 팁

# PR 기준으로 affected 계산
nx affected -t build --base=main --head=HEAD

# 특정 브랜치 대비
nx affected -t test --base=origin/main

# verbose 출력
nx affected -t build --verbose

안티패턴 체크리스트

  • 내부 파일 직접 import: import from '@myorg/lib/src/components/Foo' → 반드시 index.ts를 통해 공개된 API만 사용
  • 순환 의존성: A→B→A 구조. nx graph로 주기적 확인
  • 태그 없는 라이브러리: project.json에 tags 없으면 경계 규칙이 적용되지 않음
  • apps/ 에서 apps/ 직접 import: 앱 간 공유는 반드시 libs/shared/ 경유
  • packages/ 에 내부 전용 코드: packages/는 외부 npm 배포 전용
  • nx:run-commands 남발: 프레임워크 플러그인이 있는데 raw command 사용 금지
  • 모든 것을 feature lib으로: 유틸리티는 util, 스토어는 data-access로 분리
  • 경계 위반 무시: ESLint 경고를 // eslint-disable로 무력화 금지

Claude와 Nx 작업 플로우

Agent에게 Nx 관련 작업을 요청할 때 nx-best-practices 스킬이 자동 로드됩니다.

# 새 feature lib 생성 요청 예시
"웹 앱에 장바구니 feature lib 만들어줘"
→ Agent가 nx g @nx/react:lib web/feature-cart 실행
→ project.json에 tags: ["scope:web", "type:feature"] 자동 추가
→ tsconfig.base.json 경로 별칭 등록
→ enforce-module-boundaries 준수 확인
nx-best-practices 스킬 트리거 조건: nx.json이 있는 워크스페이스 작업, 새 app/lib 스캐폴딩, module boundary 위반/감사, Nx 버전 업그레이드, project.json/tsconfig 변경 시 자동 로드됩니다.