티스토리 뷰
REPL 사용하기
REPL(Read Eval Print Loop)이란 노드의 콘솔을 말한다. 자바스크립트는 스크립트 언어이므로 미리 컴파일하지 않아도 콘솔에서 코드를 실행할 수 있다.
노드의 REPL입력한 코드를 읽고(Read), 해석하고(Eval), 결과물을 반환하고(Print), 종료할 때까지 반복한다.(Loop)
JS 파일 실행하기
노드에서 자바스크립트 코드를 실행할때 REPL에 직접 코드를 입력하는 대신 자바스스크립트 파일을 만들어 실행할 수 있다.
콘솔에서 REPL로 들어가는 명령어는 node이고 노드를 통해 파일을 실행하는 명령어는 node [자바스크립트 파일 경로]
이다.
모듈로 만들기
모듈이란 특정한 기능을 하는 함수나 변수들의 집합을 말한다.
모듈로 만들어두면 여러 프로그램에 해당 모듈을 재사용할 수 있다. 이것은 자바스크립트에서 코드를 재사용하기 위해 함수로 만드는 것과 비슷하다.
모듈은 단 한번만 실행되고 실행된 모듈은 공유되므로 어느 한 모듈에서 멤버변수를 수정하면 다른 모듈에서도 변경사항을 확인할 수 있다.
노드는 코드를 모듈로 만들 수 있다는 점에서 브라우저의 자바스크립트와 다르다.
노드에서는 두가지 형식의 모듈을 사용한다.
- CommonJS 모듈
- ECMAScript 모듈
CommonJS 모듈
CommonJS 모듈은 표준 자바스크립트 모듈은 아니지만 표준이 나오기 이전부터 쓰이던 방식이기에 노드 생태계에서 가장 널리 쓰이는 모듈이다.
module
모듈을을 만들 때는 모듈이 될 파일과 모듈을 불러와서 사용할 파일이 필요하다.
// var.js
const odd = 'CJS 홀수입니다';
const even = 'CJS 짝수입니다';
module.exprots = {
odd,
even,
};
// func.js
const {odd, even} = require('./var');
function checkOddOrEven(num) {
if(num % 2) {
return odd;
}
return even;
}
module.exports = checkOddOrEven;
require
함수 안에 불러올 모듈의 경로를 적는다.
파일 경로에서 js
나 json
같은 확장자는 생락할 수 있다.
다른 모듈을 사용하는 파일을 다시 모듈로 만들 수 있다.module.exprots
에는 객체에만 대입해야하는 것은 아니며 함수나 변수를 대입해도 된다.
모듈 하나가 여러 개의 모듈을 사용할 수 있다.
모듈화 장점 | 모듈화 단점 |
---|---|
여러 파일에 걸쳐 재사용되는 함수나 변수를 모듈로 만들어두면 편리하다 | 모듈이 많아지고 모듈 간의 관계가 얽히게 되면 구조를 파악하기 어렵다 |
module.export
와 export
는 같은 객체를 참조하고 있다.
console.log(module.exports === exports) // true
// exports -> module.exports -> {}
exports
는 module.exports
를 참조하고 module.exports
는 빈 객체({ }
)를 참조한다.
require
require
는 함수이고, 함수는 객체이므로 객체로서 속성 몇개를 가지고 있다.require
가 반드시 파일 최상단에 위치할 필요가 없고, module.exports
도 최하단에 위치할 필요가 없다.
require.cache
파일 이름이 속성명으로 들어가 있다.
속성 값으로는 각 파일의 모듈 객체가 들어있다.
한번 require
한 파일은 require.cache
에 저장되어 재사용된다.
require.main
require.main
은 노드 실행 시 첫 모듈을 가리킨다require.main
객체의 모양은 require.cache
의 모듈 객체와 같다.
순환참조
A라는 모듈 을 참조하는 파일 B가 있는데 파일 A 파일도 B 모듈을 참조한다면 서로 참조하고 있으므로 require를 계속 반복하게 되는데 이것을 순환참조라 부른다.
노드는 순환 참조가 있을 경우에는 순환 참조되는 대상을 빈 객체로 만든다.
ECMAScript 모듈
ECMAScript모듈(ES모듈)은 공식적인 자바스크립트 모듈 형식이다.
브라우저에서도 ES 모듈을 사용할 수 있어 브라우저와 노드에 같은 모듈 형식을 사용할 수 있다는 장점이 있다.
// var.mjs
export const odd = 'MJS 홀수입니다';
export const even = 'MJS 짝수입니다';
// func.mjs
import {odd, even} from './var.mjs';
function checkOddOrEven(num){
if(num%2){
return odd;
}
return even;
}
export default checkOddOrEven;
ES모듈의 import
나 export default
는 require
나 module
처럼 함수나 객체가 아닌 문법 그 자체다.
파일도 js
가 아닌 mjs
다. mjs
확장자 대신 js
확장자를 사용하면서 ES모듈을 사용하려면 package.json
에 type: "module"
속성을 넣어야한다.
CommonJS 모듈과는 다르게 import
시 파일 경로에서 js
, mjs
같은 확장자는 생략할 수 없다.
차이점 | ComonJS 모듈 | 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; export {E}; import {C, E} from './b.mjs'; |
확장자 | js cjs |
js (package.json 에 type: "module" 필요) mjs |
확장자 생략 | 가능 | 불가능 |
다이나믹 임포트 | 가능 | 불가능 |
인덱스(index)생략 | 가능( require('./folder') ) | 불가능( import './folder/index.mjs' ) |
top level await | 불가능 | 가능 |
__filename ,__dirname , require , module.exports , exports |
사용 가능 | 사용 불가능(__filename 대신 import.meta.url 사용) |
서로 간 호출 | 가능 |
CommonJS 모듈과 ES 모듈 서로 간에 잘 호환되지 않는 케이스가 많으므로 웬만하면 한가지 형식만 사용하는것이 좋다.
다이나믹 임포트
CommonJS 모듈에서는 모듈을 동적으로 불러올 수가 있다.(dynamic import)
ES는 다이나믹 임포트가 불가능해 import
함수를 사용해 동적으로 불러온다.
// dynamic.js
const a = false;
if(a){
require('./func');
}
// dynamic.mjs
const a = true;
if(a){
import './func.mjs'; // ES모듈은 다이나믹 임포트 불가능
}
// import 함수 사용해 동적 불러오기
const a = true;
if(a){
const m1 = await import('./func.mjs');
console.log(m1);
const m2 = await import('./var.mjs');
console.log(m2);
}
import
는 Promise
를 반환하므로 await
나 then
을 붙여야한다.
ES 모듈의 최상위 스코프에서는 async
함수 없이도 await
를 할 수 있다.(top level await)
__filename
, __dirname
노드는 __filename
, __dirname
키워드로 경로에 대한 정보를 제공한다.
하지만 경로가 문자열로 반환되기도 하고 \나 / 같은 경로 구분자 문제도 있기에 path
모듈을 함께 사용한다.
console.log(__filename); // 파일의 이름까지 경로 표시
console.log(__dirname); // 파일이있는 폴더까지 경로 표시
ES모듈에서는 __filename
과 __dirname
대신 import.meta.url
을 사용해 경로를 가져온다.
console.log(import.meta.url);
노드 내장 객체
CommonJS 모듈에서 require
함수나 module
객체는 노드 내장 객체이기 때문에 따로 선언하지 않았음에도 사용할 수 있다.
노드에서는 기본적인 내장객체와 내장 모듈을 제공한다.
내장 객체와 내장 모듈은 브라우저의 window
객체와 비슷하다.
global
브라우저의 window
와 같은 전역 객체이며 모든 파일에서 접근할 수 있다.require
함수는 원래 global.require
이지만 global
이 생략된거다.console
객체도 원래는 global.console
이다.
console
console
객체는 보통 디버깅을 위해 사용된다.
const string = 'abc';
const number = 1;
const boolean = true;
const obj = {
outside: {
inside: {
key: 'value',
}
}
}
console.time('전체 시간');
console.log('평범한 로그','쉼표로 구분해 여러 값을 찍을 수 있다', string, number, boolean, obj);
console.error('에러 메시지는 console.error에 담기')
console.table([{name:'bear', birth:1999}, {name:'hero', 1988}]);
console.dir(obj, {colors: false, depth: 2});
console.dir(obj, {colors: true, depth:1});
console.time('시간 측정');
console.tiemEnd('시간 측정');
function b() {
console.trace('에러 위치 추적');
}
function a(){
b();
}
a();
console.timeEnd('전체 시간');
- console.tiem(레이블): console.timeEnd(레이블)과 대응되어 사이의 시간을 측정한다.
- console.log(내용): 평범한 로그를 콘솔에 표시한다.
- console.error(에러 내용): 에러를 콘솔에 표시한다.
- console.table(배열): 배열의 요소로 리터럴을 넣으면, 객체의 속성들이 테이블 형식으로 표현된다.
- console.dir(객체, 옵션): 객체를 콘솔에 표시할 때 사용한다. 옵션의 colors나 depth로 색을 추가하거나 객체 안의 객체를 몇 단계까지 보여줄지를 결정한다.
- console.trace(레이블): 에러가 어디서 발생했는지 추적할 수 있게 한다.
타이머
- setTimeout(콜백 함수, 밀리초): 주어진 밀리초(1,000분의 1초) 이후에 콜백 함수를 실행한다.
- setInterval(콜백 함수, 밀리초): 주어진 밀리초마다 콜백 함수를 반복 실행한다.
- setImmediate(콜백 함수): 콜백 함수를 즉시 실행한다.
setTimeout(콜백, 0)
과 setImmediate(콜백)
은 이벤트 루프를 거친뒤 즉시 실행된다.
하지만 파일 시스템 접근, 네트워킹 같은 I/O작업의 콜백 함수 안에서 타이머를 호출하는 경우의 setImmediate
는 setTimeout
보다 먼저 실행된다.
process
현재 실행되고 있는 노드 프로세스에 대한 정보를 담고 있는 객체다.
process.env
시스템의 환경 변수들을 가지고 있는 객체다.
시스템 환경 변수 외에도 임의로 환경 변수를 저장할 수 있다.
중요한 비밀번호는 process.env
에 넣어서 사용할 수 있다.
process.nextTick(콜백)
이벤트 루프가 다른 콜백 함수들보다 nextTick
의 콜백 함수를 우선으로 처리하도록 만든다.setImmediate
나 setTimeout
보다 먼저 실행된다.process.nextTick
과 Promise
를 마이크로태스크(microtask)라고 부른다.
process.exit(코드)
실행 중인 노드 프로세스를 종료한다.
인수를 주지 않거나 0을 주면 정상 종료를 뜻하고, 1을 주면 비정상 종료를 뜻한다.
노드 내장 모듈
노드는 웹 브라우저와 달리 운영체제 정보와 클라이언트가 요청한 주소에 대한 정보를 가져올 수 있는 기능을 제공하는 모듈을 제공한다.
os
노드는 os 모듈에 담겨있는 운영체제의 정보를 가져올 수 있다.
const os = require("os");
console.log("운영체제 정보-------------------------");
console.log("os.arch():", os.arch()); // process.arch와 동일하다
console.log("os.platform():", os.platform()); // process.platform과 동일하다.
console.log("os.type():", os.type()); // 운영체제의 종류를 보여준다
console.log("op.uptime():", os.uptime()); // 운영체제 부팅 이후 흐른 시간(초)을 보여준다.
console.log("os.hostname():", os.hostname()); // 컴퓨터의 이름을 보여준다.
console.log("os.release():", os.release()); // 운영체제의 버전을 보여준다.
console.log("경로--------------------------------");
console.log("os.homedir():", os.homedir()); // 홈 디렉터리 경로를 보여준다.
console.log("os.tmpdir():", os.tmpdir()); // 임시 파일 저장 경로를 보여준다.
console.log("cpu 정보----------------------------");
console.log("os.cpus()", os.cpus()); // 컴퓨터의 코어 정보를 보여준다.
console.log("os.cpus().length", os.cpus().length); // 컴퓨터의 코어 갯수를 보여준다.
console.log("메모리 정보----------------------------");
console.log("os.freemem():", os.freemem()); // 사용 가능한 메모리(RAM)를 보여준다.
console.log("os.totalmem():", os.totalmem()); // 전체 메모리 용량을 보여준다.
운영체제 정보-------------------------
os.arch(): arm64
os.platform(): darwin
os.type(): Darwin
op.uptime(): 27160
os.hostname(): username-MacBook-Air.local
os.release(): 24.0.0
경로--------------------------------
os.homedir(): /Users/username
os.tmpdir(): /var/folders/hj/5h93y06944b3t4033f3lw65m0000gn/T
cpu 정보----------------------------
os.cpus() [
{
model: 'Apple M1',
speed: 2400,
times: { user: 2160970, nice: 0, sys: 1770290, idle: 17638770, irq: 0 }
},
{
model: 'Apple M1',
speed: 2400,
times: { user: 2011390, nice: 0, sys: 1605750, idle: 17966290, irq: 0 }
},
{
model: 'Apple M1',
speed: 2400,
times: { user: 1700650, nice: 0, sys: 1148670, idle: 18765040, irq: 0 }
},
{
model: 'Apple M1',
speed: 2400,
times: { user: 1442300, nice: 0, sys: 863360, idle: 19333200, irq: 0 }
},
{
model: 'Apple M1',
speed: 2400,
times: { user: 2106880, nice: 0, sys: 358090, idle: 19214630, irq: 0 }
},
{
model: 'Apple M1',
speed: 2400,
times: { user: 3020150, nice: 0, sys: 314120, idle: 18349880, irq: 0 }
},
{
model: 'Apple M1',
speed: 2400,
times: { user: 1438210, nice: 0, sys: 163820, idle: 20091230, irq: 0 }
},
{
model: 'Apple M1',
speed: 2400,
times: { user: 700630, nice: 0, sys: 100760, idle: 20896620, irq: 0 }
}
]
os.cpus().length 8
메모리 정보----------------------------
os.freemem(): 96321536
os.totalmem(): 17179869184
path
폴더와 파일의 경로를 쉽게 조작하도록 도와주는 모듈이다.
const path = require("path");
const string = __filename;
console.log("path.sep:", path.sep); // 경로의 구분자를 말한다 윈도우는 \, POSIX는 /이다.
console.log("path.delimiter:", path.delimiter); // 환경번수의 구분자이다. process.env.PATH를 입력하면 여러 개의 경로가 이 구분자로 구분되어있다. 윈도우는 세미콜론(;)이고, POSIX는 콜론(:)이다.
console.log("-------------------------------");
console.log("path.dirname():", path.dirname(string)); // 파일이 위치한 폴더 경로를 보여준다.
console.log("path.extname():", path.extname(string)); // 파일의 확장자를 보여준다.
console.log("path.basename():", path.basename(string)); // 파일의 이름(확장자 포함)을 표시한다. 파일에 이름만 표시하고 싶다면 base
console.log(
"path.basename - extname:",
path.basename(string, path.extname(string))
); // 파일의 이름만 표시하고 싶다면 basename의 두 번째 인수로 파일의 확장자를 넣으면 된다.
console.log("-------------------------------");
console.log("path.parse():", path.parse(string)); // 파일의 경로를 root, dir, base, ext, name으로 분리한다.
console.log(
"path.format():",
path.format({
dir: "/Users/username/study",
name: "path",
ext: ".js",
})
); // path.parse()한 객체를 파일 경로로 합친다.
console.log(
"path.normalize():",
path.normalize("////Users////username//study/path.js")
); // /나 \를 실수로 여러 번 사용했거나 혼용했을 때 정상적인 경로로 변환한다.
console.log("-------------------------------");
console.log("path.isAbsolute(/Users):", path.isAbsolute("/Users")); // 파일의 경로가 절대경로인지 상대경로인지 true나 false로 알려준다.
console.log("path.isAbsolute(./path):", path.isAbsolute("./path")); // 파일의 경로가 절대경로인지 상대경로인지 true나 false로 알려준다.
console.log(
"path.relative():",
path.relative("/Users/username/study/nodejs/ch3/path.js", "/Users")
); // 첫 번째 경로에서 두 번째 경오로 가는 방법을 알려준다.
console.log("path.join():", path.join(__dirname, "..", "..", "..", "..", ".")); // 여러 인수를 넣으면 하나의 경로로 합친다.
console.log("path.resolve():", path.resolve(__dirname, "..", "..", "..", ".")); // path.join과 비슷하지만 path.join은 상대경로로 처리하고 path.resolve는 절대경로로 처리한다.
path.sep: /
path.delimiter: :
-------------------------------
path.dirname(): /Users/username/study/nodejs/ch3
path.extname(): .js
path.basename(): path.js
path.basename - extname: path
-------------------------------
path.parse(): {
root: '/',
dir: '/Users/username/study/nodejs/ch3',
base: 'path.js',
ext: '.js',
name: 'path'
}
path.format(): /Users/username/study/path.js
path.normalize(): /Users/username/study/path.js
-------------------------------
path.isAbsolute(/Users): true
path.isAbsolute(./path): false
path.relative(): ../../../../..
path.join(): /Users
path.resolve(): /Users/username
url
인터넷 주소를 쉽게 조작하도록 도와주는 모듈이다.
const url = require("url");
const { URL } = url;
const myURL = new URL(
"http://www.gilbut.co.kr/book/bookList.aspx?sercate1=001001000#anchor"
);
console.log("new URL():", myURL);
console.log("url.format():", url.format(myURL));
new URL(): URL {
href: 'http://www.gilbut.co.kr/book/bookList.aspx?sercate1=001001000#anchor',
origin: 'http://www.gilbut.co.kr',
protocol: 'http:',
username: '',
password: '',
host: 'www.gilbut.co.kr',
hostname: 'www.gilbut.co.kr',
port: '',
pathname: '/book/bookList.aspx',
search: '?sercate1=001001000',
searchParams: URLSearchParams { 'sercate1' => '001001000' },
hash: '#anchor'
}
url.format(): http://www.gilbut.co.kr/book/bookList.aspx?sercate1=001001000#anchor
dns
DNS를 다룰 때 사용하는 모듈이다. 주로 도메인을 통해 IP나 기타 DNS 정보를 얻을때 사용한다.
crypto
다양한 방식의 암호화를 도와주는 모듈이다.
util
각종 편의 기증을 모아둔 모듈이다.
worker_threads
노드에서 멀티 스레드 방식으로 작업을 할 수 있게 도와주는 모듈이다.
child_process
노드에서 지금 실행중인 파일이아닌 다른 파일을 실행하거나 명령어를 수행하고 싶을때 사용하는 모듈이다.
fs 모듈
fs모듈은 파일 시스템에 접근하는 모듈이다.
동기 메서드와 비동기 메서드
버퍼와 스트림
버퍼링은 데이터를 모으는 동작이다. 파일을 읽을 때 메모리에 파일 크기만큼 공간을 마련해두며 파일 데이터를 메모리에 저장하는데 이때의 메모리를 버퍼라한다.
스트리밍은 데이터를 조금씩 전송하는 동작이다. 버퍼의 크기를 작게 만들고 여러 번에 걸쳐 나눠 보내는 방식을 스트림이라 한다.
이벤트
커스텀 이벤트를 만들 수 있다.
const EventEmitter = require("events");
const myEmitter = new EventEmitter();
myEmitter.on("이벤트1", () => {
console.log("이벤트 1");
});
myEmitter.addListener("이벤트2", () => {
console.log("이벤트 2");
});
myEmitter.on("이벤트2", () => {
console.log("이벤트2 추가");
});
myEmitter.once("이벤트3", () => {
console.log("이벤트 3");
});
myEmitter.emit("이벤트1"); // 이벤트1
myEmitter.emit("이벤트2"); // 이벤트2 이벤트2 추가
myEmitter.emit("이벤트3"); // 이벤트3
myEmitter.emit("이벤트3"); // 실행 안 됨
myEmitter.removeAllListeners("이벤트2");
const event2Listener = () => {
console.log("이벤트2 a");
};
myEmitter.on("이벤트2", event2Listener);
myEmitter.off("이벤트2", event2Listener);
예외처리
노드는 싱글 스레드로 작동하기 때문에 메인 스레드에서 에러가 발생하면 프로세스 전체가 멈출 수 있다. 그렇기 떄문에 에어를 처리하는 방법을 알아둬야한다. 에러가 발생할 것 같은 부분에 try/catch문으로 감싸면 된다.
try {
// 에러가 발생한다면
}
catch(err) {
// 여기서 에러처리
}
참고
'Node.js' 카테고리의 다른 글
[Node.js 교과서] 섹션 6 - 익스프레스 웹 서버 만들기 (0) | 2024.11.03 |
---|---|
[Node.js 교과서] 섹션 5 - 패키지 매니저 (1) | 2024.10.25 |
[Node.js 교과서] 섹션 4 - http 모듈로 서버 만들기 (1) | 2024.10.23 |
[Node.js 교과서] 섹션 2 - 알아두어야 할 자바스크립트 (0) | 2024.10.12 |
[Node.js 교과서] 섹션 1 - 노드의 정의와 특성 (3) | 2024.10.11 |