Skip to content

Latest commit

 

History

History
556 lines (384 loc) · 18.2 KB

3. 노드 기능 알아보기.md

File metadata and controls

556 lines (384 loc) · 18.2 KB

3장. 노드 기능 알아보기

3.1 REPL 사용하기

자바스크립트는 스크립트 언어로, 미리 컴파일하지 않아도 바로 코드를 실행할 수 있다.
노드도 비슷한 콘솔을 제공하며, REPL(Read, Eval, Print, Loop)라고 한다.

사용방법은 다음과 같다.
맥과 리눅스는 터미널에서 node를 입력한다.
VS Code는 터미널에서 Ctrl + \`

프롬프트가 > 모양으로 바뀐 후, 자바스크립트 코드 입력이 가능하다.

REPL을 종료하기 위해서는 Ctrl + C를 두번 누르거나, REPL 창에 .exit을 입력한다.

REPL은 한두 줄 짜리 코드를 테스트 해보는 용도 정도로 사용할 수 있다.


3.2 JS 파일 실행하기

당연하게도, 자바스크립트 파일을 만들어 실행할 수 있다.
파일은 아무 폴더(디렉터리) 안에 만들어도 된다.

콘솔에서 node [자바스크립트 파일 경로]로 실행한다. (확장자 생략 가능)
REPL이 아니라 터미널에서 입력해야 한다.


3.3 모듈로 만들기

노드는 코드를 모듈로 만들 수 있다는 점에서 브라우저의 자바스크립트와는 다르다.
모듈이란 특정 기능을 하는 함수나 변수들의 집합을 말한다.
모듈로 만들어두면 여러 프로그램에서 모듈을 재사용할 수 있다. (자바스크립트의 함수와 비슷한 기능)

노드에서는 두 가지 형식의 모듈을 사용하는데, 하나는 CommonJS 모듈이고 하나는 ECMAScript 모듈.


3.3.1 CommonJS 모듈

CommonJS 모듈은 표준이 아니지만, 표준이 만들어지기 이전부터 쓰였기 때문에 가장 많이 쓰인다.

실질적으로 모듈이 사용되는 형식은 다음과 같다.

// var.js
const odd = "홀수";
const even = "짝수";

module.exports = {
  odd,
  even,
};
// func.js
const { odd, even } = require("./var");

function checkOddOrEven(num) {
  return num % 2 ? odd : even;
}

exports.checkOddOrEven = checkOddOrEven;
// index.js
const checkNumber = require("./func");

checkNumber(10);

module 객체가 아닌 exports 객체로도 모듈을 만들 수 있다.
exports 객체에 값을 넣으면 module.exports에도 함수가 들어간다.

export 객체는 module.exports 객체를 참조하고 module.exports 객체는 {} 객체를 참조한다.

중요한 것은 exports와 module.exports 둘의 참조관계가 깨지지 않도록 주의 해야한다.
이 떄문에 한 모듈에 두 가지 방식을 동시에 사용하지는 않는다.

`노드에서의 this`

```javascript
console.log(this); // {}
console.log(this === module.exports); // true
console.log(this === exports); // true

