Skip to content

모듈: 패키지

[기록]

버전변경 사항
v14.13.0, v12.20.0"exports" 패턴 지원 추가.
v14.6.0, v12.19.0패키지 "imports" 필드 추가.
v13.7.0, v12.17.0조건부 exports 플래그 해제.
v13.7.0, v12.16.0--experimental-conditional-exports 옵션 제거. 12.16.0에서는 조건부 exports는 여전히 --experimental-modules 뒤에 숨겨져 있음.
v13.6.0, v12.16.0패키지 이름을 사용하여 패키지를 자체 참조하는 플래그 해제.
v12.7.0클래식 "main" 필드의 더 강력한 대안으로 "exports" package.json 필드 도입.
v12.0.0package.json "type" 필드를 통해 .js 파일 확장자를 사용하는 ES 모듈 지원 추가.

소개

패키지는 package.json 파일로 설명되는 폴더 트리입니다. 패키지는 package.json 파일이 들어 있는 폴더와 다른 package.json 파일이 들어 있는 다음 폴더 또는 node_modules라는 폴더까지의 모든 하위 폴더로 구성됩니다.

이 페이지는 Node.js에서 정의한 package.json 필드에 대한 참조와 함께 package.json 파일을 작성하는 패키지 작성자를 위한 지침을 제공합니다.

모듈 시스템 결정

소개

Node.js는 다음을 node에 초기 입력으로 전달하거나 import 문 또는 import() 표현식으로 참조할 때 ES 모듈로 취급합니다.

  • .mjs 확장자를 가진 파일.
  • 가장 가까운 상위 package.json 파일에 최상위 "type" 필드에 "module" 값이 포함된 경우 .js 확장자를 가진 파일.
  • --eval에 대한 인수로 전달되거나 --input-type=module 플래그와 함께 STDIN을 통해 node로 파이프되는 문자열.
  • ES 모듈로 성공적으로 구문 분석된 구문(예: import 또는 export 문 또는 import.meta)을 포함하는 코드(해석 방법을 명시하는 마커가 없음). 명시적 마커는 .mjs 또는 .cjs 확장자, "module" 또는 "commonjs" 값이 있는 package.json "type" 필드 또는 --input-type 플래그입니다. 동적 import() 표현식은 CommonJS 또는 ES 모듈에서 지원되며 파일을 ES 모듈로 처리하도록 강제하지 않습니다. 구문 감지를 참조하십시오.

Node.js는 다음을 node에 초기 입력으로 전달하거나 import 문 또는 import() 표현식으로 참조할 때 CommonJS로 취급합니다.

  • .cjs 확장자를 가진 파일.
  • 가장 가까운 상위 package.json 파일에 최상위 필드 "type""commonjs" 값이 포함된 경우 .js 확장자를 가진 파일.
  • --eval 또는 --print에 대한 인수로 전달되거나 --input-type=commonjs 플래그와 함께 STDIN을 통해 node로 파이프되는 문자열.
  • 상위 package.json 파일이 없거나 가장 가까운 상위 package.json 파일에 type 필드가 없고 코드가 CommonJS로 성공적으로 평가될 수 있는 .js 확장자를 가진 파일. 즉, Node.js는 이러한 "모호한" 파일을 CommonJS로 먼저 실행하려고 시도하고 CommonJS로 평가하는 데 실패하면 파서가 ES 모듈 구문을 찾았기 때문에 ES 모듈로 다시 평가합니다.

"모호한" 파일에 ES 모듈 구문을 작성하면 성능 비용이 발생하므로 작성자는 가능한 한 명시적으로 작성하는 것이 좋습니다. 특히 패키지 작성자는 모든 소스가 CommonJS인 패키지에서도 항상 package.json 파일에 "type" 필드를 포함해야 합니다. 패키지의 type을 명시적으로 표시하면 Node.js의 기본 유형이 변경되는 경우 패키지를 미래에 사용할 수 있도록 보호하고 빌드 도구와 로더가 패키지 파일이 해석되는 방법을 결정하는 데 더 쉬워집니다.

구문 감지

[기록]

버전변경 사항
v22.7.0구문 감지가 기본적으로 활성화되었습니다.
v21.1.0, v20.10.0추가됨: v21.1.0, v20.10.0

[안정성: 1 - 실험적]

안정성: 1 안정성: 1.2 - 릴리스 후보

Node.js는 모호한 입력의 소스 코드를 검사하여 ES 모듈 구문이 포함되어 있는지 확인합니다. 이러한 구문이 감지되면 입력은 ES 모듈로 처리됩니다.

모호한 입력은 다음과 같이 정의됩니다.

  • .js 확장자 또는 확장자가 없는 파일, 제어하는 package.json 파일이 없거나 type 필드가 없는 파일.
  • --input-type이 지정되지 않은 경우 문자열 입력(--eval 또는 STDIN).

ES 모듈 구문은 CommonJS로 평가될 때 오류를 발생시키는 구문으로 정의됩니다. 여기에는 다음이 포함됩니다.

  • import 문(그러나 CommonJS에서 유효한 import() 식은 제외).
  • export 문.
  • import.meta 참조.
  • 모듈 최상위 수준의 await.
  • CommonJS 래퍼 변수(require, module, exports, __dirname, __filename)의 어휘 재선언.

