Skip to content

모듈: CommonJS 모듈

[Stable: 2 - Stable]

Stable: 2 Stability: 2 - Stable

CommonJS 모듈은 Node.js용 JavaScript 코드를 패키징하는 원래 방식입니다. Node.js는 브라우저 및 기타 JavaScript 런타임에서 사용되는 ECMAScript 모듈 표준도 지원합니다.

Node.js에서 각 파일은 별도의 모듈로 처리됩니다. 예를 들어, foo.js라는 파일을 고려해 보겠습니다.

js
const circle = require('./circle.js')
console.log(`The area of a circle of radius 4 is ${circle.area(4)}`)

첫 번째 줄에서 foo.jsfoo.js와 같은 디렉터리에 있는 circle.js 모듈을 로드합니다.

다음은 circle.js의 내용입니다.

js
const { PI } = Math

exports.area = r => PI * r ** 2

exports.circumference = r => 2 * PI * r

circle.js 모듈은 area()circumference() 함수를 내보냈습니다. 특수한 exports 객체에 추가 속성을 지정하여 함수와 객체를 모듈의 루트에 추가합니다.

모듈 내부의 변수는 Node.js에서 모듈이 함수로 래핑되기 때문에( 모듈 래퍼 참조) 비공개로 유지됩니다. 이 예에서 PI 변수는 circle.js에 대해 비공개입니다.

module.exports 속성에는 새 값(함수 또는 객체 등)을 할당할 수 있습니다.

다음 코드에서 bar.js는 Square 클래스를 내보내는 square 모듈을 사용합니다.

js
const Square = require('./square.js')
const mySquare = new Square(2)
console.log(`The area of mySquare is ${mySquare.area()}`)

square 모듈은 square.js에 정의되어 있습니다.

js
// exports에 할당해도 모듈이 수정되지 않으므로 module.exports를 사용해야 합니다.
module.exports = class Square {
  constructor(width) {
    this.width = width
  }

  area() {
    return this.width ** 2
  }
}

CommonJS 모듈 시스템은 module 코어 모듈에 구현되어 있습니다.

사용 설정

Node.js에는 CommonJS 모듈과 ECMAScript 모듈의 두 가지 모듈 시스템이 있습니다.

기본적으로 Node.js는 다음을 CommonJS 모듈로 처리합니다.

  • .cjs 확장자를 가진 파일;
  • 가장 가까운 상위 package.json 파일에 상위 수준 필드 "type"이 있고 값이 "commonjs".js 확장자를 가진 파일.
  • 가장 가까운 상위 package.json 파일에 상위 수준 필드 "type"이 없거나 상위 폴더에 package.json이 없는 경우, 또는 ES 모듈로 평가하지 않으면 오류가 발생하는 구문이 포함되어 있지 않은 경우 .js 확장자 또는 확장자가 없는 파일; 패키지 작성자는 모든 소스가 CommonJS인 패키지에서도 "type" 필드를 포함해야 합니다. 패키지의 type을 명시적으로 지정하면 빌드 도구와 로더가 패키지의 파일을 어떻게 해석해야 하는지 쉽게 판단할 수 있습니다.
  • .mjs, .cjs, .json, .node 또는 .js가 아닌 확장자를 가진 파일(가장 가까운 상위 package.json 파일에 상위 수준 필드 "type"이 있고 값이 "module"인 경우, 이러한 파일은 require()를 통해 포함될 때만 CommonJS 모듈로 인식되며, 프로그램의 명령줄 진입점으로 사용될 때는 인식되지 않습니다).

자세한 내용은 모듈 시스템 결정을 참조하십시오.

require()를 호출하면 항상 CommonJS 모듈 로더를 사용합니다. import()를 호출하면 항상 ECMAScript 모듈 로더를 사용합니다.

메인 모듈 접근

파일이 Node.js에서 직접 실행될 때, require.main은 해당 module로 설정됩니다. 즉, require.main === module을 테스트하여 파일이 직접 실행되었는지 여부를 판단할 수 있습니다.

foo.js 파일의 경우, node foo.js를 통해 실행되면 true가 되지만, require('./foo')를 통해 실행되면 false가 됩니다.

진입점이 CommonJS 모듈이 아닌 경우, require.mainundefined이며, 메인 모듈에 접근할 수 없습니다.

패키지 관리자 팁

Node.js require() 함수의 의미 체계는 합리적인 디렉토리 구조를 지원할 수 있도록 충분히 일반적으로 설계되었습니다. dpkg, rpm, npm과 같은 패키지 관리자 프로그램은 수정 없이 Node.js 모듈로부터 네이티브 패키지를 빌드할 수 있을 것입니다.

다음은 작동할 수 있는 제안된 디렉토리 구조입니다.

/usr/lib/node/<some-package>/<some-version>에 특정 버전의 패키지 내용을 저장한다고 가정해 보겠습니다.

패키지는 서로 의존할 수 있습니다. foo 패키지를 설치하려면 bar 패키지의 특정 버전을 설치해야 할 수 있습니다. bar 패키지는 자체적으로 종속성을 가질 수 있으며, 경우에 따라 충돌하거나 순환 종속성을 형성할 수도 있습니다.

