[Node.js 교과서] 노드 내장 모듈

fe.syhan·2023년 12월 9일
0

Node.js

목록 보기
4/4
post-thumbnail

Node.js 교과서


내장 모듈

노드는 운영체제 정보에도 접근할 수 있고, 클라이언트가 요청한 주소에 대한 정보 등 웹 브라우저에서 사용되는 자바스크립트보다 더 많은 기능을 제공한다.

os

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

os라는 파일이 존재하는 것은 아니지만 require('os') 혹은 require('node:os')로 내장 모듈인 os를 불러올 수 있다.

속성과 메서드

  • os.type(): 운영체제의 종류
  • os.uptime(): 운영체제 부팅 이후 흐른 시간(초)
  • os.hostname(): 컴퓨터의 이름
  • os.release(): 운영체제의 버전
  • os.homedir(): 홈 디렉토리 경로
  • os.tmpdir(): 임시 파일 저장 경로
  • os.cpus(): 컴퓨터의 코어 정보
  • os.freemem(): 사용 가능한 메모리(RAM)
  • os.totalmem(): 전체 메모리 용량

path

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

  • 윈도우: C:\Users\ 처럼 '\' 백슬래시로 구분한다.
  • POSIX: /home/folder 처럼 '/' 슬래시로 구분한다.

속성과 메서드

  • path.sep: 경로의 구분자. 윈도는 '\', POSIX는 '/'
  • path.delimiter(경로): 환경 변수의 구분자. process.env.PATH를 입력하면 여러 개의 경로가 이 구분자로 구분되어 있다. win은 세미콜론(;), POSIX는 콜론(:)
  • path.extname(경로): 파일의 확장자.
  • path.basename(경로, 확장자): 파일의 이름(확장자 포함)을 표시. 파일의 이름만 표시하고 싶다면 두 번째 인수로 파일의 확장자를 넣으면 된다.
  • path.parse(경로): 파일 경로를 root, dir, base, ext, name으로 분리.
  • path.format(객체): path.parse()한 객체를 파일 경로로 합친다.
  • path.normalize(경로): '/'나 '\' 를 실수로 여러 번 사용했거나 혼용했을 때 정상적인 경로로 변환한다.
  • path.isAbsolute(경로): 파일의 경로가 절대경로인지 상대경로인지를 bool값으로 알린다.
  • path.relative(기준경로, 비교경로): 경로를 두 개 넣으면 첫 번째 경로에서 두 번째 경로로 가는 방법을 알린다.
  • path.join(경로, ...): 여러 인수를 넣으면 하나의 경로로 합친다. 상대경로인 ..(부모 디렉터리)과 .(현 위치)도 알아서 처리.
  • path.resolve(경로, ...): path.join()과 비슷하지만 차이가 있다.

join과 resolve의 차이
path.join과 path.resolve 메서드는 비슷해 보이지만 동작 방식이 다르다. /를 만나면 path.resove는 정대 경로로 인시갷서 앞의 경로를 무시하고, path.join은 상대 경로로 처리한다.

path.join('/a', '/b', 'c'); // /a/b/c
path.resolve('/a', '/b', 'c') // /b/c

url

인터넷 주소를 쉽게 조작하도록 도와주는 모듈.

new 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));

① URL 생성자에 주소를 넣어 객체로 만들면 주소가 부분별로 정리된다.
URL은 노드 내장 객체이기도 해서 require할 필요는 없다)

  • url.format(객체) : 분해되었던 url 객체를 다시 원래 상태로 조립
$ node url
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'
}

searchParmas

search 부분(쿼리 스트링)은 보통 주소를 통해 데이터를 전달할 때 사용된다. ?로 시작하고 키=값형식으로 데이터를 전달한다. 여러 키가 있을 경우에는 &로 구분한다.

const myURL = new URL("http://www.gilbut.co.kr/?page=3&limit=10&category=nodejs&category=javascript");

console.log("searchParams:", myURL.searchParams);
console.log("searchParams.getAll():", myURL.searchParams.getAll("category"));
console.log("searchParams.get():", myURL.searchParams.get("limit"));
console.log("searchParams.has():", myURL.searchParams.has("page"));

console.log("searchParams.keys():", myURL.searchParams.keys());
console.log("searchParams.values():", myURL.searchParams.values());