모듈 로더

Node.js에는 지정자를 확인하고 모듈을 로드하는 두 가지 시스템이 있습니다.

CommonJS 모듈 로더가 있습니다.

  • 완전 동기식입니다.
  • require() 호출을 처리합니다.
  • monkey patch가 가능합니다.
  • 모듈로 폴더를 지원합니다.
  • 지정자를 확인할 때 정확히 일치하는 항목이 없으면 확장자(.js, .json, 마지막으로 .node)를 추가한 다음 모듈로 폴더를 확인하려고 시도합니다.
  • .json을 JSON 텍스트 파일로 처리합니다.
  • .node 파일은 process.dlopen()으로 로드된 컴파일된 추가 모듈로 해석됩니다.
  • .json 또는 .node 확장자가 없는 모든 파일은 JavaScript 텍스트 파일로 처리합니다.
  • 모듈 그래프가 동기식(최상위 수준 await이 포함되지 않음)인 경우에만 CommonJS 모듈에서 ECMAScript 모듈을 로드하는 데 사용할 수 있습니다. ECMAScript 모듈이 아닌 JavaScript 텍스트 파일을 로드하는 데 사용하면 파일이 CommonJS 모듈로 로드됩니다.

ECMAScript 모듈 로더가 있습니다.

  • require()에 대한 모듈을 로드하는 데 사용되지 않는 한 비동기식입니다.
  • import 문과 import() 식을 처리합니다.
  • monkey patch가 불가능하며, 로더 후크를 사용하여 사용자 정의할 수 있습니다.
  • 모듈로 폴더를 지원하지 않으며, 디렉토리 인덱스(예: './startup/index.js')를 완전히 지정해야 합니다.
  • 확장자 검색을 수행하지 않습니다. 지정자가 상대적 또는 절대 파일 URL인 경우 파일 확장자를 제공해야 합니다.
  • JSON 모듈을 로드할 수 있지만, import type attribute가 필요합니다.
  • JavaScript 텍스트 파일에 대해 .js, .mjs.cjs 확장자만 허용합니다.
  • JavaScript CommonJS 모듈을 로드하는 데 사용할 수 있습니다. 이러한 모듈은 cjs-module-lexer를 통해 전달되어 정적 분석을 통해 결정할 수 있는 경우 사용 가능한 명명된 내보내기를 식별하려고 시도합니다. 가져온 CommonJS 모듈은 해당 URL이 절대 경로로 변환된 다음 CommonJS 모듈 로더를 통해 로드됩니다.

package.json 및 파일 확장자

패키지 내에서 package.json "type" 필드는 Node.js가 .js 파일을 해석하는 방법을 정의합니다. package.json 파일에 "type" 필드가 없으면 .js 파일은 CommonJS로 처리됩니다.

package.json "type" 값이 "module"이면 Node.js는 해당 패키지 내의 .js 파일을 ES 모듈 구문을 사용하는 것으로 해석합니다.

"type" 필드는 초기 진입점(node my-app.js)뿐만 아니라 import 문과 import() 표현식에서 참조하는 파일에도 적용됩니다.

js
// my-app.js, 동일한 폴더에 "type": "module"이 있는 package.json
// 파일이 있으므로 ES 모듈로 처리됩니다.

import './startup/init.js'
// ./startup에 package.json 파일이 없으므로 ES 모듈로 로드되고
// 한 수준 위에서 "type" 값을 상속합니다.

import 'commonjs-package'
// ./node_modules/commonjs-package/package.json에 "type" 필드가 없거나
// "type": "commonjs"가 포함되어 있으므로 CommonJS로 로드됩니다.

import './node_modules/commonjs-package/index.js'
// ./node_modules/commonjs-package/package.json에 "type" 필드가 없거나
// "type": "commonjs"가 포함되어 있으므로 CommonJS로 로드됩니다.

.mjs로 끝나는 파일은 가장 가까운 상위 package.json과 관계없이 항상 ES 모듈로 로드됩니다.

.cjs로 끝나는 파일은 가장 가까운 상위 package.json과 관계없이 항상 CommonJS로 로드됩니다.

js
import './legacy-file.cjs'
// .cjs는 항상 CommonJS로 로드되므로 CommonJS로 로드됩니다.

import 'commonjs-package/src/index.mjs'
// .mjs는 항상 ES 모듈로 로드되므로 ES 모듈로 로드됩니다.

.mjs.cjs 확장자는 동일한 패키지 내에서 유형을 혼합하는 데 사용할 수 있습니다.

  • "type": "module" 패키지 내에서 Node.js는 .cjs 확장자로 이름을 지정하여 특정 파일을 CommonJS로 해석하도록 지시할 수 있습니다(.js.mjs 파일은 "module" 패키지 내에서 ES 모듈로 처리되므로).
  • "type": "commonjs" 패키지 내에서 Node.js는 .mjs 확장자로 이름을 지정하여 특정 파일을 ES 모듈로 해석하도록 지시할 수 있습니다(.js.cjs 파일은 "commonjs" 패키지 내에서 CommonJS로 처리되므로).

--input-type 플래그

추가된 버전: v12.0.0

