본문 바로가기

Node.js

[Node] 노드의 내장 모듈들

주요한 노드 내장 모듈들에 대해 빠르게 알아보자!

 

1️⃣ os

- 사용자 컴퓨터의 운영체제 정보

- 일반적인 웹 서비스를 제작할 때는 사용 빈도가 높지 않지만 운영체제 별로 다른 서비스를 제공하고 싶을 때 유용

 os.arch(): process.arch와 동일

 os.platform(): process.platform과 동일

 os.type(): 운영체제의 종류를 보여줌

 os.uptime(): 운영체제 부팅 이후 흐른 시간(초)을 보여줌. process.uptime()은 노드의 실행 시간

 os.hostname(): 컴퓨터의 이름을 보여줌

 os.release(): 운영체제의 버전을 보여줌

 os.homedir(): 홈 디렉터리 경로를 보여줌

 os.tmpdir(): 임시 파일 저장 경로를 보여줌

 os.cpus(): 컴퓨터의 코어 정보를 보여줌

 os.freemem(): 사용 가능한 메모리(RAM)를 보여줌

 os.totalmem(): 전체 메모리 용량을 보여줌

 

 

2️⃣ path

- 폴더와 파일의 경로를 쉽게 조작하도록 도와주는 모듈

- 앞으로 노드 프로그래밍을 하면서 매우 자주 쓰게 될 모듈 중 하나

 path.sep: 경로의 구분자. 윈도는 \, POSIX는 /

 path.delimiter: 환경 변수의 구분자. process.env.PATH를 입력하면 여러 개의 경로가 이 구분자로 구분됨. 윈도는 세미콜론(;)이고, POSIX는 콜론(:).

 path.dirname(경로): 파일이 위치한 폴더 경로를 보여줌.

 path.extname(경로): 파일의 확장자를 보여줌.

 path.basename(경로, 확장자): 파일의 이름(확장자 포함)을 표시. 파일의 이름만 표시하고 싶다면 basename의 두 번째 인수로 파일의 확장자를 넣으면 됨

 path.parse(경로): 파일 경로를 root, dir, base, ext, name으로 분리

 path.format(객체): path.parse()한 객체를 파일 경로로 합침

 path.normalize(경로): / \를 실수로 여러 번 사용했거나 혼용했을 때 정상적인 경로로 변환

 path.isAbsolute(경로): 파일의 경로가 절대경로인지 상대경로인지를 true false로 알림

 path.relative(기준경로, 비교경로): 경로를 두 개 넣으면 첫 번째 경로에서 두 번째 경로로 가는 방법을 알림

 path.join(경로, …): 여러 인수를 넣으면 하나의 경로로 합침. 상대경로인 ..(부모 디렉터리)과 .(현 위치)도 알아서 처리.

 path.resolve(경로, …): path.join()과 비슷하지만 차이가 있음. 

 

 

3️⃣ url

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

 

url 처리의 두가지 방식

1. WHATWG

const url = require('url');

// url 모듈 안에 URL 생성자가 있음
// 이 URL 생성자에 주소를 넣어 객체로 만들면 주소가 부분별로 정리됨
// WHATWG에만 있는 username, password, origin, searchParams 속성이 존재


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));
console.log('------------------------------');
const parsedUrl = url.parse('http://www.gilbut.co.kr/book/bookList.aspx?sercate1=001001000#anchor');
console.log('url.parse():', parsedUrl);
console.log('url.format():', url.format(parsedUrl));

2. 기존 노드 방식 : 아래 두개의 메서드를 주로 사용

(1) url.parse(주소)

      - 주소를 분해

      - WHATWG의 username과 password 대신 auth 속성

      - WHATWG의 searchParams 대신 query 속성

(2) url.format(객체)

      - WHATWG 방식 url과 기존 노드의 url을 모두 사용할 수 있음

      - 분해되었던 url 객체를 다시 원래 상태로 조립

 