myURL.searchParams.append("filter", "es3");
myURL.searchParams.append("filter", "es5");
console.log(myURL.searchParams.getAll("filter"));

myURL.searchParams.set("filter", "es6");
console.log(myURL.searchParams.getAll("filter"));

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

console.log("searchParams.toString():", myURL.searchParams.toString());
myURL.search = myURL.searchParams.toString();
$ node searchParams
searchParams: URLSearchParams {
  'page' => '3',
  'limit' => '10',
  'category' => 'nodejs',
  'category' => 'javascript' }
searchParams.getAll(): [ 'nodejs', 'javascript' ]
searchParams.get(): 10
searchParams.has(): true
searchParams.keys(): URLSearchParams Iterator { 'page', 'limit', 'category', 'category' }
searchParams.values(): URLSearchParams Iterator { '3', '10', 'nodejs', 'javascript' }
[ 'es3', 'es5' ]
[ 'es6' ]
[]
searchParams.toString(): page=3&limit=10&category=nodejs&category=javascript

URL 생성자를 통해 만들어진 인스턴스에는 searchParams 객체가 있다. 이 객체는 search 부분을 조작하는 다양한 메서드를 지원한다.
new URLSearchParams(myURL.search)로도 같은 결괏값을 얻을 수 있다.

  • getAll(key): 키에 해당하는 모든 값을 가져온다.
  • get(key): 키에 해당하는 첫 번째 값만 가져온다.
  • has(key): 해당 키가 있는지 없는지를 검사한다.
  • keys(): searchParams의 모든 키를 Iterator 객체로 가져온다.
  • values(): searchParams의 모든 값을 Iterator 객체로 가져온다.
  • append(key, value): 해당 키를 추가한다. 같은 키의 값이 있다면 유지하고 하나 더 추가한다.
  • set(key, value): append와 비슷하지만 같은 키의 값들을 모두 지우고 새로 추가한다.
  • delete(key): 해당 키를 제거한다.
  • toString(): 조작한 searchParams 객체를 다시 문자열로 만든다. 이 문자열을 search에 대입하면 주소 객체에 반영된다.

dns

DNS를 다룰 때 사용하는 모듈이다. 주로 도메인을 통해 IP나 기타 DNS 정보를 얻고자 할 때 사용한다.

import dns from 'dns/promises';

const ip = await dns.lookup('gilbut.co.kr');
console.log('IP', ip);

const a = await dns.resolve('gilbut.co.kr', 'A');
console.log('A', a);

const mx = await dns.resolve('gilbut.co.kr', 'MX');
console.log('MX', mx);

const cname = await dns.resolve('www.gilbut.co.kr', 'CNAME');
console.log('CNAME', cname);

const any = await dns.resolve('gilbut.co.kr', 'ANY');
console.log('ANY', any);
$ node dns.mjs
IP { address: '49.236.151.220', family: 4 }
A [ '49.236.151.220' ]
MX [
  { exchange: 'aspmx.l.google.com', priority: 1 },
  { exchange: 'alt2.aspmx.l.google.com', priority: 5 },
  { exchange: 'aspmx2.googlemail.com', priority: 10 },
  { exchange: 'aspmx3.googlemail.com', priority: 10 },
  { exchange: 'alt1.aspmx.l.google.com', priority: 5 }
]
CNAME [ 'slb-1088813.ncloudslb.com' ]
ANY [
  { value: 'ns1-2.ns-ncloud.com', type: 'NS' },
  { value: 'ns1-1.ns-ncloud.com', type: 'NS' },
  {
    nsname: 'ns1-1.ns-ncloud.com',
    hostmaster: 'ns1-2.ns-ncloud.com',
    serial: 76,
    refresh: 21600,
    retry: 1800,
    expire: 1209600,
    minttl: 300,
    type: 'SOA'
  }
]

ip 주소는 간단하게 dns.lookup이나 dns.resolve(도메인)으로 얻을 수 있다. A(ipv4 주소), AAAA(ipv6 주소), NS(네임서버), SOA(도메인 정보), CNAME(별칭), MX(메일 서버) 등 레코드라고 부르는데 해당 레코드에 대한 정보는 dns.resolve(도메인, 레코드 이름)으로 조회 가능하다.