--eval (또는 -e)에 인수로 전달되거나 STDIN을 통해 node로 파이프된 문자열은 --input-type=module 플래그가 설정된 경우 ES 모듈로 처리됩니다.

bash
node --input-type=module --eval "import { sep } from 'node:path'; console.log(sep);"

echo "import { sep } from 'node:path'; console.log(sep);" | node --input-type=module

완전성을 위해 문자열 입력을 CommonJS로 명시적으로 실행하기 위한 --input-type=commonjs도 있습니다. --input-type을 지정하지 않으면 기본 동작입니다.

패키지 관리자 결정

::: 경고 [안정성: 1 - 실험적] 안정성: 1 안정성: 1 - 실험적 :::

모든 Node.js 프로젝트는 게시되면 모든 패키지 관리자로 설치할 수 있어야 하지만, 개발 팀은 종종 특정 패키지 관리자를 사용해야 합니다. 이 프로세스를 더 쉽게 만들기 위해 Node.js는 환경에서 모든 패키지 관리자를 투명하게 사용할 수 있도록 하는 것을 목표로 하는 Corepack이라는 도구를 제공합니다(Node.js가 설치된 경우).

기본적으로 Corepack은 특정 패키지 관리자를 강제로 적용하지 않으며 각 Node.js 릴리스와 연결된 일반적인 "마지막으로 알려진 정상 버전"을 사용하지만, 프로젝트의 package.json에서 "packageManager" 필드를 설정하여 이 환경을 개선할 수 있습니다.

패키지 진입점

패키지의 package.json 파일에서 두 필드는 패키지의 진입점을 정의할 수 있습니다. "main""exports". 두 필드 모두 ES 모듈 및 CommonJS 모듈 진입점에 적용됩니다.

"main" 필드는 모든 버전의 Node.js에서 지원되지만 기능이 제한적입니다. 패키지의 주요 진입점만 정의합니다.

"exports""main"에 대한 최신 대안을 제공하여 여러 진입점을 정의하고 환경 간의 조건부 진입점 해결을 지원하며 "exports"에 정의된 진입점 외에는 다른 진입점을 방지합니다. 이러한 캡슐화를 통해 모듈 작성자는 패키지의 공용 인터페이스를 명확하게 정의할 수 있습니다.

현재 지원되는 버전의 Node.js를 대상으로 하는 새 패키지의 경우 "exports" 필드를 권장합니다. Node.js 10 이하를 지원하는 패키지의 경우 "main" 필드가 필요합니다. "exports""main"이 모두 정의된 경우 지원되는 Node.js 버전에서는 "exports" 필드가 "main"보다 우선합니다.

조건부 내보내기는 패키지가 require 또는 import를 통해 참조되는지 여부를 포함하여 환경별로 다른 패키지 진입점을 정의하기 위해 "exports" 내에서 사용할 수 있습니다. 단일 패키지에서 CommonJS와 ES 모듈을 모두 지원하는 방법에 대한 자세한 내용은 이중 CommonJS/ES 모듈 패키지 섹션을 참조하십시오.

"exports" 필드를 도입하는 기존 패키지는 패키지 소비자가 정의되지 않은 진입점(예: require('your-package/package.json'))을 사용하는 것을 방지합니다. 이는 잠재적으로 주요 변경 사항이 될 수 있습니다.

"exports"를 주요 변경 없이 도입하려면 이전에 지원되었던 모든 진입점을 내보내도록 하십시오. 패키지의 공용 API가 잘 정의되도록 진입점을 명시적으로 지정하는 것이 가장 좋습니다. 예를 들어 이전에 main, lib, featurepackage.json을 내보냈던 프로젝트는 다음과 같은 package.exports를 사용할 수 있습니다.

json
{
  "name": "my-package",
  "exports": {
    ".": "./lib/index.js",
    "./lib": "./lib/index.js",
    "./lib/index": "./lib/index.js",
    "./lib/index.js": "./lib/index.js",
    "./feature": "./feature/index.js",
    "./feature/index": "./feature/index.js",
    "./feature/index.js": "./feature/index.js",
    "./package.json": "./package.json"
  }
}

또는 프로젝트는 확장된 하위 경로를 사용하거나 사용하지 않고 전체 폴더를 내보내기 패턴을 사용하여 선택할 수 있습니다.

json
{
  "name": "my-package",
  "exports": {
    ".": "./lib/index.js",
    "./lib": "./lib/index.js",
    "./lib/*": "./lib/*.js",
    "./lib/*.js": "./lib/*.js",
    "./feature": "./feature/index.js",
    "./feature/*": "./feature/*.js",
    "./feature/*.js": "./feature/*.js",
    "./package.json": "./package.json"
  }
}

위의 코드는 사소한 패키지 버전의 이전 버전과의 호환성을 제공하므로 패키지의 미래 주요 변경 사항은 노출된 특정 기능 내보내기로 내보내기를 적절하게 제한할 수 있습니다.

json
{
  "name": "my-package",
  "exports": {
    ".": "./lib/index.js",
    "./feature/*.js": "./feature/*.js",
    "./feature/internal/*": null
  }
}

기본 진입점 내보내기

새로운 패키지를 작성할 때 "exports" 필드를 사용하는 것이 좋습니다.