Node.js는 로드하는 모든 모듈의 realpath를 조회하고(즉, 심볼릭 링크를 해결하고) 그런 다음 node_modules 폴더에서 종속성을 찾으므로, 다음 아키텍처를 사용하여 이러한 상황을 해결할 수 있습니다.

  • /usr/lib/node/foo/1.2.3/: foo 패키지 버전 1.2.3의 내용.
  • /usr/lib/node/bar/4.3.2/: foo가 의존하는 bar 패키지의 내용.
  • /usr/lib/node/foo/1.2.3/node_modules/bar: /usr/lib/node/bar/4.3.2/에 대한 심볼릭 링크.
  • /usr/lib/node/bar/4.3.2/node_modules/*: bar가 의존하는 패키지에 대한 심볼릭 링크.

따라서 순환이 발생하거나 종속성 충돌이 발생하더라도 모든 모듈은 사용할 수 있는 종속성 버전을 얻을 수 있습니다.

foo 패키지의 코드가 require('bar')를 실행하면 /usr/lib/node/foo/1.2.3/node_modules/bar에 심볼릭 링크된 버전을 가져옵니다. 그런 다음 bar 패키지의 코드가 require('quux')를 호출하면 /usr/lib/node/bar/4.3.2/node_modules/quux에 심볼릭 링크된 버전을 가져옵니다.

또한 모듈 조회 프로세스를 더욱 최적화하기 위해 패키지를 /usr/lib/node에 직접 배치하는 대신 /usr/lib/node_modules/<name>/<version>에 배치할 수 있습니다. 그러면 Node.js는 /usr/node_modules 또는 /node_modules에서 누락된 종속성을 찾는 것을 건너뜁니다.

Node.js REPL에서 모듈을 사용할 수 있도록 하려면 /usr/lib/node_modules 폴더를 $NODE_PATH 환경 변수에 추가하는 것이 유용할 수 있습니다. node_modules 폴더를 사용하는 모듈 조회는 모두 상대적이며 require()를 호출하는 파일의 실제 경로를 기반으로 하므로 패키지는 어디에나 있을 수 있습니다.

require()를 사용한 ECMAScript 모듈 로딩

[히스토리]

버전변경 사항
v23.5.0이 기능은 기본적으로 더 이상 실험적 경고를 내보내지 않지만, --trace-require-module을 통해 경고를 계속 내보낼 수 있습니다.
v23.0.0이 기능은 더 이상 --experimental-require-module CLI 플래그 뒤에 있지 않습니다.
v23.0.0require(esm)에서 'module.exports' 상호 운용성 내보내기를 지원합니다.
v22.0.0, v20.17.0추가됨: v22.0.0, v20.17.0

[안정성: 1 - 실험적]

안정성: 1 안정성: 1.2 - 출시 후보

.mjs 확장자는 ECMAScript 모듈에 예약되어 있습니다. 어떤 파일이 ECMAScript 모듈로 구문 분석되는지에 대한 자세한 내용은 모듈 시스템 결정 섹션을 참조하십시오.

require()는 다음 요구 사항을 충족하는 ECMAScript 모듈 로딩만 지원합니다.

  • 모듈이 완전히 동기적입니다(최상위 await가 포함되지 않음).
  • 다음 조건 중 하나를 충족합니다.

로드되는 ES 모듈이 요구 사항을 충족하는 경우 require()는 모듈을 로드하고 모듈 네임스페이스 객체를 반환할 수 있습니다. 이 경우 동적 import()와 유사하지만 동기적으로 실행되고 네임스페이스 객체를 직접 반환합니다.

다음 ES 모듈을 사용하여:

js
// distance.mjs
export function distance(a, b) {
  return (b.x - a.x) ** 2 + (b.y - a.y) ** 2
}
js
// point.mjs
export default class Point {
  constructor(x, y) {
    this.x = x
    this.y = y
  }
}

CommonJS 모듈은 require()를 사용하여 이들을 로드할 수 있습니다.

js
const distance = require('./distance.mjs')
console.log(distance)
// [Module: null prototype] {
//   distance: [Function: distance]
// }

const point = require('./point.mjs')
console.log(point)
// [Module: null prototype] {
//   default: [class Point],
//   __esModule: true,
// }

ES 모듈을 CommonJS로 변환하는 기존 도구와의 상호 운용성을 위해, require()를 통해 실제 ES 모듈을 로드할 수 있는 CommonJS는 default 내보내기가 있는 경우 __esModule: true 속성을 포함하는 네임스페이스를 반환합니다. 따라서 도구에 의해 생성된 코드는 실제 ES 모듈의 기본 내보내기를 인식할 수 있습니다. 네임스페이스가 이미 __esModule을 정의한 경우 이것은 추가되지 않습니다. 이 속성은 실험적이며 향후 변경될 수 있습니다. 기존 에코시스템 규칙을 따르는 CommonJS 모듈로 ES 모듈을 변환하는 도구에서만 사용해야 합니다. CommonJS에서 직접 작성된 코드는 이를 사용하지 않아야 합니다.

ES 모듈에 명명된 내보내기와 기본 내보내기가 모두 포함되어 있는 경우 require()에서 반환되는 결과는 모듈 네임스페이스 객체이며, 여기서 기본 내보내기는 .default 속성에 배치됩니다. import()에서 반환되는 결과와 유사합니다. require(esm)에서 직접 반환할 내용을 사용자 지정하려면 ES 모듈은 문자열 이름 "module.exports"를 사용하여 원하는 값을 내보낼 수 있습니다.

js
// point.mjs
export default class Point {
  constructor(x, y) {
    this.x = x
    this.y = y
  }
}

// `distance`는 이 모듈의 CommonJS 사용자가 `Point`에 정적 속성으로 추가하지 않는 한 손실됩니다.
export function distance(a, b) {
  return (b.x - a.x) ** 2 + (b.y - a.y) ** 2
}
export { Point as 'module.exports' }
js
const Point = require('./point.mjs')
console.log(Point) // [class Point]

// 'module.exports'가 사용될 때 명명된 내보내기는 손실됩니다.
const { distance } = require('./point.mjs')
console.log(distance) // undefined

위 예에서 module.exports 내보내기 이름이 사용될 때 명명된 내보내기는 CommonJS 사용자에게 손실됩니다. CommonJS 사용자가 명명된 내보내기에 계속 액세스할 수 있도록 모듈은 기본 내보내기가 속성으로 첨부된 명명된 내보내기를 포함하는 객체임을 확인할 수 있습니다. 예를 들어 위 예에서 distance는 기본 내보내기인 Point 클래스에 정적 메서드로 첨부될 수 있습니다.

js
export function distance(a, b) {
  return (b.x - a.x) ** 2 + (b.y - a.y) ** 2
}

export default class Point {
  constructor(x, y) {
    this.x = x
    this.y = y
  }
  static distance = distance
}

export { Point as 'module.exports' }
js
const Point = require('./point.mjs')
console.log(Point) // [class Point]

const { distance } = require('./point.mjs')
console.log(distance) // [Function: distance]

require()되는 모듈에 최상위 await가 포함되어 있거나, import하는 모듈 그래프에 최상위 await가 포함되어 있는 경우 ERR_REQUIRE_ASYNC_MODULE이 throw됩니다. 이 경우 사용자는 import()를 사용하여 비동기 모듈을 로드해야 합니다.

--experimental-print-required-tla가 활성화된 경우 평가 전에 ERR_REQUIRE_ASYNC_MODULE을 throw하는 대신 Node.js는 모듈을 평가하고 최상위 await를 찾아 위치를 출력하여 사용자가 수정할 수 있도록 지원합니다.

require()를 사용하여 ES 모듈을 로드하는 기능은 현재 실험적이며 --no-experimental-require-module을 사용하여 비활성화할 수 있습니다. 이 기능이 사용되는 위치를 출력하려면 --trace-require-module을 사용하십시오.

이 기능은 process.features.require_moduletrue인지 확인하여 감지할 수 있습니다.

모두 합쳐서

require()가 호출될 때 로드될 정확한 파일 이름을 얻으려면 require.resolve() 함수를 사용하십시오.

위의 모든 것을 종합하면, require()가 수행하는 작업의 의사 코드로 된 고수준 알고리즘은 다음과 같습니다.

text
경로 Y의 모듈에서 require(X)
1. X가 코어 모듈이면,
   a. 코어 모듈을 반환합니다.
   b. 중지합니다.
2. X가 '/'로 시작하면
   a. Y를 파일 시스템 루트로 설정합니다.
3. X가 './' 또는 '/' 또는 '../'로 시작하면
   a. LOAD_AS_FILE(Y + X)
   b. LOAD_AS_DIRECTORY(Y + X)
   c. "찾을 수 없음"을 던집니다.
4. X가 '#'로 시작하면
   a. LOAD_PACKAGE_IMPORTS(X, dirname(Y))
5. LOAD_PACKAGE_SELF(X, dirname(Y))
6. LOAD_NODE_MODULES(X, dirname(Y))
7. "찾을 수 없음"을 던집니다.

MAYBE_DETECT_AND_LOAD(X)
1. X가 CommonJS 모듈로 파싱되면, X를 CommonJS 모듈로 로드합니다. 중지합니다.
2. 그렇지 않고, X의 소스 코드가 <a href="esm#resolver-algorithm-specification">ESM 리졸버에 정의된 DETECT_MODULE_SYNTAX</a>를 사용하여 ECMAScript 모듈로 파싱될 수 있으면,
  a. X를 ECMAScript 모듈로 로드합니다. 중지합니다.
3. 1에서 X를 CommonJS로 파싱하려고 시도한 SyntaxError를 던집니다. 중지합니다.

LOAD_AS_FILE(X)
1. X가 파일이면, X를 해당 파일 확장자 형식으로 로드합니다. 중지합니다.
2. X.js가 파일이면,
    a. X에 가장 가까운 패키지 범위 SCOPE를 찾습니다.
    b. 범위가 없으면
      1. MAYBE_DETECT_AND_LOAD(X.js)
    c. SCOPE/package.json에 "type" 필드가 있으면,
      1. "type" 필드가 "module"이면, X.js를 ECMAScript 모듈로 로드합니다. 중지합니다.
      2. "type" 필드가 "commonjs"이면, X.js를 CommonJS 모듈로 로드합니다. 중지합니다.
    d. MAYBE_DETECT_AND_LOAD(X.js)
3. X.json이 파일이면, X.json을 JavaScript 객체로 로드합니다. 중지합니다.
4. X.node가 파일이면, X.node를 바이너리 애드온으로 로드합니다. 중지합니다.

LOAD_INDEX(X)
1. X/index.js가 파일이면
    a. X에 가장 가까운 패키지 범위 SCOPE를 찾습니다.
    b. 범위가 없으면, X/index.js를 CommonJS 모듈로 로드합니다. 중지합니다.
    c. SCOPE/package.json에 "type" 필드가 있으면,
      1. "type" 필드가 "module"이면, X/index.js를 ECMAScript 모듈로 로드합니다. 중지합니다.
      2. 그렇지 않으면, X/index.js를 CommonJS 모듈로 로드합니다. 중지합니다.
2. X/index.json이 파일이면, X/index.json을 JavaScript 객체로 파싱합니다. 중지합니다.
3. X/index.node가 파일이면, X/index.node를 바이너리 애드온으로 로드합니다. 중지합니다.

LOAD_AS_DIRECTORY(X)
1. X/package.json이 파일이면,
   a. X/package.json을 파싱하고, "main" 필드를 찾습니다.
   b. "main"이 거짓 값이면, 2로 이동합니다.
   c. M = X + (json main 필드)
   d. LOAD_AS_FILE(M)
   e. LOAD_INDEX(M)
   f. LOAD_INDEX(X) (사용되지 않음)
   g. "찾을 수 없음"을 던집니다.
2. LOAD_INDEX(X)

LOAD_NODE_MODULES(X, START)
1. DIRS = NODE_MODULES_PATHS(START)
2. DIRS의 각 DIR에 대해:
   a. LOAD_PACKAGE_EXPORTS(X, DIR)
   b. LOAD_AS_FILE(DIR/X)
   c. LOAD_AS_DIRECTORY(DIR/X)

NODE_MODULES_PATHS(START)
1. PARTS = path split(START)
2. I = PARTS의 개수 - 1
3. DIRS = []
4. I >= 0이면,
   a. PARTS[I] = "node_modules"이면, d로 이동합니다.
   b. DIR = path join(PARTS[0 .. I] + "node_modules")
   c. DIRS = DIR + DIRS
   d. I = I - 1
5. DIRS + GLOBAL_FOLDERS를 반환합니다.

LOAD_PACKAGE_IMPORTS(X, DIR)
1. DIR에 가장 가까운 패키지 범위 SCOPE를 찾습니다.
2. 범위가 없으면, 반환합니다.
3. SCOPE/package.json의 "imports"가 null 또는 undefined이면, 반환합니다.
4. `--experimental-require-module`이 활성화되면
  a. CONDITIONS = ["node", "require", "module-sync"]
  b. 그렇지 않으면, CONDITIONS = ["node", "require"]
5. MATCH = PACKAGE_IMPORTS_RESOLVE(X, pathToFileURL(SCOPE), CONDITIONS) <a href="esm#resolver-algorithm-specification">ESM 리졸버에 정의됨</a>.
6. RESOLVE_ESM_MATCH(MATCH).

LOAD_PACKAGE_EXPORTS(X, DIR)
1. X를 NAME과 SUBPATH의 조합으로 해석하려고 시도합니다. 여기서 이름에는 @scope/ 접두사가 있을 수 있으며, 서브패스는 슬래시(`/`)로 시작합니다.
2. X가 이 패턴과 일치하지 않거나 DIR/NAME/package.json이 파일이 아니면, 반환합니다.
3. DIR/NAME/package.json을 파싱하고, "exports" 필드를 찾습니다.
4. "exports"가 null 또는 undefined이면, 반환합니다.
5. `--experimental-require-module`이 활성화되면
  a. CONDITIONS = ["node", "require", "module-sync"]
  b. 그렇지 않으면, CONDITIONS = ["node", "require"]
6. MATCH = PACKAGE_EXPORTS_RESOLVE(pathToFileURL(DIR/NAME), "." + SUBPATH, `package.json` "exports", CONDITIONS) <a href="esm#resolver-algorithm-specification">ESM 리졸버에 정의됨</a>.
7. RESOLVE_ESM_MATCH(MATCH)

LOAD_PACKAGE_SELF(X, DIR)
1. DIR에 가장 가까운 패키지 범위 SCOPE를 찾습니다.
2. 범위가 없으면, 반환합니다.
3. SCOPE/package.json의 "exports"가 null 또는 undefined이면, 반환합니다.
4. SCOPE/package.json의 "name"이 X의 첫 번째 세그먼트가 아니면, 반환합니다.
5. MATCH = PACKAGE_EXPORTS_RESOLVED(pathToFileURL(SCOPE), "." + X.slice("name".length), `package.json` "exports", ["node", "require"]) <a href="esm#resolver-algorithm-specification">ESM 리졸버에 정의됨</a>.
6. RESOLVE_ESM_MATCH(MATCH)

RESOLVE_ESM_MATCH(MATCH)
1. RESOLVED_PATH = fileURLToPath(MATCH)
2. RESOLVED_PATH에 있는 파일이 있으면, RESOLVED_PATH를 해당 확장자 형식으로 로드합니다. 중지합니다.
3. "찾을 수 없음"을 던집니다.

캐싱

모듈은 처음 로드된 후 캐시됩니다. 이는 (다른 것들 중에서도) require('foo')에 대한 모든 호출이 동일한 파일로 확인되는 경우 정확히 동일한 객체를 반환한다는 것을 의미합니다.

require.cache가 수정되지 않은 경우 require('foo')에 대한 여러 호출은 모듈 코드가 여러 번 실행되지 않습니다. 이것은 중요한 기능입니다. 이 기능을 통해 "부분적으로 완료된" 객체를 반환할 수 있으므로 순환을 일으키는 경우에도 전이적 종속성을 로드할 수 있습니다.

모듈이 코드를 여러 번 실행하도록 하려면 함수를 내보내고 해당 함수를 호출합니다.

모듈 캐싱 주의 사항

모듈은 확인된 파일 이름을 기반으로 캐시됩니다. 모듈은 호출 모듈의 위치에 따라 다른 파일 이름으로 확인될 수 있으므로 require('foo')가 항상 동일한 객체를 반환한다는 보장이 없습니다. (다른 파일로 확인되는 경우).

또한, 대소문자를 구분하지 않는 파일 시스템이나 운영 체제에서는 다른 확인된 파일 이름이 동일한 파일을 가리킬 수 있지만 캐시는 여전히 이러한 파일을 다른 모듈로 처리하고 파일을 여러 번 다시 로드합니다. 예를 들어, ./foo./FOO가 동일한 파일인지 여부에 관계없이 require('./foo')require('./FOO')는 두 개의 다른 객체를 반환합니다.

내장 모듈

[히스토리]

버전변경 사항
v16.0.0, v14.18.0require(...)node: import 지원 추가

Node.js는 바이너리에 컴파일된 여러 모듈을 가지고 있습니다. 이러한 모듈은 이 설명서의 다른 곳에서 자세히 설명합니다.

내장 모듈은 Node.js 소스 내에 정의되며 lib/ 폴더에 있습니다.

내장 모듈은 node: 접두사를 사용하여 식별할 수 있으며, 이 경우 require 캐시를 무시합니다. 예를 들어, require('node:http')require.cache에 해당 이름의 항목이 있더라도 항상 내장 HTTP 모듈을 반환합니다.

일부 내장 모듈은 식별자가 require()에 전달되면 항상 우선적으로 로드됩니다. 예를 들어, require('http')는 해당 이름의 파일이 있더라도 항상 내장 HTTP 모듈을 반환합니다. node: 접두사를 사용하지 않고 로드할 수 있는 내장 모듈 목록은 module.builtinModules에 접두사 없이 나열되어 있습니다.

node: 접두사가 필수인 내장 모듈

require()에 의해 로드될 때, 일부 내장 모듈은 node: 접두사를 사용하여 요청해야 합니다. 이러한 요구 사항은 새로 도입된 내장 모듈이 이미 이름을 사용하고 있는 사용자 영역 패키지와 충돌하는 것을 방지하기 위해 존재합니다. 현재 node: 접두사를 필요로 하는 내장 모듈은 다음과 같습니다.

이러한 모듈의 목록은 접두사를 포함하여 module.builtinModules에 표시됩니다.

순환 참조

순환적인 require() 호출이 있을 때, 모듈이 반환될 때 실행이 완료되지 않았을 수 있습니다.

다음 상황을 고려해 보세요.

a.js:

js
console.log('a starting')
exports.done = false
const b = require('./b.js')
console.log('in a, b.done = %j', b.done)
exports.done = true
console.log('a done')

b.js:

js
console.log('b starting')
exports.done = false
const a = require('./a.js')
console.log('in b, a.done = %j', a.done)
exports.done = true
console.log('b done')

main.js:

js
console.log('main starting')
const a = require('./a.js')
const b = require('./b.js')
console.log('in main, a.done = %j, b.done = %j', a.done, b.done)

main.jsa.js를 로드하고, a.jsb.js를 로드하면, 그 시점에서 b.jsa.js를 로드하려고 합니다. 무한 루프를 방지하기 위해 a.js exports 객체의 미완성 복사본b.js 모듈에 반환됩니다. 그런 다음 b.js 로드가 완료되고, 해당 exports 객체가 a.js 모듈에 제공됩니다.

main.js가 두 모듈을 모두 로드할 때까지 두 모듈 모두 완료됩니다. 따라서 이 프로그램의 출력은 다음과 같습니다.

bash
$ node main.js
main starting
a starting
b starting
in b, a.done = false
b done
in a, b.done = true
a done
in main, a.done = true, b.done = true

애플리케이션 내에서 순환 모듈 종속성이 제대로 작동하도록 하려면 신중한 계획이 필요합니다.

파일 모듈

정확한 파일 이름이 발견되지 않으면 Node.js는 .js, .json, 마지막으로 .node 확장자를 추가하여 필요한 파일 이름을 로드하려고 시도합니다. 다른 확장자(예: .cjs)를 가진 파일을 로드할 때는 파일 확장자를 포함한 전체 이름을 require()에 전달해야 합니다(예: require('./file.cjs')).

.json 파일은 JSON 텍스트 파일로 구문 분석되고, .node 파일은 process.dlopen()으로 로드되는 컴파일된 애드온 모듈로 해석됩니다. 다른 확장자를 사용하는 파일(또는 확장자가 없는 파일)은 JavaScript 텍스트 파일로 구문 분석됩니다. 사용할 구문 분석 목표를 이해하려면 모듈 시스템 결정 섹션을 참조하십시오.

'/' 접두사가 붙은 필요한 모듈은 파일의 절대 경로입니다. 예를 들어, require('/home/marco/foo.js')/home/marco/foo.js에 있는 파일을 로드합니다.

'./' 접두사가 붙은 필요한 모듈은 require()를 호출하는 파일을 기준으로 합니다. 즉, require('./circle')이 찾으려면 circle.jsfoo.js와 같은 디렉토리에 있어야 합니다.

파일을 나타내는 선행 '/', './' 또는 '../'이 없으면 모듈은 코어 모듈이거나 node_modules 폴더에서 로드되어야 합니다.

주어진 경로가 존재하지 않으면 require()MODULE_NOT_FOUND 오류를 throw합니다.

폴더를 모듈로 사용

[안정적: 3 - 레거시]

안정적: 3 안정성: 3 - 레거시: 대신 하위 경로 내보내기 또는 하위 경로 가져오기를 사용하십시오.

폴더를 require()에 인수로 전달하는 세 가지 방법이 있습니다.

첫 번째는 폴더의 루트에 main 모듈을 지정하는 package.json 파일을 만드는 것입니다. 예시 package.json 파일은 다음과 같습니다.

json
{ "name": "some-library", "main": "./lib/some-library.js" }

이것이 ./some-library에 있는 폴더에 있다면 require('./some-library')./some-library/lib/some-library.js를 로드하려고 시도합니다.

디렉토리에 package.json 파일이 없거나 "main" 항목이 없거나 확인할 수 없는 경우 Node.js는 해당 디렉토리에서 index.js 또는 index.node 파일을 로드하려고 시도합니다. 예를 들어, 이전 예시에 package.json 파일이 없다면 require('./some-library')는 다음을 로드하려고 시도합니다.

  • ./some-library/index.js
  • ./some-library/index.node

이러한 시도가 실패하면 Node.js는 기본 오류와 함께 전체 모듈이 누락되었다고 보고합니다.

bash
Error: Cannot find module 'some-library'

위의 세 가지 경우 모두 import('./some-library') 호출은 ERR_UNSUPPORTED_DIR_IMPORT 오류를 발생시킵니다. 패키지 하위 경로 내보내기 또는 하위 경로 가져오기를 사용하면 폴더를 모듈로 사용하는 것과 동일한 포함 구성 이점을 제공하고 requireimport 모두에 대해 작동합니다.

node_modules 폴더에서 로드하기

require()에 전달된 모듈 식별자가 내장 모듈이 아니고, '/', '../', 또는 './'로 시작하지 않는 경우, Node.js는 현재 모듈의 디렉토리에서 시작하여 /node_modules를 추가하고 해당 위치에서 모듈을 로드하려고 시도합니다. Node.js는 이미 node_modules로 끝나는 경로에 node_modules를 추가하지 않습니다.

해당 위치에서 찾을 수 없으면 상위 디렉토리로 이동하고, 파일 시스템의 루트에 도달할 때까지 계속합니다.

예를 들어, '/home/ry/projects/foo.js'에 있는 파일이 require('bar.js')를 호출한 경우, Node.js는 다음 위치를 이 순서대로 검색합니다.

  • /home/ry/projects/node_modules/bar.js
  • /home/ry/node_modules/bar.js
  • /home/node_modules/bar.js
  • /node_modules/bar.js

이를 통해 프로그램은 종속성을 지역화하여 충돌을 방지할 수 있습니다.

모듈 이름 뒤에 경로 접미사를 포함하여 모듈과 함께 배포된 특정 파일이나 하위 모듈을 요구할 수 있습니다. 예를 들어 require('example-module/path/to/file')example-module이 있는 위치를 기준으로 path/to/file을 확인합니다. 접미사가 붙은 경로는 동일한 모듈 확인 의미 체계를 따릅니다.

전역 폴더에서 로드하기

NODE_PATH 환경 변수가 절대 경로의 콜론으로 구분된 목록으로 설정된 경우, Node.js는 다른 위치에서 찾을 수 없는 경우 해당 경로에서 모듈을 검색합니다.

Windows에서는 NODE_PATH가 콜론 대신 세미콜론(;)으로 구분됩니다.

NODE_PATH는 원래 현재 모듈 확인 알고리즘이 정의되기 전에 다양한 경로에서 모듈을 로드하는 것을 지원하기 위해 만들어졌습니다.

NODE_PATH는 여전히 지원되지만, Node.js 생태계가 종속 모듈을 찾는 규칙에 정착했으므로 이제는 덜 필요합니다. NODE_PATH를 설정해야 한다는 사실을 모르는 사람들이 NODE_PATH에 의존하는 배포에서 놀라운 동작이 나타나는 경우가 있습니다. 때때로 모듈의 종속성이 변경되어 NODE_PATH가 검색될 때 다른 버전(또는 심지어 다른 모듈)이 로드될 수 있습니다.

또한, Node.js는 다음 GLOBAL_FOLDERS 목록에서 검색합니다.

  • 1: $HOME/.node_modules
  • 2: $HOME/.node_libraries
  • 3: $PREFIX/lib/node

여기서 $HOME은 사용자의 홈 디렉토리이고, $PREFIX는 Node.js가 구성한 node_prefix입니다.

이것들은 주로 역사적인 이유 때문입니다.

로컬 node_modules 폴더에 종속성을 배치하는 것이 좋습니다. 이렇게 하면 더 빠르고 안정적으로 로드됩니다.

모듈 래퍼

모듈의 코드가 실행되기 전에, Node.js는 다음과 같은 함수 래퍼로 감쌉니다.

js
;(function (exports, require, module, __filename, __dirname) {
  // 모듈 코드는 여기에 실제로 존재합니다.
})

이렇게 함으로써 Node.js는 다음과 같은 몇 가지 작업을 수행합니다.

  • 최상위 변수(var, const, 또는 let으로 정의됨)를 전역 객체가 아닌 모듈에 대한 범위로 유지합니다.
  • 실제로 모듈에 특정한 몇 가지 전역 변수처럼 보이는 변수를 제공하는 데 도움이 됩니다. 예를 들어:
    • 구현자가 모듈에서 값을 내보내는 데 사용할 수 있는 moduleexports 객체.
    • 모듈의 절대 파일 이름과 디렉토리 경로를 포함하는 편의 변수 __filename__dirname.

모듈 범위

__dirname

추가됨: v0.1.27

현재 모듈의 디렉토리 이름입니다. 이는 __filenamepath.dirname()과 동일합니다.

예: /Users/mjr에서 node example.js 실행

js
console.log(__dirname)
// 출력: /Users/mjr
console.log(path.dirname(__filename))
// 출력: /Users/mjr

__filename

추가됨: v0.0.1

현재 모듈의 파일 이름입니다. 심볼릭 링크가 해결된 현재 모듈 파일의 절대 경로입니다.

주 프로그램의 경우 명령줄에서 사용된 파일 이름과 반드시 같은 것은 아닙니다.

현재 모듈의 디렉토리 이름은 __dirname을 참조하십시오.

예:

/Users/mjr에서 node example.js 실행

js
console.log(__filename)
// 출력: /Users/mjr/example.js
console.log(__dirname)
// 출력: /Users/mjr

두 개의 모듈 ab가 있는 경우, ba의 종속성이며 다음과 같은 디렉토리 구조가 있습니다.

  • /Users/mjr/app/a.js
  • /Users/mjr/app/node_modules/b/b.js

b.js 내의 __filename에 대한 참조는 /Users/mjr/app/node_modules/b/b.js를 반환하고, a.js 내의 __filename에 대한 참조는 /Users/mjr/app/a.js를 반환합니다.

exports

추가됨: v0.1.12

더 짧게 입력할 수 있는 module.exports에 대한 참조입니다. exports를 사용해야 할 때와 module.exports를 사용해야 할 때에 대한 자세한 내용은 exports 단축키에 대한 섹션을 참조하십시오.

module

추가됨: v0.1.16

현재 모듈에 대한 참조이며, module 객체에 대한 섹션을 참조하십시오. 특히, module.exports는 모듈이 내보내는 내용을 정의하고 require()를 통해 사용할 수 있도록 합니다.

require(id)

추가됨: v0.1.13

  • id <string> 모듈 이름 또는 경로
  • 반환값: <any> 내보낸 모듈 콘텐츠

모듈, JSON 및 로컬 파일을 가져오는 데 사용됩니다. 모듈은 node_modules에서 가져올 수 있습니다. 로컬 모듈과 JSON 파일은 __dirname (정의된 경우) 또는 현재 작업 디렉토리를 기준으로 확인되는 상대 경로(예: ./, ./foo, ./bar/baz, ../foo)를 사용하여 가져올 수 있습니다. POSIX 스타일의 상대 경로는 OS 독립적인 방식으로 확인되므로 위의 예는 Unix 시스템에서와 동일한 방식으로 Windows에서도 작동합니다.

js
// `__dirname` 또는 현재 작업 디렉토리를 기준으로 하는 경로를 사용하여 로컬 모듈 가져오기. (Windows에서는 .\path\myLocalModule로 확인됩니다.)
const myLocalModule = require('./path/myLocalModule')

// JSON 파일 가져오기:
const jsonData = require('./path/filename.json')

// node_modules 또는 Node.js 내장 모듈에서 모듈 가져오기:
const crypto = require('node:crypto')

require.cache

추가됨: v0.3.0

모듈은 필요할 때 이 객체에 캐시됩니다. 이 객체에서 키 값을 삭제하면 다음 require에서 모듈을 다시 로드합니다. 이것은 네이티브 애드온에는 적용되지 않으며, 다시 로드하면 오류가 발생합니다.

항목을 추가하거나 바꾸는 것도 가능합니다. 이 캐시는 내장 모듈 전에 확인되며, 내장 모듈과 일치하는 이름이 캐시에 추가되면 node: 접두사가 붙은 require 호출만 내장 모듈을 수신합니다. 주의해서 사용하십시오!

js
const assert = require('node:assert')
const realFs = require('node:fs')

const fakeFs = {}
require.cache.fs = { exports: fakeFs }

assert.strictEqual(require('fs'), fakeFs)
assert.strictEqual(require('node:fs'), realFs)

require.extensions

추가됨: v0.3.0

더 이상 사용되지 않음: v0.10.6

[안정성: 0 - 더 이상 사용되지 않음]

안정성: 0 안정성: 0 - 더 이상 사용되지 않음

특정 파일 확장자를 처리하는 방법에 대해 require에 지시합니다.

.sjs 확장자를 가진 파일을 .js로 처리합니다.

js
require.extensions['.sjs'] = require.extensions['.js']

더 이상 사용되지 않음. 과거에는 이 목록을 사용하여 필요에 따라 컴파일하여 Node.js로 JavaScript가 아닌 모듈을 로드했습니다. 그러나 실제로는 다른 Node.js 프로그램을 통해 모듈을 로드하거나 미리 JavaScript로 컴파일하는 등 훨씬 더 나은 방법이 있습니다.

require.extensions 사용을 피하십시오. 사용하면 미묘한 버그가 발생하고 등록된 확장자 수가 많아질수록 확장자 확인 속도가 느려집니다.

require.main

추가됨: v0.1.17

Node.js 프로세스가 시작될 때 로드된 진입 스크립트를 나타내는 Module 객체이거나 프로그램의 진입점이 CommonJS 모듈이 아닌 경우 undefined입니다. "메인 모듈 액세스"를 참조하십시오.

entry.js 스크립트에서:

js
console.log(require.main)
bash
node entry.js
js
Module {
  id: '.',
  path: '/absolute/path/to',
  exports: {},
  filename: '/absolute/path/to/entry.js',
  loaded: false,
  children: [],
  paths:
   [ '/absolute/path/to/node_modules',
     '/absolute/path/node_modules',
     '/absolute/node_modules',
     '/node_modules' ] }

require.resolve(request[, options])

[히스토리]

버전변경 사항
v8.9.0paths 옵션이 지원됩니다.
v0.3.0추가됨: v0.3.0
  • request <string> 확인할 모듈 경로입니다.

  • options <Object>

    • paths <string[]> 모듈 위치를 확인할 경로입니다. 이 경로가 있는 경우 $HOME/.node_modules와 같은 GLOBAL_FOLDERS를 제외하고 기본 확인 경로 대신 사용됩니다. node_modules 계층 구조는 이 위치에서 확인되므로 이러한 각 경로는 모듈 확인 알고리즘의 시작점으로 사용됩니다.
  • 반환값: <string>

내부 require() 기계를 사용하여 모듈의 위치를 찾지만 모듈을 로드하는 대신 확인된 파일 이름을 반환합니다.

모듈을 찾을 수 없는 경우 MODULE_NOT_FOUND 오류가 발생합니다.

require.resolve.paths(request)

추가됨: v8.9.0

request 해석 중 검색된 경로를 담은 배열을 반환합니다. request 문자열이 http 또는 fs와 같이 코어 모듈을 참조하는 경우 null을 반환합니다.

module 객체

추가됨: v0.1.16

각 모듈에서 module 자유 변수는 현재 모듈을 나타내는 객체에 대한 참조입니다. 편의를 위해 module.exportsexports 모듈 전역 변수를 통해서도 접근할 수 있습니다. module은 실제로 전역 변수가 아니라 각 모듈에 지역적인 변수입니다.

module.children

추가됨: v0.1.16

이 모듈이 처음으로 필요로 하는 모듈 객체들입니다.

module.exports

추가됨: v0.1.16

module.exports 객체는 Module 시스템에 의해 생성됩니다. 때때로 이것은 허용되지 않습니다. 많은 사람들이 자신의 모듈을 어떤 클래스의 인스턴스로 만들기를 원합니다. 이를 위해 원하는 내보내기 객체를 module.exports에 할당합니다. 원하는 객체를 exports에 할당하면 단순히 지역 exports 변수를 재바인딩하게 되는데, 이는 아마도 원하는 것이 아닐 것입니다.

예를 들어, a.js라는 모듈을 만들었다고 가정해 보겠습니다.

js
const EventEmitter = require('node:events')

module.exports = new EventEmitter()

// 작업을 수행하고, 약간의 시간이 지난 후
// 모듈 자체에서 'ready' 이벤트를 방출합니다.
setTimeout(() => {
  module.exports.emit('ready')
}, 1000)

그런 다음 다른 파일에 다음과 같이 할 수 있습니다.

js
const a = require('./a')
a.on('ready', () => {
  console.log('module "a" is ready')
})

module.exports에 대한 할당은 즉시 수행되어야 합니다. 콜백에서 수행할 수 없습니다. 다음은 작동하지 않습니다.

x.js:

js
setTimeout(() => {
  module.exports = { a: 'hello' }
}, 0)

y.js:

js
const x = require('./x')
console.log(x.a)

exports 바로가기

추가됨: v0.1.16

exports 변수는 모듈의 파일 수준 범위 내에서 사용할 수 있으며, 모듈이 평가되기 전에 module.exports의 값으로 할당됩니다.

이는 module.exports.f = ...exports.f = ...로 더 간결하게 작성할 수 있는 바로가기를 허용합니다. 하지만 변수와 마찬가지로 exports에 새 값을 할당하면 더 이상 module.exports에 바인딩되지 않습니다.

js
module.exports.hello = true // 모듈의 require에서 내보냄
exports = { hello: false } // 내보내지 않고, 모듈 내에서만 사용 가능

module.exports 속성이 새 객체로 완전히 대체될 때 exports를 재할당하는 것이 일반적입니다.

js
module.exports = exports = function Constructor() {
  // ... 등
}

동작을 설명하기 위해 require()의 가상 구현을 생각해 보세요. 이는 실제 require()에서 수행되는 작업과 매우 유사합니다.

js
function require(/* ... */) {
  const module = { exports: {} }
  ;((module, exports) => {
    // 모듈 코드 여기. 이 예에서는 함수를 정의합니다.
    function someFunc() {}
    exports = someFunc
    // 이 시점에서 exports는 더 이상 module.exports에 대한 바로가기가 아니며,
    // 이 모듈은 여전히 빈 기본 객체를 내보냅니다.
    module.exports = someFunc
    // 이 시점에서 모듈은 이제 기본 객체 대신 someFunc를 내보냅니다.
  })(module, module.exports)
  return module.exports
}

