모듈: CommonJS 모듈
[Stable: 2 - Stable]
Stable: 2 Stability: 2 - Stable
CommonJS 모듈은 Node.js용 JavaScript 코드를 패키징하는 원래 방식입니다. Node.js는 브라우저 및 기타 JavaScript 런타임에서 사용되는 ECMAScript 모듈 표준도 지원합니다.
Node.js에서 각 파일은 별도의 모듈로 처리됩니다. 예를 들어, foo.js
라는 파일을 고려해 보겠습니다.
const circle = require('./circle.js')
console.log(`The area of a circle of radius 4 is ${circle.area(4)}`)
첫 번째 줄에서 foo.js
는 foo.js
와 같은 디렉터리에 있는 circle.js
모듈을 로드합니다.
다음은 circle.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
모듈을 사용합니다.
const Square = require('./square.js')
const mySquare = new Square(2)
console.log(`The area of mySquare is ${mySquare.area()}`)
square
모듈은 square.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.main
은 undefined
이며, 메인 모듈에 접근할 수 없습니다.
패키지 관리자 팁
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.0 | require(esm) 에서 'module.exports' 상호 운용성 내보내기를 지원합니다. |
v22.0.0, v20.17.0 | 추가됨: v22.0.0, v20.17.0 |
.mjs
확장자는 ECMAScript 모듈에 예약되어 있습니다. 어떤 파일이 ECMAScript 모듈로 구문 분석되는지에 대한 자세한 내용은 모듈 시스템 결정 섹션을 참조하십시오.
require()
는 다음 요구 사항을 충족하는 ECMAScript 모듈 로딩만 지원합니다.
- 모듈이 완전히 동기적입니다(최상위
await
가 포함되지 않음). - 다음 조건 중 하나를 충족합니다.
로드되는 ES 모듈이 요구 사항을 충족하는 경우 require()
는 모듈을 로드하고 모듈 네임스페이스 객체를 반환할 수 있습니다. 이 경우 동적 import()
와 유사하지만 동기적으로 실행되고 네임스페이스 객체를 직접 반환합니다.
다음 ES 모듈을 사용하여:
// distance.mjs
export function distance(a, b) {
return (b.x - a.x) ** 2 + (b.y - a.y) ** 2
}
// point.mjs
export default class Point {
constructor(x, y) {
this.x = x
this.y = y
}
}
CommonJS 모듈은 require()
를 사용하여 이들을 로드할 수 있습니다.
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"
를 사용하여 원하는 값을 내보낼 수 있습니다.
// 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' }
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
클래스에 정적 메서드로 첨부될 수 있습니다.
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' }
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_module
이 true
인지 확인하여 감지할 수 있습니다.
모두 합쳐서
require()
가 호출될 때 로드될 정확한 파일 이름을 얻으려면 require.resolve()
함수를 사용하십시오.
위의 모든 것을 종합하면, require()
가 수행하는 작업의 의사 코드로 된 고수준 알고리즘은 다음과 같습니다.
경로 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.0 | require(...) 에 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
:
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
:
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
:
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.js
가 a.js
를 로드하고, a.js
가 b.js
를 로드하면, 그 시점에서 b.js
는 a.js
를 로드하려고 합니다. 무한 루프를 방지하기 위해 a.js
exports 객체의 미완성 복사본이 b.js
모듈에 반환됩니다. 그런 다음 b.js
로드가 완료되고, 해당 exports
객체가 a.js
모듈에 제공됩니다.
main.js
가 두 모듈을 모두 로드할 때까지 두 모듈 모두 완료됩니다. 따라서 이 프로그램의 출력은 다음과 같습니다.
$ 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.js
가 foo.js
와 같은 디렉토리에 있어야 합니다.
파일을 나타내는 선행 '/'
, './'
또는 '../'
이 없으면 모듈은 코어 모듈이거나 node_modules
폴더에서 로드되어야 합니다.
주어진 경로가 존재하지 않으면 require()
는 MODULE_NOT_FOUND
오류를 throw합니다.
폴더를 모듈로 사용
[안정적: 3 - 레거시]
안정적: 3 안정성: 3 - 레거시: 대신 하위 경로 내보내기 또는 하위 경로 가져오기를 사용하십시오.
폴더를 require()
에 인수로 전달하는 세 가지 방법이 있습니다.
첫 번째는 폴더의 루트에 main
모듈을 지정하는 package.json
파일을 만드는 것입니다. 예시 package.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는 기본 오류와 함께 전체 모듈이 누락되었다고 보고합니다.
Error: Cannot find module 'some-library'
위의 세 가지 경우 모두 import('./some-library')
호출은 ERR_UNSUPPORTED_DIR_IMPORT
오류를 발생시킵니다. 패키지 하위 경로 내보내기 또는 하위 경로 가져오기를 사용하면 폴더를 모듈로 사용하는 것과 동일한 포함 구성 이점을 제공하고 require
와 import
모두에 대해 작동합니다.
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는 다음과 같은 함수 래퍼로 감쌉니다.
;(function (exports, require, module, __filename, __dirname) {
// 모듈 코드는 여기에 실제로 존재합니다.
})
이렇게 함으로써 Node.js는 다음과 같은 몇 가지 작업을 수행합니다.
- 최상위 변수(
var
,const
, 또는let
으로 정의됨)를 전역 객체가 아닌 모듈에 대한 범위로 유지합니다. - 실제로 모듈에 특정한 몇 가지 전역 변수처럼 보이는 변수를 제공하는 데 도움이 됩니다. 예를 들어:
- 구현자가 모듈에서 값을 내보내는 데 사용할 수 있는
module
및exports
객체. - 모듈의 절대 파일 이름과 디렉토리 경로를 포함하는 편의 변수
__filename
및__dirname
.
- 구현자가 모듈에서 값을 내보내는 데 사용할 수 있는
모듈 범위
__dirname
추가됨: v0.1.27
현재 모듈의 디렉토리 이름입니다. 이는 __filename
의 path.dirname()
과 동일합니다.
예: /Users/mjr
에서 node example.js
실행
console.log(__dirname)
// 출력: /Users/mjr
console.log(path.dirname(__filename))
// 출력: /Users/mjr
__filename
추가됨: v0.0.1
현재 모듈의 파일 이름입니다. 심볼릭 링크가 해결된 현재 모듈 파일의 절대 경로입니다.
주 프로그램의 경우 명령줄에서 사용된 파일 이름과 반드시 같은 것은 아닙니다.
현재 모듈의 디렉토리 이름은 __dirname
을 참조하십시오.
예:
/Users/mjr
에서 node example.js
실행
console.log(__filename)
// 출력: /Users/mjr/example.js
console.log(__dirname)
// 출력: /Users/mjr
두 개의 모듈 a
와 b
가 있는 경우, b
는 a
의 종속성이며 다음과 같은 디렉토리 구조가 있습니다.
/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
모듈, JSON
및 로컬 파일을 가져오는 데 사용됩니다. 모듈은 node_modules
에서 가져올 수 있습니다. 로컬 모듈과 JSON 파일은 __dirname
(정의된 경우) 또는 현재 작업 디렉토리를 기준으로 확인되는 상대 경로(예: ./
, ./foo
, ./bar/baz
, ../foo
)를 사용하여 가져올 수 있습니다. POSIX 스타일의 상대 경로는 OS 독립적인 방식으로 확인되므로 위의 예는 Unix 시스템에서와 동일한 방식으로 Windows에서도 작동합니다.
// `__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 호출만 내장 모듈을 수신합니다. 주의해서 사용하십시오!
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
특정 파일 확장자를 처리하는 방법에 대해 require
에 지시합니다.
.sjs
확장자를 가진 파일을 .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
스크립트에서:
console.log(require.main)
node entry.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.0 | paths 옵션이 지원됩니다. |
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
<string> 검색 경로를 가져올 모듈 경로입니다.- 반환값: <string[]> | <null>
request
해석 중 검색된 경로를 담은 배열을 반환합니다. request
문자열이 http
또는 fs
와 같이 코어 모듈을 참조하는 경우 null
을 반환합니다.
module
객체
추가됨: v0.1.16
각 모듈에서 module
자유 변수는 현재 모듈을 나타내는 객체에 대한 참조입니다. 편의를 위해 module.exports
는 exports
모듈 전역 변수를 통해서도 접근할 수 있습니다. module
은 실제로 전역 변수가 아니라 각 모듈에 지역적인 변수입니다.
module.children
추가됨: v0.1.16
이 모듈이 처음으로 필요로 하는 모듈 객체들입니다.
module.exports
추가됨: v0.1.16
module.exports
객체는 Module
시스템에 의해 생성됩니다. 때때로 이것은 허용되지 않습니다. 많은 사람들이 자신의 모듈을 어떤 클래스의 인스턴스로 만들기를 원합니다. 이를 위해 원하는 내보내기 객체를 module.exports
에 할당합니다. 원하는 객체를 exports
에 할당하면 단순히 지역 exports
변수를 재바인딩하게 되는데, 이는 아마도 원하는 것이 아닐 것입니다.
예를 들어, a.js
라는 모듈을 만들었다고 가정해 보겠습니다.
const EventEmitter = require('node:events')
module.exports = new EventEmitter()
// 작업을 수행하고, 약간의 시간이 지난 후
// 모듈 자체에서 'ready' 이벤트를 방출합니다.
setTimeout(() => {
module.exports.emit('ready')
}, 1000)
그런 다음 다른 파일에 다음과 같이 할 수 있습니다.
const a = require('./a')
a.on('ready', () => {
console.log('module "a" is ready')
})
module.exports
에 대한 할당은 즉시 수행되어야 합니다. 콜백에서 수행할 수 없습니다. 다음은 작동하지 않습니다.
x.js
:
setTimeout(() => {
module.exports = { a: 'hello' }
}, 0)
y.js
:
const x = require('./x')
console.log(x.a)
exports
바로가기
추가됨: v0.1.16
exports
변수는 모듈의 파일 수준 범위 내에서 사용할 수 있으며, 모듈이 평가되기 전에 module.exports
의 값으로 할당됩니다.
이는 module.exports.f = ...
를 exports.f = ...
로 더 간결하게 작성할 수 있는 바로가기를 허용합니다. 하지만 변수와 마찬가지로 exports
에 새 값을 할당하면 더 이상 module.exports
에 바인딩되지 않습니다.
module.exports.hello = true // 모듈의 require에서 내보냄
exports = { hello: false } // 내보내지 않고, 모듈 내에서만 사용 가능
module.exports
속성이 새 객체로 완전히 대체될 때 exports
를 재할당하는 것이 일반적입니다.
module.exports = exports = function Constructor() {
// ... 등
}
동작을 설명하기 위해 require()
의 가상 구현을 생각해 보세요. 이는 실제 require()
에서 수행되는 작업과 매우 유사합니다.
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.main
과 module.children
을 대신 사용하십시오.
이 모듈을 처음으로 요구한 모듈이거나, 현재 모듈이 현재 프로세스의 진입점인 경우 null
또는 CommonJS 모듈이 아닌 것(예: REPL 또는 import
)에 의해 모듈이 로드된 경우 undefined
입니다.
module.path
추가됨: v11.14.0
모듈의 디렉토리 이름입니다. 일반적으로 module.id
의 path.dirname()
와 동일합니다.
module.paths
추가됨: v0.4.0
모듈의 검색 경로입니다.
module.require(id)
추가됨: v0.5.1
module.require()
메서드는 require()
가 원래 모듈에서 호출된 것처럼 모듈을 로드하는 방법을 제공합니다.
이렇게 하려면 module
객체에 대한 참조를 가져와야 합니다. require()
는 module.exports
를 반환하고 module
은 일반적으로 특정 모듈의 코드 내에서만 사용 가능하기 때문에 사용하려면 명시적으로 내보내야 합니다.
Module
객체
이 섹션은 모듈: module
코어 모듈로 이동되었습니다.
소스 맵 v3 지원
이 섹션은 모듈: module
코어 모듈로 이동되었습니다.