json
{
  "exports": "./index.js"
}

"exports" 필드가 정의되면 패키지의 모든 하위 경로는 캡슐화되어 가져오는 사람에게 더 이상 사용할 수 없습니다. 예를 들어, require('pkg/subpath.js')ERR_PACKAGE_PATH_NOT_EXPORTED 오류를 발생시킵니다.

이러한 내보내기의 캡슐화는 패키지에 대한 도구 및 semver 업그레이드를 처리할 때 패키지 인터페이스에 대한 보다 안정적인 보증을 제공합니다. require('/path/to/node_modules/pkg/subpath.js')와 같은 패키지의 절대 하위 경로를 직접 require하는 경우에도 subpath.js를 로드하므로 강력한 캡슐화는 아닙니다.

현재 지원되는 모든 Node.js 버전과 최신 빌드 도구는 "exports" 필드를 지원합니다. 이전 버전의 Node.js 또는 관련 빌드 도구를 사용하는 프로젝트의 경우, 동일한 모듈을 가리키는 "main" 필드를 "exports"와 함께 포함하여 호환성을 확보할 수 있습니다.

json
{
  "main": "./index.js",
  "exports": "./index.js"
}

하위 경로 내보내기

추가된 버전: v12.7.0

"exports" 필드를 사용하는 경우 기본 진입점을 "." 하위 경로로 처리하여 사용자 지정 하위 경로를 기본 진입점과 함께 정의할 수 있습니다.

json
{
  "exports": {
    ".": "./index.js",
    "./submodule.js": "./src/submodule.js"
  }
}

이제 "exports"에 정의된 하위 경로만 소비자가 가져올 수 있습니다.

js
import submodule from 'es-module-package/submodule.js'
// ./node_modules/es-module-package/src/submodule.js를 로드합니다.

다른 하위 경로는 오류가 발생합니다.

js
import submodule from 'es-module-package/private-module.js'
// ERR_PACKAGE_PATH_NOT_EXPORTED 오류 발생

하위 경로의 확장자

패키지 작성자는 내보내기에서 확장자 포함 (import 'pkg/subpath.js') 또는 확장자 없는 (import 'pkg/subpath') 하위 경로를 제공해야 합니다. 이렇게 하면 내보낸 각 모듈에 대해 하나의 하위 경로만 존재하여 모든 종속성이 일관된 동일한 지정자를 가져오고, 소비자에게 패키지 계약을 명확하게 유지하고 패키지 하위 경로 완성을 단순화할 수 있습니다.

전통적으로 패키지는 읽기 용이성과 패키지 내 파일의 실제 경로를 가리는 데 이점이 있는 확장자 없는 스타일을 사용하는 경향이 있었습니다.

이제 가져오기 맵이 브라우저 및 기타 JavaScript 런타임에서 패키지 확인을 위한 표준을 제공함에 따라 확장자 없는 스타일을 사용하면 가져오기 맵 정의가 커질 수 있습니다. 명시적 파일 확장자를 사용하면 가져오기 맵이 패키지 폴더 매핑을 활용하여 패키지 하위 경로 내보내기별로 별도의 맵 항목 대신 가능한 경우 여러 하위 경로를 매핑할 수 있으므로 이 문제를 피할 수 있습니다. 이는 상대적 및 절대 가져오기 지정자에서 전체 지정자 경로를 사용해야 하는 요구 사항을 반영합니다.

Exports sugar

추가됨: v12.11.0

"." 익스포트가 유일한 익스포트인 경우, "exports" 필드는 해당 케이스가 직접적인 "exports" 필드 값이 되는 데에 대한 설탕 구문(sugar)을 제공합니다.

json
{
  "exports": {
    ".": "./index.js"
  }
}

다음과 같이 쓸 수 있습니다:

json
{
  "exports": "./index.js"
}

하위 경로 임포트

추가됨: v14.6.0, v12.19.0

"exports" 필드 외에도 패키지 내에서만 임포트 스펙시피어(import specifiers)에 적용되는 프라이빗 매핑을 생성하기 위한 패키지 "imports" 필드가 있습니다.

"imports" 필드의 항목은 외부 패키지 스펙시피어와 구별되도록 항상 #로 시작해야 합니다.

예를 들어, imports 필드를 사용하여 내부 모듈에 대한 조건부 익스포트의 이점을 얻을 수 있습니다:

json
// package.json
{
  "imports": {
    "#dep": {
      "node": "dep-node-native",
      "default": "./dep-polyfill.js"
    }
  },
  "dependencies": {
    "dep-node-native": "^1.0.0"
  }
}

여기서 import '#dep'은 외부 패키지 dep-node-native의 (결과적으로 익스포트를 포함한) 해석을 얻지 못하고 대신 다른 환경에서 패키지를 기준으로 한 로컬 파일 ./dep-polyfill.js를 얻습니다.

"exports" 필드와 달리 "imports" 필드는 외부 패키지로의 매핑을 허용합니다.

imports 필드의 해석 규칙은 exports 필드와 유사합니다.

하위 경로 패턴

[기록]

버전변경 사항
v16.10.0, v14.19.0"imports" 필드에서 패턴 트레일러 지원.
v16.9.0, v14.19.0패턴 트레일러 지원.
v14.13.0, v12.20.0추가됨: v14.13.0, v12.20.0

