REPL 사용하기
자바스크립트는 스크립트 언어이기 때문에 즉석에서 코드를 실행할 수 있다.
그 중 REPL 이라는 콘솔을 제공함으로서 cmd, 터미널 등에서 node 입력 후 코드 작성 및 실행이 가능하다.
이 방법은 간단한 코드를 테스트 하는 용도로는 적합하나 긴 코드를 입력하기엔 부적합 하다.
CommonJS 모듈 시스템
1. 모듈
노드는 자바스크립트 코드를 모듈로 만들 수 있다.
여기서 모듈이란, 특정한 기능을 하는 함수나 변수들의 집합을 의미한다. 즉, 모듈로 만들면 여러 프로그램에서 재사용이 가능하다.
예를 들어 같은 폴더 내에 var.js, func.js 파일이 있을 때,
파일 끝에 module.exports로 모듈로 만들 값을 지정하면 다른 파일에서 require(파일경로) 로 그 모듈의 내용을 가져올 수 있다.
// var.js
const odd = '홀수'
const even = '짝수'
module.exports = {
odd : odd,
even
}
// func.js
const value = require('./var')
console.log(value) // {odd : '홀수', even : '짝수'}
// 또는 구조분해할당을 활용해서
// func.js
const {odd, even} = require('./var')
exports,this,require,순환참조
1. exports
const odd = '홀수입니다.'
const even = '짝수입니다.'
module.exports = {
odd : odd,
even : even,
}
위 코드에서 사실 module을 생략할 수 있다. 만약 생략한다면 아래와 같이 표현해주어야 한다.
exports.odd = odd;
exports.even = even;
이처럼 module.exports외에도 exports로 모듈을 만들어줄 수 있다.
이유는 module.exports와 exports가 참조 관계이기 때문이다. 그러나 exports에 객체의 속성이 아닌 다른 값을 대입하면 참조관계가 깨어져 버린다.
위 내용을 정리하면 아래와 같다.
// module.exports === exports === {}
// 그런데 만약 module.exports에 함수를 넣게되면
// module.exports !== exports === {}가 되어버린다.
// 즉 참조관계가 깨어져버린다.
// 그래서 보통 한가지만 exports 하고 싶을 땐 module.exports={}와 같은 형식을 주로 사용하고
// 여러 개를 모듈화하고 싶은 경우에는
exports.odd = odd;
exports.even = even;
// 과 같은 형식을 많이 사용한다.
// 그런데 이때 주의할 점으로 위와 같이 exports를 사용했는데 아래에서 module.exports를 사용하여
// 다른 모듈을 만들면 참조관계가 깨어져버린다.
// 즉, 아래와 같이 사용하면 안된다는 의미이다.
exports.odd = odd;
exports.even = even;
module.exports = {}
2. this
노드에서 this를 사용할 때 주의점이 있다.
보통 자바스크립트에서 최상위 스코프에 있는 this는 전역객체 즉, window를 가리킨다.
그렇다면 노드에선 최상위 스코프의 this는 global(전역) 객체를 가리키지 않을까 싶지만 특별하게도
전역스코프의 this는 module.exports를 가리키며, 그 외에는 브라우저의 자바스크립트와 동일하다.
함수 선언문 내부의 this는 global 객체를 가리킨다.
console.log(this) // {}
console.log(this === module.exports) // true
console.log(this === exports) // true
3. require
require에 몇가지 알아둘 만한 속성이 있다.
1. require가 제일 위에 올 필요는 없다.
2. require.cache에 한번 require한 모듈에 대한 캐싱 정보가 들어있다.
3. require.main은 노드 실행 시 첫 모듈을 가리킨다.
module.exports = '저를 찾아보세요'
require('./var')
console.log('require.cache입니다.')
console.log(require.cache)
console.log('require.main입니다.')
console.log(require.main === module)
console.log(require.main.filename)
4. 순환참조
모듈 A가 B를 require하고 B가 다시 A를 require하는 경우 무한 반복을 막기 위해 순환참조 대상이 빈 객체가 된다.
ECMAScript 모듈, 다이나믹 임포트, top level await
1. ES 모듈
자바스크립트 자체 표준! 모듈 시스템 문법이 생겼다.
그 전까진 표준이 없었기 때문에 노드 생태계에선 CommonJS 모듈을 많이 사용해왔는데 이제 ES 모듈이 표준으로 정해지면서 점차 사용비율이 늘어나고 있고 브라우저에서도 ES모듈을 사용할 수 있어 브라우저와 노드 모두에 같은 모듈 형식을 사용할 수 있게 되었다.
스타일은 다음와 같다.
mjs 확장자를 사용하거나 package.json에 type : "module"을 추가해야 한다.
크게는 require 대신 import, module.exports 대신 exports default, exports 대신 export를 쓰는 것으로 변경되었다.
// var.mjs
export const odd = "MJS홀수입니다."
exprot const even = "MJS짝수입니다."
// func.mjs
import {odd, even} from './var.mjs'
function checkOddOrEven(num){
if(num%2){
return odd;
}
return even;
}
export default checkOddOrEven;
// index.js
import {odd, even} from './var.mjs'
import checkOddOrEven from './func.mjs'
function checkStringOddOrEven(str){
if(str.length % 2){
return odd;
}
return even;
}
CommonJS와 ES 모듈의 차이는 아래 표와 같다.
차이점 | 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' |
확장자 | js 또는 cjs | js(package.json에 type : "module" 필요) 또는 mjs |
확장자 생략 | 가능 | 불가능 |
다이내믹 임포트 | 가능 | 불가능 |
인덱스 생략 | 가능 -> require('./folder') | 불가능 -> import './folder/index.mjs' |
top level await | 불가능 | 가능 |
__filename, __dirnaem, require, module.exports, exports | 사용가능 | 불가능 |
서로 간 호출 | 가능 |
2. 다이나믹 임포트 / top level await
CommonJS 모듈에서는 다이나믹 임포트가 가능하나 ES 모듈에선는 다이나믹 임포트가 불가하다.
아래 코드처럼 조건부로 모듈을 불러오는 것을 다이나믹 임포트라고 한다.
즉, 코드 중간에 동적으로 모듈을 불러오는 것을 의미한다.
// dynamic.js
const a = false;
if(a){
require('./func')
}
console.log('성공')
// 실행결과
// 성공
위처럼 CommonJS 모듈에선 다이나믹 임포트가 가능하지만 ES 모듈은 if문 안에서 import하는 것이 불가능하다.
왜냐하면 import는 항상 코드 최상단에 위치해야 하기 때문인데
ES 모듈에서도 동적으로 모듈을 불러올 수 있는 방법이 있는데
바로 import 함수를 사용하는 것이다.
import 함수는 Promise를 반환하기 때문에 await이나 then을 앞에 반드시 붙여주어야 한다.
import 함수 사용 예시는 다음과 같으며, 아래 코드처럼 ES 모듈의 최상단 스코프에선 async 함수 없이도 await할 수 있다.
이를 top level await 이라 한다.
const a = true;
if(a){
const m1 = await import('./func.mjs')
const m2 = await import('./var.mjs')
}
3. _ _ filename, _ _ dirname
__filename : 현재 파일 경로
__dirname : 현재 폴더 (디렉토리) 경로
ES 모듈에서는 사용할 수 없기 때문에 import.meta.url을 써야 한다.
// filename.js
console.log(__filename) // C:\Users\wonji\filename.js
console.log(__dirname) // C:\Users\wonji
// filename.mjs
console.log(import.meta.url) // file:///C:/Users/wonji/filename.js
노드 내장 객체 (Global, console, 타이머)
1. Global
global은 노드의 전역 객체를 의미한다.
브라우저의 window 같은 역할이며, 모든 파일에서 접근이 가능하고 window처럼 생략도 가능하다. (console, require도 global의 속성이다.)
global 속성에 값을 대입하면 다른 파일에서도 사용이 가능하나 권장하는 사용법은 아니다.
2. Console 객체
브라우저의 console 객체와 매우 유사하다.
1. console.time, console.timeEnd : time ~ timeEnd까지 그 사이의 코드가 실행되는 시간 로깅
2. console.error : 에러 로깅
3. console.log : 평범한 로깅
4. console.dir : 객체 로깅
5. console.trace : 함수 안에서 사용하면 호출 스택을 로깅
3. 타이머
타이머 메서드
1. setTimeout(콜백함수, 밀리초) : 주어진 밀리초 이후에 콜백함수 실행
2. setInterval(콜백함수, 밀리초) : 주어진 밀리초마다 콜백 함수를 반복 실행
3. setImmediate(콜백함수) : 콜백함수를 즉시 실행
4. clearTimeout(아이디) : setTimeout을 취소
5. clearInterval(아이디) : setInterval을 취소
6. clearImmediate(아이디) : setImmediate
* 단, 취소하려면 변수에 대입을 한 다음, 그 변수를 아이디 자리에 넣고 취소해야 한다.
* setTimeout(콜백, 0)과 setImmediate(콜백)은 실행순서에서 차이가 있는데 보통 setImmediate 사용을 권장한다.
Process
현재 실행중인 노드 프로세스에 대한 정보를 담고 있다.
예를 들어
process.version : 설치된 노드의 버전
process.arch : 프로세서 아키텍쳐의 정보
process.platform : 운영체제 플랫폼 정보
등등
1. process.env
시스템 환경 변수들이 들어있는 객체로,
비밀키(데이터베이스 비밀번호, 서드파이 앱 키 등)를 보관하는 용도로 사용되며, 환경 변수는 process.env로 접근 가능하다.
const secretId = process.env.SECRET_ID;
const secretCode = process.env.SECRET_CODE;
일부 환경 변수는 노드 실행 시 영향을 미치는데
예를 들어 NODE_OPTIONS(노드 실행 옵션), UV_THREADPOOL_SIZE(스레드 풀 갯수)가 있다.
NODE_OPTIONS = --max-old-space-size=8192
// max-old-space-size는 노드가 사용할 수 있는 메모리를 지정하는 옵션
UV_THREADPOOL_SIZE = 8
2. process.nextTick(콜백)
이벤트 루프가 다른 콜백함수들보다 nextTick의 콜백 함수를 우선적으로 처리한다.
하지만 너무 남용하면 다른 콜백 함수들 실행이 늦어진다.
비슷한 경우로 promise가 있는데 이는 nextTick처럼 우선순위가 높다.
3. process.exit(코드)
현재의 프로세스를 멈추는데 코드가 없거나 0이면 정상종료, 이외의 코드는 비정상 종료를 의미한다.