module.filename

추가됨: v0.1.16

모듈의 완전히 해결된 파일 이름입니다.

module.id

추가됨: v0.1.16

모듈의 식별자입니다. 일반적으로 이는 완전히 해결된 파일 이름입니다.

module.isPreloading

추가됨: v15.4.0, v14.17.0

  • 유형: <boolean> Node.js 프리로드 단계 중에 모듈이 실행 중인 경우 true입니다.

module.loaded

추가됨: v0.1.16

모듈 로딩이 완료되었는지 또는 로딩 중인지 여부입니다.

module.parent

추가됨: v0.1.16

더 이상 사용되지 않음: v14.6.0, v12.19.0

[안정성: 0 - 더 이상 사용되지 않음]

안정성: 0 안정성: 0 - 더 이상 사용되지 않음: require.mainmodule.children을 대신 사용하십시오.

이 모듈을 처음으로 요구한 모듈이거나, 현재 모듈이 현재 프로세스의 진입점인 경우 null 또는 CommonJS 모듈이 아닌 것(예: REPL 또는 import)에 의해 모듈이 로드된 경우 undefined입니다.

module.path

추가됨: v11.14.0

모듈의 디렉토리 이름입니다. 일반적으로 module.idpath.dirname()와 동일합니다.

module.paths

추가됨: v0.4.0

모듈의 검색 경로입니다.

module.require(id)

추가됨: v0.5.1

module.require() 메서드는 require()가 원래 모듈에서 호출된 것처럼 모듈을 로드하는 방법을 제공합니다.

이렇게 하려면 module 객체에 대한 참조를 가져와야 합니다. require()module.exports를 반환하고 module은 일반적으로 특정 모듈의 코드 내에서만 사용 가능하기 때문에 사용하려면 명시적으로 내보내야 합니다.

Module 객체

이 섹션은 모듈: module 코어 모듈로 이동되었습니다.

소스 맵 v3 지원

이 섹션은 모듈: module 코어 모듈로 이동되었습니다.