익스포트 또는 임포트 수가 적은 패키지의 경우 각 익스포트 하위 경로 항목을 명시적으로 나열하는 것이 좋습니다. 그러나 하위 경로 수가 많은 패키지의 경우 package.json이 부풀어지고 유지 관리 문제가 발생할 수 있습니다.

이러한 사용 사례에서는 하위 경로 익스포트 패턴을 대신 사용할 수 있습니다:

json
// ./node_modules/es-module-package/package.json
{
  "exports": {
    "./features/*.js": "./src/features/*.js"
  },
  "imports": {
    "#internal/*.js": "./src/internal/*.js"
  }
}

* 매핑은 중첩된 하위 경로를 문자열 대체 구문으로만 노출합니다.

오른쪽의 *의 모든 인스턴스는 / 구분 기호가 포함된 경우를 포함하여 이 값으로 대체됩니다.

js
import featureX from 'es-module-package/features/x.js'
// ./node_modules/es-module-package/src/features/x.js를 로드합니다.

import featureY from 'es-module-package/features/y/y.js'
// ./node_modules/es-module-package/src/features/y/y.js를 로드합니다.

import internalZ from '#internal/z.js'
// ./node_modules/es-module-package/src/internal/z.js를 로드합니다.

이것은 파일 확장자에 대한 특수 처리 없이 직접적인 정적 매칭 및 대체입니다. 매핑의 양쪽에 "*.js"를 포함하면 노출된 패키지 익스포트가 JS 파일로만 제한됩니다.

패키지에 대한 개별 익스포트는 패키지 내 파일 목록에 대해 오른쪽 대상 패턴을 ** glob으로 취급하여 결정할 수 있기 때문에 익스포트가 정적으로 열거 가능하다는 속성은 익스포트 패턴으로 유지됩니다. node_modules 경로가 익스포트 대상에서 금지되어 있기 때문에 이러한 확장은 패키지 자체의 파일에만 종속됩니다.

패턴에서 프라이빗 하위 폴더를 제외하려면 null 대상을 사용할 수 있습니다:

json
// ./node_modules/es-module-package/package.json
{
  "exports": {
    "./features/*.js": "./src/features/*.js",
    "./features/private-internal/*": null
  }
}
js
import featureInternal from 'es-module-package/features/private-internal/m.js'
// 오류 발생: ERR_PACKAGE_PATH_NOT_EXPORTED

import featureX from 'es-module-package/features/x.js'
// ./node_modules/es-module-package/src/features/x.js를 로드합니다.

조건부 내보내기

[기록]

버전변경 사항
v13.7.0, v12.16.0조건부 내보내기 플래그 제거.
v13.2.0, v12.16.0추가됨: v13.2.0, v12.16.0

조건부 내보내기는 특정 조건에 따라 다른 경로로 매핑하는 방법을 제공합니다. CommonJS 및 ES 모듈 가져오기 모두에 대해 지원됩니다.

예를 들어 require()import에 대해 다른 ES 모듈 내보내기를 제공하려는 패키지는 다음과 같이 작성할 수 있습니다.

json
// package.json
{
  "exports": {
    "import": "./index-module.js",
    "require": "./index-require.cjs"
  },
  "type": "module"
}

Node.js는 조건이 정의되어야 하는 가장 구체적인 것부터 가장 덜 구체적인 순서로 나열된 다음 조건을 구현합니다.

  • "node-addons" - "node"와 유사하며 모든 Node.js 환경에 대해 일치합니다. 이 조건은 네이티브 C++ 애드온을 사용하는 진입점을 보다 보편적이고 네이티브 애드온에 의존하지 않는 진입점과 대조적으로 제공하는 데 사용할 수 있습니다. 이 조건은 --no-addons 플래그를 통해 비활성화할 수 있습니다.
  • "node" - 모든 Node.js 환경에 대해 일치합니다. CommonJS 또는 ES 모듈 파일일 수 있습니다. 대부분의 경우 Node.js 플랫폼을 명시적으로 호출하는 것은 필요하지 않습니다.
  • "import" - 패키지가 import 또는 import()를 통해 또는 ECMAScript 모듈 로더에 의한 모든 최상위 가져오기 또는 확인 작업을 통해 로드될 때 일치합니다. 대상 파일의 모듈 형식에 관계없이 적용됩니다. 항상 "require"와 상호 배타적입니다.
  • "require" - 패키지가 require()를 통해 로드될 때 일치합니다. 참조된 파일은 대상 파일의 모듈 형식에 관계없이 조건이 일치하지만 require()로 로드할 수 있어야 합니다. 예상 형식에는 CommonJS, JSON, 네이티브 애드온 및 ES 모듈이 포함됩니다. 항상 "import"와 상호 배타적입니다.
  • "module-sync" - 패키지가 import, import() 또는 require()를 통해 로드되는지에 관계없이 일치합니다. 형식은 모듈 그래프에 최상위 await가 포함되지 않은 ES 모듈로 예상됩니다. 포함된 경우 모듈을 require()로 사용하면 ERR_REQUIRE_ASYNC_MODULE이 발생합니다.
  • "default" - 항상 일치하는 일반적인 폴백입니다. CommonJS 또는 ES 모듈 파일일 수 있습니다. 이 조건은 항상 마지막에 와야 합니다.