searchParams 객체의 여러 메서드들

 getAll(키): 키에 해당하는 모든 값들을 가져옴. category 키에는 nodejs javascript라는 두 가지 값이 들어 있음.

 get(키): 키에 해당하는 첫 번째 값만 가져옴.

 has(키): 해당 키가 있는지 없는지를 검사

 keys(): searchParams의 모든 키를 반복기(iterator)(ES2015 문법) 객체로 가져옴

 values(): searchParams의 모든 값을 반복기 객체로 가져옴

 append(키, 값): 해당 키를 추가합니다. 같은 키의 값이 있다면 유지하고 하나 더 추가

 set(키, 값): append와 비슷하지만, 같은 키의 값들을 모두 지우고 새로 추가.

 delete(키): 해당 키를 제거

 toString(): 조작한 searchParams 객체를 다시 문자열로 만듦. 이 문자열을 search에 대입하면 주소 객체에 반영

 

📌 query 같은 문자열보다 searchParams가 유용한 이유는 query의 경우 다음에 배우는 querystring 모듈을 한 번 더 사용해야 하기 때문

 

📌 querystring

- WHATWG방식의 url 대신 기존 노드의 url을 사용할 때, search 부분을 사용하기 쉽게 객체로 만드는 모듈

 querystring.parse(쿼리): url의 query 부분을 자바스크립트 객체로 분해

 querystring.stringify(객체): 분해된 query 객체를 문자열로 다시 조립

 

 

 

4️⃣ crypto

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

 

📌 단방향 암호화 알고리즘

- 비밀번호와 같이 복호화가 필요없는 경우

- 해시 기법을 주로 사용

- 해시 기법? 어떠한 문자열을 고정된 길이의 다른 문자열로 바꿔버리는 방식

 

 createHash(알고리즘): 사용할 해시 알고리즘을 넣음. md5, sha1, sha256, sha512 등이 가능하지만, md5와 sha1은 이미 취약점이 발견. 현재는 sha512 정도로 충분하지만, 나중에 sha512마저도 취약해지면 더 강화된 알고리즘으로 바꿔야 함.

 update(문자열): 변환할 문자열을 넣음.

 digest(인코딩): 인코딩할 알고리즘을 넣음. base64, hex, latin1이 주로 사용되는데, 그중 base64가 결과 문자열이 가장 짧아 애용. 결과물로 변환된 문자열을 반환.

 

📌 양방향 암호화 알고리즘

- 키를 통해 암호화된 문자열을 복호화 할 수 있음

 crypto.createCipheriv(알고리즘, 키, iv): 암호화 알고리즘과 키, iv를 넣습니다. 암호화 알고리즘은 aes-256-cbc를 사용했으며, 다른 알고리즘을 사용해도 됩니다. aes-256-cbc 알고리즘의 경우 키는 32바이트여야 하고, iv는 16바이트여야 합니다. iv는 암호화할 때 사용하는 초기화 벡터를 의미하지만, 이 책에서 설명하기에는 내용이 많으므로 AES 암호화에 대해 따로 공부하는 것이 좋습니다. 사용 가능한 알고리즘 목록은 crypto.getCiphers()를 호출하면 볼 수 있습니다.

 cipher.update(문자열, 인코딩, 출력 인코딩): 암호화할 대상과 대상의 인코딩, 출력 결과물의 인코딩을 넣습니다. 보통 문자열은 utf8 인코딩을, 암호는 base64를 많이 사용합니다.

 cipher.final(출력 인코딩): 출력 결과물의 인코딩을 넣으면 암호화가 완료됩니다.

 crypto.createDecipheriv(알고리즘, 키, iv): 복호화할 때 사용합니다. 암호화할 때 사용했던 알고리즘과 키, iv를 그대로 넣어야 합니다.

 decipher.update(문자열, 인코딩, 출력 인코딩): 암호화된 문장, 그 문장의 인코딩, 복호화할 인코딩을 넣습니다. createCipheriv update()에서 utf8, base64순으로 넣었다면 createDecipheriv update()에서는 base64, utf8순으로 넣으면 됩니다.

 decipher.final(출력 인코딩): 복호화 결과물의 인코딩을 넣습니다.

 

 

 

5️⃣ util

- 각종 편의 기능을 모아둔 모듈

 util.deprecate: 함수가 deprecated 처리되었음을 알립니다. 첫 번째 인수로 넣은 함수를 사용했을 때 경고 메시지가 출력됩니다. 두 번째 인수로 경고 메시지 내용을 넣으면 됩니다. 함수가 조만간 사라지거나 변경될 때 알려줄 수 있어 유용합니다.

 util.promisify: 콜백 패턴을 프로미스 패턴으로 바꿉니다. 바꿀 함수를 인수로 제공하면 됩니다. 이렇게 바꿔두면 async/await 패턴까지 사용할 수 있어 좋습니다. 3.5.5.1절의 randomBytes와 비교해보세요. 프로미스를 콜백으로 바꾸는 util.callbackify도 있지만 자주 사용되지는 않습니다.

 

 

 

