쌓고 쌓다
[node] CommonJS 모듈, ECMASciprt 모듈 본문
REPL
자바스크립트를 컴파일하지 않고 콘솔을 통해 코드를 즉시 실행할 수 있었다.
노드도 비슷한 콘솔을 제공하는데 REPL(Read Eval Print Loop)이다.
윈도에서는 명령 프롬프트에서 node를 입력하고, VS Code에서는 터미널을 통해 실행 가능하다.
콘솔에서 node [자바스크립트 파일경로]로 실행한다.
Ctrl + c 를 두번 누르거나 .exit를 입력하여 종료함.
노드는 브라우저의 자바스크립트와 다르게 코드를 모듈로 만들 수 있다.
모듈? 특정한 기능을 하는 함수나 변수들의 집합.
노드에서는 CommonJS 모듈 또는 ECMAScript 모듈을 사용한다.
CommonJS 모듈
var.js
const odd = '홀수입니다.'
const even = '짝수입니다.'
module.exports = {
odd,
even
}
/*
module.exports = {
odd: odd,
even: even
}
*/
module.exports에 변수들을 담은 객체를 대입한다.
다른 파일에서 이 파일을 불러오면 module.exports에 대입된 값을 사용할 수 있다.
이 파일은 이제 모듈이 된것이다. func.js에서 var.js를 참조해 보자.
func.js
const { odd, even } = require('./var')
// const obj = require('./var') 로 객체 통째로 가져와 obj.odd로 사용도 가능하다.
function checkOddOrEven(num) {
if(num%2){
return odd
} else {
return even
}
}
module.exports = checkOddOrEven
require 함수 안에 불러온 모듈의 경로를 적는다. js나 json 같은 확장자는 생략 가능하다.
func.js처럼 다른 모듈(var.js)을 사용하는 파일을 다시 모듈(func.js)로도 만들 수 있다.
module.exports에는 객체뿐만 아니라, 함수나 변수, 배열도 대입할 수 있다.
*module.exports는 파일에서 단 한 번만 사용해야 한다.
index.js
const {odd, even} = require('./var')
const checkNumber = require('./func.js')
function checkStringOddOrEven(str) {
if(str.length%2){
return odd
} else {
return even
}
}
console.log(checkNumber(10))
console.log(checkStringOddOrEven('hello'))
var.js가 func.js와 index.js에 두번 쓰이는 것처럼 모듈 하나가 여러 개의 모듈에 사용될 수 있다.
모듈을 만들 때 modele.exports만 사용했지만, module 객체 말고 exports 객체로도 가능하다.
var.js - exports 객체
exports.odd = '홀수'
exports.even = '짝수'
module.exports와 exports가 같은 객체를 참조하기에 가능하다.
exports 객체에 add 함수를 넣으면 module.exports에도 add 함수가 들어간다.
모듈 여러 개 불렀는데 중복되는 모듈이 있다면? (require.cache)
위의 index.js에서 var.js를 참조하고, func.js도 참조하는데 이 모듈이 또 var.js를 참조한다.
var.js를 두 번 읽는 건가? 싶었는데 require('./var')를 실행하고 require.cache를 보면 알겠지만
캐시에 저장해 두어 두 번째 부를 땐 실제 파일을 읽는 것이 아니라 캐시에 저장된 것을 불러온다.
=> require.cache에 한번 require한 모듈에 대한 정보가 들어있다.
exports와 module.exports
exports => module.exports => { }
exports는 module.exports를 참조하고 module.exports는 빈 객체를 참조한다.
그래서 exports === module.exports는 true가 나오는 것이다.
주의 사항!
exports === module.exports === { } 라는것을 생각하고 아래의 코드를 보자.
var.js
const odd = '홀수'
const even = '짝수'
const word = 'hello'
exports.odd = odd
exports.even = even
module.exports = word
module.exports = word로 해버리면
exports === module.exports === {} 참조 관계가 깨져버린다.
exports !== module.exports가 되어버리는것이다.
module.exports는 'hello'만 들어가게 되는것이다.
func.js
const hello = require('./var')
console.log(hello)
var.js를 참조하는데 hello를 출력하면
{ odd, even, hello } 객체가 아닌 hello만 담겨 온다.
var.js가 아래와 같다면.
const odd = '홀수'
const even = '짝수'
exports.odd = odd
exports.even = even
객체로써 넘어온다.
그러므로 혼용하여 사용하는 것은 불가능하다.
순환 참조
dep1.js
const dep2 = require('./dep2')
console.log('require dep2', dep2)
module.exports = () => {
console.log('dep2', dep2)
}
dep2.js
const dep1 = require('./dep1')
console.log('require dep1', dep1)
module.exports = () => {
console.log('dep1', dep1)
}
dep-run.js
const dep1 = require('./dep1')
const dep2 = require('./dep2')
dep1()
dep2()
dep-run.js를 실행하면
require('./dep1')이 실행되고, dept1.js에서는 require('./dep2')가 실행되는데 그럼 또 require('./dep1')이 실행되고
참조하고 참조하고 무한반복이 일어난다.
노드에서는 빈 객체로 만들어버려 실행한다. 그러니 주의하자.
ECMAScript 모듈(ES모듈)
var.mjs
export const odd = '홀수'
export const even = '짝수'
func.mjs
import {odd, even} from './var.mjs'
function checkOddOrEven(num) {
if(num$%2) {
return odd
} else {
return even
}
}
export default checkOddOrEven
index.mjs
import {odd, even} from './var.mjs'
import checkNumber from './func.mjs'
function checkStringOddOrEven(str) {
if(str.length%2) {
return odd
} else {
return even
}
}
console.log(checkNumber(10))
console.log(checkStringOddOrEven('hello'))
require, exports, module.exports는 각각 import, export, export default로 바뀌었다.
ES모듈의 import, export default는 require, module처럼 함수나 객체가 아니라 문법 그 자체이다.
import시에 js, mjs 같은 확장자는 생략 불가능하며 파일 확장자도 mjs여야 한다.
(js 확장자로 ES모듈을 사용하려면 package.json에 type: "module" 속성을 넣어야 함.)
CommonJS 모듈 | ECMAScript 모듈 |
require('./a'); module.exports = A; const A = require('./a'); exports.C = D; const E = F; exports.E = E; const { C, E } = require('./b'); |
import './a.mjs'; export default A; import A from './a.mjs'; export const C = D; const E = F; export { E }; import { C, E } from './b.mjs'; |
다이나믹 임포트
조건부로 모듈을 불러오는 방법을 다이나믹 임포트라고 한다.
CommonJS 모듈에서는 다이나믹 임포트가 가능하고, ES 모듈에서는 불가능하다. 하지만 다른 방법으로
ES 모듈에서 다이나믹 임포트를 한다.
dynamic.js
const a = false
if(a) {
require(./func')
}
console.log('성공')
require('./func')는 실행되지 않는다.
dynamic.mjs
const a = false
if(a) {
import './func.mjs'
}
console.log('성공')
import는 항상 탑 레벨에서, 몰아서 이루어져야 한다.
아래와 같이 dynamic.mjs를 수정하자.
const a = true
if(a) {
const m1 = await import('./func.mjs')
console.log(m1)
const m2 = await import('./var.mjs')
console.log(m2)
}
import라는 함수를 사용해서 모듈을 동적으로 불러올 수 있다.
import는 Promise를 반환하기에 await이나 then을 붙여야 한다.
ES모듈에서는 async 함수 없이도 최상위 스코프에서 await을 사용할 수 있다.
'프로그래밍 > node.js' 카테고리의 다른 글
[Node.js] REST와 REST API (0) | 2023.02.01 |
---|---|
[Node.js] http 모듈로 서버 만들기 (2) | 2023.01.30 |
[node] 버퍼와 스트림 / pipe (0) | 2023.01.29 |
[node] 'fs 모듈' 파일 시스템 접근하기 (0) | 2023.01.27 |
[node] path 내장 모듈 (0) | 2023.01.23 |