"exports" 객체 내에서 키 순서가 중요합니다. 조건 일치 중에 이전 항목의 우선 순위가 더 높고 이후 항목보다 우선합니다. 일반적인 규칙은 조건이 객체 순서에서 가장 구체적인 것부터 가장 덜 구체적인 순서로 지정되어야 한다는 것입니다.

"import""require" 조건을 사용하면 몇 가지 위험이 발생할 수 있으며 이에 대한 자세한 내용은 듀얼 CommonJS/ES 모듈 패키지 섹션에 설명되어 있습니다.

"node-addons" 조건은 네이티브 C++ 애드온을 사용하는 진입점을 제공하는 데 사용할 수 있습니다. 그러나 이 조건은 --no-addons 플래그를 통해 비활성화할 수 있습니다. "node-addons"를 사용할 때 "default"를 네이티브 애드온 대신 WebAssembly를 사용하는 등 보다 보편적인 진입점을 제공하는 개선 사항으로 취급하는 것이 좋습니다.

조건부 내보내기는 예를 들어 다음으로 내보내기 하위 경로로 확장할 수도 있습니다.

json
{
  "exports": {
    ".": "./index.js",
    "./feature.js": {
      "node": "./feature-node.js",
      "default": "./feature.js"
    }
  }
}

require('pkg/feature.js')import 'pkg/feature.js'가 Node.js와 다른 JS 환경 간에 다른 구현을 제공할 수 있는 패키지를 정의합니다.

환경 분기를 사용할 때는 가능한 한 항상 "default" 조건을 포함하십시오. "default" 조건을 제공하면 알 수 없는 모든 JS 환경이 이 보편적 구현을 사용할 수 있도록 보장하여 이러한 JS 환경이 조건부 내보내기가 있는 패키지를 지원하기 위해 기존 환경인 척할 필요가 없도록 합니다. 이러한 이유로 "node""default" 조건 분기를 사용하는 것이 일반적으로 "node""browser" 조건 분기를 사용하는 것보다 선호됩니다.

중첩 조건

직접 매핑 외에도 Node.js는 중첩된 조건 객체도 지원합니다.

예를 들어 Node.js에서만 듀얼 모드 진입점을 갖고 브라우저에서는 갖지 않는 패키지를 정의하려면 다음과 같이 할 수 있습니다.

json
{
  "exports": {
    "node": {
      "import": "./feature-node.mjs",
      "require": "./feature-node.cjs"
    },
    "default": "./feature.mjs"
  }
}

조건은 플랫 조건과 마찬가지로 순서대로 계속 매칭됩니다. 중첩된 조건에 매핑이 없으면 부모 조건의 나머지 조건을 계속 확인합니다. 이러한 방식으로 중첩된 조건은 중첩된 JavaScript if 문과 유사하게 동작합니다.

사용자 조건 해결

추가된 버전: v14.9.0, v12.19.0

Node.js를 실행할 때 --conditions 플래그를 사용하여 사용자 정의 조건을 추가할 수 있습니다.

bash
node --conditions=development index.js

그러면 패키지 가져오기 및 내보내기에서 "development" 조건이 해결되고 기존 "node", "node-addons", "default", "import""require" 조건이 적절하게 해결됩니다.

반복 플래그를 사용하여 사용자 정의 조건을 원하는 만큼 설정할 수 있습니다.

일반적인 조건에는 영숫자 문자만 포함해야 하며 필요한 경우 ":" , "-" 또는 "="를 구분 기호로 사용해야 합니다. 그 외의 것은 node 외부에서 호환성 문제가 발생할 수 있습니다.

node에서 조건에는 제한 사항이 거의 없지만 특히 다음이 포함됩니다.

커뮤니티 조건 정의

"import", "require", "node", "module-sync", "node-addons""default" 조건 외의 조건 문자열Node.js 코어에서 구현됨은 기본적으로 무시됩니다.

다른 플랫폼에서는 다른 조건을 구현할 수 있으며, Node.js에서 --conditions / -C 플래그를 통해 사용자 조건을 활성화할 수 있습니다.

사용자 지정 패키지 조건에는 올바른 사용을 보장하기 위한 명확한 정의가 필요하므로, 에코시스템 조정을 지원하기 위해 일반적인 알려진 패키지 조건과 해당 엄격한 정의 목록이 아래에 제공됩니다.

  • "types" - 형식 시스템에서 지정된 내보내기에 대한 형식 파일을 해결하는 데 사용할 수 있습니다. 이 조건은 항상 먼저 포함해야 합니다.
  • "browser" - 모든 웹 브라우저 환경.
  • "development" - 개발 전용 환경 진입점을 정의하는 데 사용할 수 있습니다. 예를 들어 개발 모드에서 실행할 때 더 나은 오류 메시지와 같은 추가 디버깅 컨텍스트를 제공할 수 있습니다. 항상 "production"과 상호 배타적이어야 합니다.
  • "production" - 프로덕션 환경 진입점을 정의하는 데 사용할 수 있습니다. 항상 "development"과 상호 배타적이어야 합니다.