function whatIsThis() {
  console.log(this === exports); // false
  console.log(this === global); // true
}
whatIsThis();
```

다른 부분은 브라우저의 자바스크립트와 동일하지만,<br/>
최상위 스코프에 존재하는 this는 module.exports 또는 exports 객체를 가리킨다.

또한, 함수 선언문 내부의 this는 global 객체를 가리킨다.
// 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();
**$ node dep-run**
require dep1 {}<br/>
require dep2 [Function (anonymous)]<br/>
dep2 [Function (anonymous)]<br/>
dep1 {}

Warning: -

이처럼 순환 참조가 발생할 경우, 순환 참조 대상을 빈 객체로 만든다.
순환 참조가 발생하지 않도록 구조를 잘 잡는 것이 중요하다.


3.3.2 ECMAScript 모듈

공식적인 자바스크립트 표준 모듈.
사용법은 익숙하기 때문에 생략.


3.3.3 다이내믹 임포트

CommonJS 모듈 에서는 다이내믹 임포트가 가능하지만 ES 모듈에서는 불가능하다.

const TARGET = false;

if (TARGET) {
  require("/func");
}

위와같은 조건부 모듈 불러오기가 다이내믹 임포트.
그렇다면 ES 모듈에서는 어떠한 방식으로 다이내믹 임포트를 구현하는가

const TARGET = true;

if (TARGET) {
  const m1 = await import("./func.mjs");
  const m2 = await import("./var.mjs");
}

import라는 함수를 사용해서 모듈을 동적으로 불러올 수 있다.
import는 Promise를 반환하기에 await이나 then을 붙여 사용해야 한다.

위의 코드에서는 async를 사용하지 않았는데,
ES 모듈 최상위 스코프에서는 async 함수 없이도 await을 할 수 있다.


3.3.4 __filename, __dirname

노드에서는 파일 사이에 모듈 관계가 있는 경우가 많기 때문에
현재 파일의 경로나 파일명을 알아야 하는 경우가 있다.

__filename, __dirname 키워드를 통해 경로에 대한 정보를 제공한다.

사용되는 OS 환경에 따라 폴더 경로 구분자가 /혹은 \로 달라지기 때문에
보통은 이를 해결해주는 path 모듈과 함께 사용한다.

ES 모듈에서는 __filename, __dirname을 사용할 수 없기 떄문에,
import.meta.url로 경로를 가져와서 사용한다.


3.4 노드 내장 객체 알아보기

3.4.1 global

브라우저의 window와 같은 전역 객체로, 모든 파일에서 접근할 수 있다.
(console 객체도 원래는 global.console)

global 객체 내부에는 굉장히 많은 속성들이 들어있는데, 확인을 위해서는 REPL을 사용.

노드에는 DOM이나 BOM이 없어 window와 document 객체를 사용할 수 없다.
그래서 이 둘을 아우르는 globalThis 객체가 만들어짐.
globalThis는 브라우저 환경에서는 window로, 노드에서는 global로 동작한다.


3.4.2 console

console도 노드에서는 window 대신 global 객체 안에 들어있다.

console 객체는 보통 디버깅을 위해 사용한다.
다음과 같은 로깅 함수들도 존재한다.

  • console.time(레이블)
    consoletimeEnd(레이블)과 대응되어 두 레이블 사이의 시간을 측정한다.

  • console.log(내용)
    평범한 로그를 콘솔에 출력.

  • console.error(에러 내용)
    에러를 콘솔에 표시한다.

  • console.table(배열)
    배열의 요소로 객체 리터럴을 넣으면, 객체 속성들이 테이블 형식으로 표현됨.

  • console.dir(객체, 옵션)
    객체를 콘솔에 표시할 때 사용.

  • console.trace(레이블)
    에러가 어디서 발생했는지 추적할 수 있게 함. 보통은 에러 발생의 위치를 알 수 있어 자주 사용하지는 않음.


3.4.3 timer

타이머 기능을 제공하는 함수들은 노드에서는 window 대신 global 객체 안에 들어있다.

  • setTimeout(콜백 함수, 밀리초)
    주어진 밀리초 이후에 콜백 함수를 실행.

  • setInterval(콜백 함수, 밀리초)
    주어진 밀리초마다 콜백 함수를 반복 실행.

  • setImmediate(콜백 함수)
    콜백 함수를 즉시 실행.

  • clearTimeout(아이디)
    setTimeout을 취소.

  • clearInterval(아이디)
    setInterval을 취소.

  • clearImmediate(아이디)
    setImmediate를 취소.

setImmediate(콜백)과 setTimeout(콜백, 0)의 차이.

특수한 경우에 setImmediate(콜백)은 setTimeout(콜백, 0)보다 먼저 실행된다.
파일 시스템 접근, 네트워킹 같은 I/O 작업의 콜백 함수 안에서 타이머를 호출하는 경우.

하지만 setImmediate(콜백)이 setTimeout(콜백, 0)보다 반드시 먼저 실행되는 것은 아니다. setTimeout(콜백, 0)을 사용하지 않는 것을 권장.


3.4.4 process

process 객체는 현재 실행되고 있는 노드 프로세스에 대한 정보를 담고 있다.

해당 정보들의 사용 빈도는 높지 않지만, 운영체제나 실행 환경별로 다른 동작을 구현하고자 할 때 사용한다.

  • process.env

    시스템의 환경 변수를 저장하는 공간.

    대표적으로 NODE_OPTIONS나 UV_THREADPOOL_SIZE가 있다.
    NODE_OPTIONS는 노드의 메모리를 사용할 수 있는 용량이며,
    UV_THREADPOOL_SIZE는 노드에서 기본적으로 사용하는 스레드 풀의 스레드 개수이다.

    시스템 환경 변수 외에도 개발자가 임의로 환경 변수를 저장할 수 있다.
    process.env는 서비스의 중요한 키를 젖아하는 공간으로도 사용된다.
    따라서 중요한 비밀번호는 process.env의 속성으로 대체한다.

    넣는 방법은 운영체제마다 차이가 있지만, dotenv 등의 라이브러리를 통해 동일하게 넣는 방법을 부여할 수 있다.

  • process.nextTick(콜백)

    이벤트 루프가 다른 콜백 함수들보다 nextTick의 콜백 함수를 우선으로 처리하도록 만든다.
    process.nextTick은 setImmediate나 setTimeout보다 먼저 실행된다.

    setImmediate(() => {
      console.log("4");
    });
    
    setTimeout(() => {
      console.log("3");
    });
    
    process.nextTick(() => {
      console.log("1");
    });
    
    Promise.resolve().then(() => console.log("2"));

    process.nextTick과 resolved된 Promise는 마이크로태스크 큐에 등록되어,
    다른 태스크 큐의 콜백들보다 우선시된다.

  • process.exit

    실행 중인 노드 프로세스를 종료한다.
    서버에서 해당 함수를 사용하면 서버가 정지되어 버리기 때문에, 특수한 경우를 제외하고는 사용하지 않는다.

    인수를 0을 주면 정상 종료를 뜻하고, 1을 주면 비정상 종료를 뜻한다.


3.4.5 기타 내장 객체

fetch를 노드에서도 쓸 수 있게 됨에 따라 브라우저에 존재하던 객체들이 노드에도 동일하게 생성되었다.

  • URL, URLSearchParams

  • AbortController, FormData, fetch, Headers, Request, Response, Event, EventTarget
    브라우저에서 사용하던 API가 노드에서도 동일하게 생성됨.

  • TextDecoder & TextEncoder
    Buffer에서 문자열로, 문자열에서 Buffer로 바꾼다.

  • WebAssembly
    웹어셈블리 처리를 담당.


3.5 노드 내장 모듈 사용하기

노드는 웹 브라우저에서 사용되는 자바스크립트보다 더 많은 기능을 제공한다.
공식 문서에 모두 나와있는 내용이지만, 자주 사용하는 것만 정리한다.

3.5.1 os

웹 브라우저에서 사용되는 자바스크립트는 운영체제의 정보를 가져올 수 없지만, 노드는 os 모듈에 정보가 담겨 있어 정보를 가져올 수 있다.

const os = require("node:os"); // os만 입력해도 되지만, 내장 모듈임을 명시하기 위해 'node:'를 추가적으로 입력.

console.log("운영체제 정보 ---");
console.log("os.arch():", os.arch()); // os.arch(): x64
console.log("os.platform():", os.platform()); // os.platform(): win32
console.log("os.type():", os.type()); // os.type(): Windows NT
console.log("os.uptime():", os.uptime()); // os.uptime(): 53354 // 운영체제 부팅 이후 흐른 시간.
console.log("os.hostname():", os.hostname()); // os.hostname(): DESKTOP-RRANDNC
console.log("os.release():", os.release()); // os.release(): 10.0.18362 // 운영체제 버전

console.log("경로 ---");
console.log("os.homedir():", os.homedir()); // os.homedir(): C:\Users\zerocho
console.log("os.tmpdir():", os.tmpdir()); // os.tmpdir(): C:\Users\zerocho\AppData\Local\Temp // 임시 파일 저장 경로

console.log("cpu 정보 --- ");
console.log("os.cpus():", os.cpus()); // 컴퓨터의 코어 정보
// os.cpus(): [ { model: 'Intel(R) Core(TM) i5-9400F CPU @ 2.90GHz'
// speed: 2904,
// times: user: 970250, nice: 0, sys: 1471906, idle: 9117578, irq: 359109 } },
// (다른 코어가 다면 나머지 코어의 정보가 나옴)
console.log("os.cpus().length:", os.cpus().length); // os.cpus().length: 6

console.log("메모리 정보 --- ");
console.log("os.freemem():", os.freemem()); // os.freemem(): 23378612224 // 사용 가능한 메모리
console.log("os.totalmem ():", os.totalmem()); // os.totalmem(): 34281246720 // 전체 메모리 용량

os 모듈은 주로 컴퓨터 내부 자원에 빈번하게 접근하는 경우 사용된다.
즉, 일반적인 웹 서비스를 제작할 때는 사용 빈도가 높지 않다.


3.5.2 path

폴더와 파일의 경로를 쉽게 조작하도록 도와주는 모듈.
path 모듈이 필요한 이유 중 하나나는 운영체제별로 경로 구분자가 다르기 때문.

윈도: \ 로 구분. POSIX(맥, 윈도우): / 로 구분.

const path = require("node:path");

const string = _filename;

console.log("path.sep", path.sep); // path.sep: \ // 경로 구분자
console.log("path.delimiter:", path.delimiter); // path.delimiter: ; // 환경 변수 구분자
console.log("----------------------------");
console.log("path.dirname():", path.dirname(string)); // path.dirname(): C:\Users\sangmin
console.log("path.extname():", path.extname(string)); // path.extname(): .js
console.log("path.basename():", path.basename(string)); // path.basename(): path.js
console.log(
  "path.basename - extname:",
  path.basename(string, path.extname(string))
); // path.basename extname: path
console.log("----------------------------");
console.log("path.parse()", path.parse(string));
// path.parse() { root: 'C:\\', dir: 'C:\\Users\\zerocho', base: 'path.js', ext: '.js', name: 'path' }
console.log(
  "path. format():",
  path.format({ dir: "C:\\users\\zerocho", name: "path", ext: ".js" })
); // path.format(): C:\users\zerocho\path.js
console.log(
  "path.normalize():",
  path.normalize("C://users\\\\zerocho\\path.js")
); // path.normalize(): C:\users\zerocho\path.js // 경로 구분자의 연속 사용에서 정상적인 경로로 변환
console.log("----------------------------");
console.log("path.isAbsolute(C:\\):", path.isAbsolute("C:\\")); // path.isAbsolute(C:\\): true // 파일의 경로가 절대경로인지 여부
console.log("path.isAbsolute(./home):", path.isAbsolute("./home")); // path.isAbsolute(./home): false // 파일의 경로가 절대경로인지 여부
console.log("----------------------------");
console.log(
  "path.relative():",
  path.relative("C:\\users\\zerocho\\path.js", "C:\\")
); // path.relative(): ..\..\.. // 첫 번째 인자의 경로에서 두 번재 인자의 경로로 가는 방법을 알려줌
console.log(
  "path.join():",
  path.join(__dirname, "..", "..", "/users", ".", "/sangmin")
); // path.join(): C:\Users\sangmin // 하나의 경로로 합침
console.log(
  "path.resolve():",
  path.join(__dirname, "..", "/users", ".", "/sangmin")
); // path.resolve(): C:\sangmin

3.5.3 url

인터넷 주소를 쉽게 조작하도록 도와주는 모듈.
현재는 WHATWG(웹 표준을 정하는 단체의 이름)의 url 처리 방식만을 사용.

"https://user:[email protected]:8080/path?query=string#hash"

부분 설명
https: protocol
user username
pass password
sub.host.com hostname
8080 port
sub.host.com:8080 host
https://:@sub.host.com:8080 origin
path pathname
?query=string search
#hash hash
https://user:[email protected]:8080/path?query=string#hash href

const url = require("url");

const { URL } = url;
const myURL = new URL(
  "https://github.com/dltkdals224/dev-book_and_lecture/blob/main/Node.js%20%EA%B5%90%EA%B3%BC%EC%84%9C/README.md"
);

console.log(myURL); // 주소가 상위 언급한 부분별로 정리되어 출력
console.log(url.format(myURL)); // 분히되었던 url 객체를 다시 원래 상태로 조립.

참고로 URL은 노드 내장 객체이기도 하기 때문에 require 할 필요는 없다.

또한 주소가 host 부분 없이 pathname 부분만 오는 경우 WHATWG 방식은 이 주소를 처리할 수 없다.
이럴 때에는 new URL()의 두 번째 인자로 host를 직접 작성해야 한다.

search 부분(쿼리스트링)은 보통 주소를 통해 데이터를 전달할 때 사용한다.
해당 부분을 다루기 위해 searchParams라는 특수한 객체를 사용한다.

const url = require("url");

const { URL } = url;
const myURL = new URL(
  "https://github.co.kr/page=3&limit=10&category=nodejs&category=javascript"
);

console.log(myURL.searchParams.getAll("category")); // [ 'nodejs', 'javascript' ]
console.log(myURL.searchParams.get("limit")); // 10
console.log(myURL.searchParams.has("page")); // true

console.log(myURL.searchParams.keys());
// URLSearchParams Iterator { 'page', 'limit', 'category', 'category' }
console.log(myURL.searchParams.values());
// URLSearchParams Iterator { '3', '10', 'nodejs', 'javascript' }

myURL.searchParams.append("filter", "es3");
myURL.searchParams.append("filter", "es5");
console.log(myURL.searchParams.getAll("filter")); // [ 'es3', 'es5' ]

myURL.searchParams.set("filter", "es6");
console.log(myURL.searchParams.getAll("filter")); // [ 'es6' ]

myURL.searchParams.delete("filter");
console.log(myURL.searchParams.getAll("filter")); // []

console.log(myURL.searchParams.toString());
// page=3&limit=10&category=nodejs&category=javascript

3.5.4 dns


3.5.5 crypto


3.5.6 util


3.5.7 worker_threads


3.5.8 child_process


3.5.9 기타 모듈들


3.6 파일 시스템 접근하기


3.7 이벤트 이해하기


3.8 예외 처리하기


3.9 함께 보면 좋은 자료