crypto

다양한 방식의 암호화를 도와주는 모듈이다.

단방향 암호화

단방향 암호화란 복호화할 수 없는 암호화 방식을 뜻한다.
복호화는 암호화된 문자열을 원래 문자열로 되돌려 놓는 것을 의미한다. 즉, 단뱡향 암호화는 한번 암호화하면 원래 문자열을 찾을 수 없다.
복호화가 불가능하기 때문에 암호화 대신 해시 함수라고 부르기도 한다.

단방향 알고리즘은 주로 해시 기법을 사용한다. 해시 기법이란 어떠한 문자열을 고정된 길이의 다른 문자열로 바꿔버리는 방식이다.

const crypto = require("crypto");

console.log('base64:', crypto.createHash('sha512').update('비밀번호').digest('base64'));
console.log('hex:', crypto.createHash('sha512').update('비밀번호').digest('hex'));
console.log('base64:', crypto.createHash('sha512').update('다른 비밀번호').digest('base64'));
$ node hash
base64: dvfV6nyLRRt3NxKSlTHOkkEGgqW2HRtfu19Ou/psUXvwlebbXCboxIPmDYOFRIpqav2eUTBFuHaZri5x+usy1g==
hex: 76f7d5ea7c8b451b773712929531ce92410682a5b61d1b5fbb5f4ebbfa6c517bf095e6db5c26e8c483e60d8385448a6a6afd9e513045b87699ae2e71faeb32d6
base64: cx49cjC8ctKtMzwJGBY853itZeb6qxzXGvuUJkbWTGn5VXAFbAwXGEOxU2Qksoj+aM2GWPhc1O7mmkyohXMsQw==
  • createHash(알고리즘): 사용할 해시 알고리즘을 넣는다. md5, sha1, sha256, sha512 등이 가능하지만 md5와 sha1은 취약점이 발견되었다. 현재 sha512 정도로 충분하지만 sha512도 취약해지면 더 강화된 알고리즘으로 바꾸어야 한다.
  • update(문자열): 변환할 문자열을 넣는다.
  • digest(인코딩): 인코딩할 알고리즘을 넣는다. base64, hex, latin1이 주로 사용되며, 그중 base64가 결과 문자열이 가장 짧아서 애용된다. 결과물로 변환된 문자열을 반환한다.

PBKDF2(Password-Based Key Derivation Function version 2)

주로 pbkdf2나 bcrypt, scrypt 알고리즘으로 패스워드를 암호화하는데 그중에서도 pbkdf2는 기존 문자열에 salt라고 불리는 문자열을 붙인 후 해시 알고리즘을 반복해서 적용하는 방식이다.

const crypto = require("crypto");

crypto.randomBytes(64, (err, buf) => {
  const salt = buf.toString("base64");
  console.log("salt:", salt);
  crypto.pbkdf2("비밀번호", salt, 100000, 64, "sha512", (err, key) => {
    console.log("password", key.toString("base64"));
  });
});

randomBytes() 메서드로 64바이트 길이의 문자열을 만들면 이것이 salt가 된다. pbkdf2() 메서드에는 순서대로 비밀번호, salt, 반복 횟수, 출력 바이트, 해시 알고리즘을 인수로 넣는다.

  • randomBytes이므로 매번 실행할 때마다 결과가 달라지므로 salt를 잘 보관해야만 비밀번호도 찾을 수 있다.
  • pbkdf2는 간단하지만 bcrypt나 scrypt보다 취약하다.

양방향 암호화

대칭키

암호화된 문자열을 복호화할 수 있으며, 키(열쇠)가 사용된다.
암호를 복호화 하려면 암호화할 때 사용한 키와 같은 키를 사용해야 한다.

const crypto = require("crypto");

const algo = "aes-256-cbc";
const key = "abcdefghijklmnopqrstuvwxyz123456";
const iv = "1234567890123456";

const cipher = crypto.createCipheriv(algo, key, iv);
let result = cipher.update("암호화할 문장", "utf8", "base64");
result += cipher.final("base64");
console.log("암호화:", result);