다른 런타임의 경우 플랫폼별 조건 키 정의는 WinterCG에서 런타임 키 제안 사양으로 관리됩니다.

새로운 조건 정의는 이 섹션의 Node.js 문서에 풀 요청을 만들어 이 목록에 추가할 수 있습니다. 여기에 새 조건 정의를 나열하기 위한 요구 사항은 다음과 같습니다.

  • 정의는 모든 구현자에게 명확하고 모호하지 않아야 합니다.
  • 조건이 필요한 이유에 대한 사용 사례를 명확하게 정당화해야 합니다.
  • 충분한 기존 구현 사용이 있어야 합니다.
  • 조건 이름은 광범위하게 사용되는 다른 조건 정의 또는 조건과 충돌하지 않아야 합니다.
  • 조건 정의 목록은 그렇지 않으면 불가능한 에코시스템에 대한 조정 이점을 제공해야 합니다. 예를 들어 회사별 또는 애플리케이션별 조건의 경우에는 반드시 해당되지 않습니다.
  • 조건은 Node.js 사용자가 Node.js 코어 문서에 포함될 것으로 예상하는 것이어야 합니다. "types" 조건이 좋은 예입니다. 런타임 키 제안에는 실제로 속하지 않지만 여기 Node.js 문서에는 잘 맞습니다.

위의 정의는 적절한 시기에 전용 조건 레지스트리로 이동할 수 있습니다.

패키지 이름을 사용하여 자체 참조하기

[기록]

버전변경 사항
v13.6.0, v12.16.0패키지 이름을 사용하여 자체 참조하는 것을 플래그 해제합니다.
v13.1.0, v12.16.0추가됨: v13.1.0, v12.16.0

패키지 내에서 패키지의 package.json "exports" 필드에 정의된 값은 패키지 이름을 통해 참조할 수 있습니다. 예를 들어, package.json이 다음과 같다고 가정합니다.

json
// package.json
{
  "name": "a-package",
  "exports": {
    ".": "./index.mjs",
    "./foo.js": "./foo.js"
  }
}

그러면 해당 패키지의 모든 모듈은 패키지 자체의 내보내기를 참조할 수 있습니다.

js
// ./a-module.mjs
import { something } from 'a-package' // ./index.mjs에서 "something"을 가져옵니다.

자체 참조는 package.json"exports"가 있는 경우에만 사용할 수 있으며, "exports"(package.json에 있는)에서 허용하는 것만 가져올 수 있습니다. 따라서 이전 패키지가 주어지면 아래 코드는 런타임 오류를 생성합니다.

js
// ./another-module.mjs

// ./m.mjs에서 "another"를 가져옵니다. 실패 이유는
// "package.json"의 "exports" 필드에서
// "./m.mjs"라는 내보내기를 제공하지 않기 때문입니다.
import { another } from 'a-package/m.mjs'

자체 참조는 ES 모듈과 CommonJS 모듈 모두에서 require를 사용할 때도 사용할 수 있습니다. 예를 들어, 이 코드는 다음과 같이 작동합니다.

js
// ./a-module.js
const { something } = require('a-package/foo.js') // ./foo.js에서 로드합니다.

마지막으로, 자체 참조는 범위가 지정된 패키지에서도 작동합니다. 예를 들어, 이 코드는 다음과 같이 작동합니다.

json
// package.json
{
  "name": "@my/package",
  "exports": "./index.js"
}
js
// ./index.js
module.exports = 42
js
// ./other.js
console.log(require('@my/package'))
bash
$ node other.js
42

이중 CommonJS/ES 모듈 패키지

자세한 내용은 패키지 예제 저장소를 참조하십시오.

Node.js package.json 필드 정의

이 섹션에서는 Node.js 런타임에서 사용하는 필드를 설명합니다. 다른 도구(예: npm)는 Node.js에서 무시하고 여기에 문서화되지 않은 추가 필드를 사용합니다.

package.json 파일의 다음 필드는 Node.js에서 사용됩니다.

  • "name" - 패키지 내에서 명명된 가져오기를 사용할 때 관련이 있습니다. 패키지 관리자에서도 패키지 이름으로 사용됩니다.
  • "main" - 내보내기가 지정되지 않은 경우와 내보내기 도입 이전의 Node.js 버전에서 패키지를 로드할 때 기본 모듈입니다.
  • "packageManager" - 패키지에 기여할 때 권장되는 패키지 관리자입니다. Corepack shim에서 활용됩니다.
  • "type" - .js 파일을 CommonJS 또는 ES 모듈로 로드할지 여부를 결정하는 패키지 유형입니다.
  • "exports" - 패키지 내보내기 및 조건부 내보내기입니다. 존재하면 패키지 내에서 로드할 수 있는 하위 모듈을 제한합니다.
  • "imports" - 패키지 자체 내의 모듈에서 사용할 수 있는 패키지 가져오기입니다.

"name"

[기록]

버전변경 사항
v13.6.0, v12.16.0--experimental-resolve-self 옵션 제거.
v13.1.0, v12.16.0추가됨: v13.1.0, v12.16.0
json
{
  "name": "package-name"
}

