Nx는 캐싱과 병렬 실행에 더해 프로젝트 그래프, 코드 생성, 아키텍처 규칙 강제까지 제공하는 모노레포 플랫폼이다. "빠른 실행 한 가지"에 집중하는 Turborepo와 달리 기능을 두루 갖춘 쪽을 지향한다.
태스크 실행과 프로젝트 그래프
- 태스크의 출처는 셋이다.
package.json스크립트,project.json의 targets, 그리고 플러그인이 설정 파일에서 추론한 태스크(예:@nx/vite가vite.config.ts를 보고build·typecheck를 만들고 캐시 설정까지 붙인다. Vitest의test는 Nx 22부터@nx/vitest몫이다) - 프로젝트 사이 의존은 import를 분석해 프로젝트 그래프(project graph)로 만든다(
nx graph). 순서는nx.json의targetDefaults에"build": { "dependsOn": ["^build"] }처럼 적는다.^는 "의존하는 프로젝트의 같은 태스크 먼저"라는 뜻이다 nx run-many -t build test는 전체 또는-p로 고른 프로젝트에서 여러 태스크를 그래프 순서를 지키며 병렬로 돌린다
affected: 바뀐 것과 그 의존자만
nx affected -t test --base=origin/main --head=HEAD
nx graph --affected- git으로 바뀐 파일을 찾고, 그 파일이 속한 프로젝트와 그 프로젝트에 의존하는 모든 프로젝트를 affected로 표시한다.
lib10이 바뀌면lib10을 쓰는lib4,lib5,app2까지 돌고 무관한app1은 건너뛴다 - 기본 base는
main, head는 현재 파일시스템이다. CI에서는 "main에서 마지막으로 성공한 커밋"을 base로 두는 것을 권한다. GitHub Actions는nrwl/nx-set-shas가NX_BASE·NX_HEAD를 채워 준다. base 커밋이 있어야 하므로fetch-depth: 0이 필요하다 - lockfile이 바뀌면 기본으로 모든 프로젝트가 affected다(
projectsAffectedByDependencyUpdates: "all")."auto"로 두면 실제로 의존성이 바뀐 프로젝트만 고른다
캐시: namedInputs로 입력을 이름 붙여 쓴다
{
"namedInputs": {
"default": ["{projectRoot}/**/*"],
"production": ["default", "!{projectRoot}/**/*.spec.ts", { "env": "NODE_ENV" }, { "runtime": "node --version" }]
},
"targetDefaults": {
"build": { "cache": true, "inputs": ["production", "^production"], "outputs": ["{workspaceRoot}/dist/{projectRoot}"] }
}
}- 입력은 파일 패턴만이 아니라 환경변수(
{ "env": ... })와 런타임 명령 결과({ "runtime": "node --version" })도 된다. 테스트 파일을production에서 빼면 테스트만 고쳐도 build 캐시가 유지된다 ^production은 의존하는 프로젝트의 production 입력까지 해시에 넣는다는 뜻이다dist·build출력 폴더는 자동으로 인식한다. 다른 곳이면outputs에 적는다. 히트(hit)면 터미널 출력과 출력 파일을 복원한다- 원격 캐시는 Nx Cloud(
nx connect)로 공유한다. 캐시는 부수 효과가 없는 태스크에만 안전하다. 라이브 백엔드를 치는 e2e는 캐시하지 않는다. 미스 원인은nx show project <이름> --web으로 실제 적용된 inputs를 보고 좁힌다
생성기와 마이그레이션
nx g @nx/react:lib packages/mylib처럼 플러그인 생성기로 프로젝트를 만든다. 폴더 구조, 린트·테스트 설정, 태그가 같은 양식으로 찍혀 나온다- 조직 규약은 커스텀 생성기로 코드에 담는다. 생성기는 가상 파일시스템
Tree와 옵션 스키마를 받는 함수이고,@nx/devkit의formatFiles·installPackagesTask같은 도우미를 쓴다. 생성기는 사람이 기억하는 컨벤션을 실행 가능한 코드로 바꾸는 장치다 nx migrate는 Nx와 플러그인 버전을 올리면서 설정·코드 변경까지 함께 적용한다
모듈 경계 규칙
@nx/enforce-module-boundaries ESLint 규칙이 프로젝트 태그로 import 방향을 강제한다.
{
"@nx/enforce-module-boundaries": ["error", {
"depConstraints": [
{ "sourceTag": "scope:client", "onlyDependOnLibsWithTags": ["scope:client", "scope:shared"] },
{ "sourceTag": "type:feature", "notDependOnLibsWithTags": ["type:app"] }
]
}]
}- 태그는
project.json의tags나package.json의nx.tags에 적는다.scope:*(도메인)와type:*(계층) 두 축으로 붙이는 관례가 흔하다. 태그 매칭은 정확한 문자열, 글로브(scope:*), 정규식(/^scope.*/)을 받는다 sourceTag가 붙은 프로젝트는onlyDependOnLibsWithTags에 있는 태그의 프로젝트만 import할 수 있고,notDependOnLibsWithTags에 있는 것은 import하지 못한다. 태그 없는 프로젝트는 기본으로 아무것도 import하지 못한다- 이 규칙은 순환 의존(자기 자신 포함), lazy-load하는 라이브러리의 정적 import,
package.json에 없는 전이 의존성 import(banTransitiveDependencies), 특정 npm 패키지 금지·허용(bannedExternalImports·allowedExternalImports), buildable 라이브러리가 non-buildable을 import하는 것(enforceBuildableLibDependency)도 잡는다 - 위반은 린트 에러로 나온다. "scope:admin 프로젝트는 scope:shared나 scope:admin만 의존할 수 있다" 같은 메시지다
고르는 기준
- 코드 생성, affected 분석, 아키텍처 거버넌스, 다중 언어가 필요한 큰 조직이라면 Nx. Lerna도 지금은 패키지 탐지와 태스크 실행을 Nx에 맡긴다
- 그만큼 도입 비용이 든다. 프로젝트를 어떻게 태깅하고 어떤 규칙을 강제할지 정해야 한다. 그럴 준비가 안 됐다면 Turborepo로 가볍게 시작하고 팀이 커지면 옮기는 순서가 흔하다(2026 기준)
모듈 경계 강제는 사람이 기억하지 않아도 결합도 규칙이 지켜지게 하는 장치다. 폴더 규약으로 같은 것을 노리는 Feature-Sliced Design은 도구 없이는 유지되기 어렵다는 점에서 대비된다. 팀 구조와 코드 구조를 맞추는 일은 콘웨이의 법칙와 이어지고, 모노레포 일반은 모노레포를 본다.
출처: Nx: Run Tasks · Nx with Vite · Run Only Tasks Affected by a PR · Cache Task Results · Caching tutorial — namedInputs · Generate Code · Automate Updating Dependencies · Enforce Module Boundaries · enforce-module-boundaries 옵션 · Mastering the Project Boundaries in Nx · Lerna and Nx