🔧 DevOps

Nx

모노레포 개발 도구 및 빌드 시스템

📖 상세 설명

Nx는 모노레포(Monorepo) 환경에서 빌드, 테스트, 린트를 효율적으로 수행하는 빌드 시스템이자 개발 도구입니다. Google, Facebook 같은 대형 기업에서 사용하는 모노레포 방식을 일반 팀도 쉽게 도입할 수 있게 해줍니다. Nrwl에서 개발했으며, 현재 매우 활발히 발전 중입니다.

Nx의 핵심 기능은 영향받은 프로젝트만 빌드/테스트하는 Affected Commands, 이전 빌드 결과를 재사용하는 Computation Caching, 로컬과 원격의 Task를 병렬 실행하는 Distributed Task Execution입니다. 100개 패키지가 있어도 변경된 3개 패키지만 빌드하니 CI 시간이 획기적으로 단축됩니다.

프로젝트 간 의존성 그래프를 자동으로 분석하여 올바른 빌드 순서를 결정합니다. `nx graph` 명령으로 의존성을 시각화할 수 있어 아키텍처 파악에도 유용합니다. 순환 의존성이 생기면 경고해주어 코드베이스 건강을 유지합니다.

Nx는 React, Angular, Next.js, Node.js, NestJS 등 다양한 프레임워크를 지원하며, 플러그인 시스템으로 커스텀 generator와 executor를 만들 수 있습니다. Turborepo, Lerna 같은 다른 모노레포 도구와 비교해 더 강력한 캐싱과 분산 실행 기능을 제공합니다.

💻 코드 예제

// nx.json - Nx 워크스페이스 설정
{
  "$schema": "./node_modules/nx/schemas/nx-schema.json",
  "namedInputs": {
    "default": ["{projectRoot}/**/*", "sharedGlobals"],
    "production": [
      "default",
      "!{projectRoot}/**/?(*.)+(spec|test).[jt]s?(x)?(.snap)",
      "!{projectRoot}/tsconfig.spec.json"
    ],
    "sharedGlobals": ["{workspaceRoot}/babel.config.json"]
  },
  "targetDefaults": {
    "build": {
      "dependsOn": ["^build"],  // 의존 프로젝트 먼저 빌드
      "inputs": ["production", "^production"],
      "cache": true
    },
    "test": {
      "inputs": ["default", "^production", "{workspaceRoot}/jest.preset.js"],
      "cache": true
    },
    "lint": {
      "inputs": ["default", "{workspaceRoot}/.eslintrc.json"],
      "cache": true
    }
  },
  "tasksRunnerOptions": {
    "default": {
      "runner": "nx/tasks-runners/default",
      "options": {
        "cacheableOperations": ["build", "lint", "test"],
        "parallel": 3
      }
    }
  },
  // Nx Cloud로 원격 캐시 활성화
  "nxCloudAccessToken": "your-access-token"
}
// apps/web/project.json - 프로젝트 설정
{
  "name": "web",
  "$schema": "../../node_modules/nx/schemas/project-schema.json",
  "sourceRoot": "apps/web/src",
  "projectType": "application",
  "tags": ["scope:web", "type:app"],
  "targets": {
    "build": {
      "executor": "@nx/next:build",
      "outputs": ["{options.outputPath}"],
      "options": {
        "outputPath": "dist/apps/web"
      },
      "configurations": {
        "production": {
          "optimization": true,
          "sourceMap": false
        }
      }
    },
    "serve": {
      "executor": "@nx/next:server",
      "options": {
        "buildTarget": "web:build",
        "dev": true
      }
    }
  },
  "implicitDependencies": ["shared-ui", "api-client"]
}

// 자주 사용하는 Nx CLI 명령어
// nx run web:build              - 특정 프로젝트 빌드
// nx affected:build --base=main  - 변경된 프로젝트만 빌드
// nx run-many -t build -p web,api  - 여러 프로젝트 빌드
// nx graph                       - 의존성 그래프 시각화
// nx reset                       - 캐시 초기화

🗣️ 실무에서 이렇게 말해요

리드: "프론트엔드랑 백엔드 레포 분리돼있으니까 공통 타입 정의 공유가 힘들어요. Nx로 모노레포 전환하면 어떨까요?"

주니어: "모노레포면 CI가 오래 걸리지 않나요?"

시니어: "Nx affected 쓰면 변경된 프로젝트만 빌드돼요. 그리고 Nx Cloud 연결하면 캐시 공유돼서 로컬에서도 CI에서도 빨라져요."

면접관: "대규모 프로젝트에서 빌드 시간 최적화 경험이 있나요?"

지원자: "12개 패키지의 모노레포에서 Nx를 도입했습니다. affected 명령으로 변경 영향받는 패키지만 빌드해서 PR 빌드 시간이 평균 15분에서 4분으로 줄었고, Nx Cloud로 원격 캐시를 공유해서 팀원 모두 캐시 히트율이 80% 이상 나왔습니다. 의존성 그래프로 순환 참조도 발견해서 정리했어요."

리뷰어: "이 패키지 tags에 scope:shared 붙여주세요. 그래야 module-boundary 규칙으로 잘못된 import 막을 수 있어요."

작성자: "project.json에 tags 추가하고, .eslintrc에 @nx/enforce-module-boundaries 규칙도 설정하겠습니다."

⚠️ 주의사항

📚 더 배우기