"name" 필드는 패키지 이름을 정의합니다. npm 레지스트리에 게시하려면 특정 요구 사항을 충족하는 이름이 필요합니다.

"name" 필드는 "exports" 필드와 함께 사용하여 패키지 이름을 사용하여 패키지를 자체 참조할 수 있습니다.

"main"

추가됨: v0.4.0

json
{
  "main": "./index.js"
}

"main" 필드는 node_modules 조회를 통해 이름으로 가져올 때 패키지의 진입점을 정의합니다. 해당 값은 경로입니다.

패키지에 "exports" 필드가 있는 경우, 이름으로 패키지를 가져올 때 "main" 필드보다 우선합니다.

또한 패키지 디렉터리가 require()를 통해 로드될 때 사용되는 스크립트를 정의합니다.

js
// ./path/to/directory/index.js로 확인됩니다.
require('./path/to/directory')

"packageManager"

추가됨: v16.9.0, v14.19.0

[안정성: 1 - 실험적]

안정성: 1 안정성: 1 - 실험적

json
{
  "packageManager": "<패키지 관리자 이름>@<버전>"
}

"packageManager" 필드는 현재 프로젝트에서 작업할 때 사용할 것으로 예상되는 패키지 관리자를 정의합니다. 지원되는 패키지 관리자 중 하나로 설정할 수 있으며, 팀이 Node.js 외에 다른 것을 설치할 필요 없이 정확히 동일한 패키지 관리자 버전을 사용하도록 합니다.

이 필드는 현재 실험적이며 옵트인해야 합니다. 절차에 대한 자세한 내용은 Corepack 페이지를 확인하십시오.

"type"

[History]

버전변경 사항
v13.2.0, v12.17.0--experimental-modules 플래그 해제.
v12.0.0추가됨: v12.0.0

"type" 필드는 해당 package.json 파일을 가장 가까운 부모로 가진 모든 .js 파일에 대해 Node.js가 사용하는 모듈 형식을 정의합니다.

.js로 끝나는 파일은 가장 가까운 부모 package.json 파일에 "type" 최상위 필드가 "module" 값을 가지는 경우 ES 모듈로 로드됩니다.

가장 가까운 부모 package.json은 현재 폴더, 해당 폴더의 부모 등을 탐색하여 node_modules 폴더 또는 볼륨 루트에 도달할 때까지 발견되는 첫 번째 package.json으로 정의됩니다.

json
// package.json
{
  "type": "module"
}
bash
# 위의 package.json과 동일한 폴더 {#in-same-folder-as-preceding-packagejson}
node my-app.js # ES 모듈로 실행

가장 가까운 부모 package.json"type" 필드가 없거나 "type": "commonjs"가 포함된 경우, .js 파일은 CommonJS로 처리됩니다. 볼륨 루트에 도달했고 package.json이 발견되지 않으면 .js 파일은 CommonJS로 처리됩니다.

.js 파일의 import 문은 가장 가까운 부모 package.json"type": "module"이 포함되어 있는 경우 ES 모듈로 처리됩니다.

js
// my-app.js, 위의 예와 동일
import './startup.js' // package.json으로 인해 ES 모듈로 로드됨

"type" 필드의 값에 관계없이 .mjs 파일은 항상 ES 모듈로 처리되고 .cjs 파일은 항상 CommonJS로 처리됩니다.

"exports"

[History]

버전변경 사항
v14.13.0, v12.20.0"exports" 패턴 지원 추가.
v13.7.0, v12.17.0조건부 내보내기 플래그 해제.
v13.7.0, v12.16.0논리적 조건부 내보내기 순서 구현.
v13.7.0, v12.16.0--experimental-conditional-exports 옵션 제거. 12.16.0에서 조건부 내보내기는 여전히 --experimental-modules에 따라 숨겨져 있습니다.
v13.2.0, v12.16.0조건부 내보내기 구현.
v12.7.0추가됨: v12.7.0
json
{
  "exports": "./index.js"
}

"exports" 필드는 node_modules 조회를 통해 로드되거나 자체 이름에 대한 자체 참조를 통해 이름으로 가져온 패키지의 진입점을 정의할 수 있습니다. 이는 하위 경로 내보내기조건부 내보내기를 정의하면서 내부적으로 내보내지 않은 모듈을 캡슐화할 수 있는 "main"에 대한 대안으로 Node.js 12+에서 지원됩니다.

조건부 내보내기는 패키지가 require를 통해 참조되는지 또는 import를 통해 참조되는지 여부를 포함하여 환경별로 다른 패키지 진입점을 정의하기 위해 "exports" 내에서 사용할 수도 있습니다.

"exports"에 정의된 모든 경로는 ./로 시작하는 상대 파일 URL이어야 합니다.

"imports"

추가된 버전: v14.6.0, v12.19.0

json
// package.json
{
  "imports": {
    "#dep": {
      "node": "dep-node-native",
      "default": "./dep-polyfill.js"
    }
  },
  "dependencies": {
    "dep-node-native": "^1.0.0"
  }
}

imports 필드의 항목은 #로 시작하는 문자열이어야 합니다.

패키지 import는 외부 패키지로의 매핑을 허용합니다.

이 필드는 현재 패키지에 대한 하위 경로 import를 정의합니다.