const decipher = crypto.createDecipheriv(algo, key, iv);
let result2 = decipher.update(result, "base64", "utf8");
result2 += decipher.final("utf8");
console.log("복호화", result2);
  • crypto.createCipheriv(알고리즘, 키, iv): 암호화 알고리즘과 키, iv를 넣는다. aes-256-cbc알고리즘의 경우 키는 32바이트여야 하고, iv는 16바이트여야 한다. iv는 암호화할 때 사용하는 초기화 벡터를 의미한다.
    추가로 crypto.getCipher()를 호출하면 사용 가능한 알고리즘 목록을 볼 수 있다.
  • cipher.update(문자열, 인코딩, 출력 인코딩): 암호화할 대상과 대상의 인코딩, 출력 결과물의 인코딩을 넣는다. 보통 문자열은 utf8, 암호는 base64를 많이 용한다.
  • cipher.createDecipheriv(알고리즘, 키, iv): 복호화할 때 사용한다. 암호화할 떄 사용했던 알고리즘과 키, iv를 그대로 넣어야 한다.
  • decipher.update(문자열, 인코딩, 출력 인코딩): 암호화된 문장, 그 문장의 인코딩, 복호화할 인코딩. createCipheriv.update()에서 utf8, base64순으로 넣었다면 base64, utf8 순으로 넣으면 된다.
  • decipher.final(출력 인코딩): 복호화할 결과물의 인코딩을 넣는다.

crypto 모듈은 다양한 암호화를 제공하고 있으니 어떤 메서드를 지원하는지 공식 문서에서 확인이 가능하다.
https://nodejs.org/api/crypto.html

util

각종 편의 기능을 모아둔 모듈이다. 계속 API가 추가되고 있으며 desperated되어 사라지는 경우도 있다.

deprecated?
프로그래밍 용어로, '중요도가 떨어져 더 이상 사용되지 않고 앞으로는 사라지게 될'이라는 뜻. 새로운 기능이 나와서 기존 기능보다 더 좋을 때, 기존 기능을 desperated 처리하곤 한다. 이전 사용자를 위해 제거하지는 않지만 곧 없앨 예정이르모 더 이상 사용하지 말라는 의미다.

const util = require("util");
const crypto = require("crypto");

const dontUseMe = util.deprecate((x, y) => {
  console.log(x + y);
}, "dontUseMe 함수는 desperted 되었으니 더 이상 사용하지 마세요!");
dontUseMe(1, 2);

const randomBytesPromise = util.promisify(crypto.randomBytes);
randomBytesPromise(64)
  .then((buf) => {
    console.log(buf.toString("base64"));
  })
  .catch((err) => {
    console.log(err);
  });
  • util.deprecatedd: 함수가 deprecated 처리되었음을 알린다. 첫 번재 인수로 넣은 함수를 사용했을 때 경고 메시지가 출력된다. 두 번째 인수로 경고 메시지 내용을 넣으면 된다.
  • util.promisify: 콜백 패턴을 프로미스 패턴으로 바꾼다. 바꿀 함수를 인수로 제공하면 된다.

그외 모듈들

  • worker_threads: 노드에서 멀티 스레드 방식으로 작업하기 위한 모듈들을 제공한다.
  • child_process: 노드에서 다른 프로그램을 실행하고 싶거나 명령어를 수행하고 싶을 때 사용 하는 모듈. 다른 언어의 코드를 실행하고 결괏값을 받을 수 있다.
  • async_hooks: 비동기 코드의 흐름을 추적할 수 있는 실험적인 모듈
  • dgram: UDP와 관련된 작업을 할 때 사용.
  • net: HTTP보다 로우 레벨인 TCP나 IPC 통신을 할 때 사용.
  • pref_hooks: 성능 측정을 할 때 console.time 보다 더 정교하게 측정
  • queryString: URLSearchParams가 나오기 이전에 쿼리스트링을 다루기 위해 사용했던 모듈. 근래엔 URLSearchParams을 사용하는 것을 권장.
  • string_decoder: 버퍼 데이터를 문자열로 바꾸는 데 사용.
  • tls: TLS와 SSL에 관련된 작업을 할 때 사용
  • tty: 터미널과 관련된 작업을 할 때 사용
  • v8: v8엔진에 직접 접근할 때 사용
  • vm: 가상 머신에 직업 접근할 댸 사용
  • wasi: 웹어셈블리를 실행할 댸 사용하는 실험적인 모듈

0개의 댓글