6️⃣ worker_threads

- 노드에서 멀티 스레드 방식으로 작업하는 것이 가능하게 하는 모듈

worker_threads.js

const { 
  Worker, isMainThread, parentPort,
} = require('worker_threads'); 

if (isMainThread) { // 부모일 때 
  const 
  worker = new Worker(__filename); 
  worker.on('message', message => console.log('from worker', message)); 
  worker.on('exit', () => console.log('worker exit')); 
  worker.postMessage('ping');
} else { // 워커일 때 
  parentPort.on('message', (value) => { 
    console.log('from parent', value); 
    parentPort.postMessage('pong'); 
    parentPort.close(); 
  }); 
}

worker.postMessage : 워커에 데이터를 보냄

parentPort.on('message') 이벤트리스너 : 부모로부터 메시지를 받음

parentPost.postMessage('pong'): 부모에게 메시지를 보냄

worker.on('message') : 부모가 메시지를 받는 방법

parentPort.close() : 부모와의 연결 종료

→ 종료될때는 worker.on('exit')이 실행

$ node worker_threads
from parent ping
from worker pong
worker exit

 

📌 여러개의 worker thread에 데이터를 보내보기

// worker_data.js

const {
  Worker, isMainThread, parentPort, workerData,
} = require('worker_threads');

if (isMainThread) { // 부모일 때
  const threads = new Set();
  threads.add(new Worker(__filename, {
    workerData: { start: 1 },
  }));
  threads.add(new Worker(__filename, {
    workerData: { start: 2 },
  }));
  for (let worker of threads) {
    worker.on('message', message => console.log('from worker', message));
    worker.on('exit', () => {
      threads.delete(worker);
      if (threads.size === 0) {
        console.log('job done');
      }
    });
  }
} else { // 워커일 때
  const data = workerData;
  parentPort.postMessage(data.start + 100);
}

new Worker을 호출할 때, 두번째 인수의 workerData 속성으로 원하는 데이터를 보낼 수 있음

각 워커에서는 workerData로 부모로부터 데이터를 받음

위의 코드에서는 두개의 워커가 돌아가고 있으며, 각각 부모로부터 숫자를 받아서 100을 더해 돌려줌 → 돌려주는 순간 워커가 종료되어 worker.on('exit') 수행 두개의 워커 모두 종료되면 job done이 로깅

$ node worker_data
from worker 101
from worker 102
job done

 

 

7️⃣ child_process

- 노드에서 다른 프로그램을 실행하고 싶거나 명령어를 수행하고 싶을 때 사용하는 모듈

- 현재 노드 프로세스 외에 새로운 프로세스를 띄워서 명령을 수행하고, 노드 프로세스에 결과를 알려줌 (그래서 이름이 자식 프로세스 인 것!)

 

📌 exec

- 명령 프롬프트의 명령어 ls를 노드를 통해 실행해보자! (윈도우의 경우 dir)

const exec = require('child_process').exec;

var process = exec('dir');

process.stdout.on('data', function(data) {
  console.log(data.toString());
}); // 실행 결과

process.stderr.on('data', function(data) {
  console.error(data.toString());
}); // 실행 에러

결과

- stdout(표준출력)과 stderr(표준에러)에 붙여둔 data 이벤트 리스너에 버퍼 형태로 전달

- 현재 폴더의 파일 목록이 표시됨.

 

📌 spawn

- 파이썬 코드를 실행해보자

- spawn의 첫번째 인수 : 명령어

- spawn의 두번째 인수 : 옵션 배열

const spawn = require('child_process').spawn;

var process = spawn('python', ['test.py']);

process.stdout.on('data', function(data) {
  console.log(data.toString());
}); // 실행 결과

process.stderr.on('data', function(data) {
  console.error(data.toString());
}); // 실행 >에러

 

exec 와 spawn의 차이점

- exec : 셸을 실행해서 명령어를 수행

- spawn : 새로운 프로세스를 띄우면서 명령어를 실행

 

 


Reference

- Node.js 교과서 개정 2판