상세 컨텐츠

본문 제목

[node.js] 기본모듈

Server/node.js

by 클리엘 클리엘 2021. 3. 3. 13:55

본문

728x90

1. process

 

process를 사용하면 현재 node.js버전과 같은 정보나 시스템 정보등을 확인할 수 있습니다.

 

예제에서 version은 현재 node.js의 버전을, arch는 CPU 아키텍쳐, platform은 운영체제 종류, pid는 프로세스 ID, execPath는 node.js의 경로, uptime()은 실행시간, cwd()는 프로세스 실행위치, cpuUsage는 CPU사용상태등을 나타냅니다.

console.log(process.execPath);
console.log(process.cpuUsage());
console.log(process.cwd());

또한 process.env를 통해 시스템의 환경변수값을 확인할 수 있고 전역 변수처럼 특정 값을 지정한뒤 꺼내올 수도 있습니다.

process.env.SITEURL = 'http://cliel.com/';

console.log(process.env.SITEURL);

변수이외에 함수의 경우 이벤트 루프에서 특정 함수를 최우선적으로 실행하도록 우선권을 부여하는 것도 가능합니다. 이 실행은 setImmediate()나 setTimeout()같은 다른 콜백함수보다 더 우선하여 실행됩니다.

process.nextTick(() => { console.log('1 st') });

마지막으로 node.js 스스로를 종료하고자 한다면 exit() 함수를 호출할 수 있습니다. 참고로 exit() 함수에 지정하는 숫자 0은 정상종료를 의미하며 0이 아닌 다른 숫자는 오류가 발생했다는 것을 의미합니다.


2. OS

 

OS에서는 운영체제정보나 CPU, 메모리와 같은 정보를 확인할 수 있습니다.

const os = require('os');

console.log(os.hostname());
console.log(os.platform());
console.log(os.uptime());
console.log(os.release());

console.log(os.cpus());

console.log(os.freemem());


3. path

 

path를 이용하면 디렉토리와 파일에 대한 다양한 경로를 확인할 수 있습니다.

const path = require('path');

console.log(path.dirname(__filename));
console.log(path.extname(__filename));

const _path = path.dirname(__filename);

console.log(path.isAbsolute(_path));
console.log(path.join('abc', 'def'));
console.log(path.resolve('abc', 'def'));

참고로 join은 상태경로로 매개변수로 전달한 경로를 합치고 resolve는 절대경로로 경로를 반환합니다.


4. url

 

node.js에서 url을 처리하는 방식은 node방식과 WHATWG방식 2가지로 나뉘게 됩니다. 이 2개의 차이는 아래 주소에서 확인하시면 됩니다.

 

URL | Node.js v15.10.0 Documentation

 

URL | Node.js v15.10.0 Documentation

URL# Source Code: lib/url.js The url module provides utilities for URL resolution and parsing. It can be accessed using: const url = require('url'); URL strings and URL objects# A URL string is a structured string containing multiple meaningful components.

nodejs.org

2개의 방식을 이해할때는 장점과 단점이 아닌 서로 다른 형태로서 이해하고 필요할때마다 2개의 방식중 하나를 선택해 사용합니다.

 

URL Node.js v15.10.0 Documentation

const url = require('url');

const nodeUrl = url.parse('http://www.cliel.com/abc/def?uid=aaa&pwd=bbb');
console.log(nodeUrl);
console.log(url.format(nodeUrl));

const whatwgUrl = new URL('http://www.cliel.com/abc/def?uid=aaa&pwd=bbb');
console.log(whatwgUrl);

예제에서 parse를 통해 주소를 얻는 방식이 node방식, URL생성자를 통해 url을 얻는 방식이 WHATWG방식입니다. 2개를 비교해 보면 일부 차이점을 확인할 수 있습니다.

 

parse()는 주소를 분해하고 format()은 주소를 다시 조립할 수 있습니다.

node방식에서 GET방식으로 전달되어 오는 데이터(Query String)를 확인하려면 querystring모듈을 사용합니다.

const query = querystring.parse(nodeUrl.query);

console.log(query);
console.log(query.uid);
console.log(querystring.stringify(query));

parse함수는 데이터를 객체로 만들어 반환하므로 해당 객체에서 key로 속성을 지정해 해당 키의 값을 알아낼 수 있습니다. stringify()함수는 데이터를 단순 문자열로서 반환합니다.

 

WHATWG방식에서는 searchParams객체를 통해 GET방식으로 전달되어 오는 데이터(QueryString)를 편리하게 확인할 수 있습니다.

const whatwgUrl = new URL('http://www.cliel.com/abc/def?uid=aaa&pwd=bbb');
console.log(whatwgUrl);

console.log(whatwgUrl.searchParams);
console.log(whatwgUrl.searchParams.get('uid'));

console.log(whatwgUrl.searchParams.keys());

get() 함수를 통해 특정한 key의 value를 확인할 수 있고 has()함수를 사용하면 해당 key가 존재하는지의 여부를 알 수 있습니다. 또한 keys()로 전체키를, values()로 전체 값만을 나열할 수도 있습니다.

const whatwgUrl = new URL('http://www.cliel.com/abc/def?uid=aaa&pwd=bbb');
console.log(whatwgUrl);

whatwgUrl.searchParams.set('name', 'kim');
whatwgUrl.searchParams.append('uid', 'bbb');

console.log(whatwgUrl);

whatwgUrl.searchParams.delete('name');

console.log(whatwgUrl.searchParams.toString());

set과 append함수는 필요한 키와 값을 추가합니다. 다만 append는 중복되는 키가 있으면 더 추가하는 것이 다릅니다.

또한 특정 키가 필요하지 않다면 delete를 통해 삭제할 수 있습니다.

 


5. crypto

 

우선 cypto를 사용해 암호화를 하는데는 단방향암호화와 양방향암호화 2가지가 있습니다.

 

단방향암호화는 randomBytes함수로 지정한 바이트만큼의 문자열을 생성하고 이 문자열을 기초로 pbkdf2함수를 통해 해시를 수행한 결과를 받는것으로 구현할 수 있습니다.

const crypto = require('crypto');

const buf = crypto.randomBytes(128);
const p = crypto.pbkdf2Sync('abcd1234', buf, 100000, 128, 'sha512');

console.log(p);

양방향에서는 알고리즘과 암호화키, 그리고 벡터값을 지정한 뒤 암호화의 경우 createCipheriv()함수를 사용해 암호화를 수행하며 복호화는 createDecipheriv()함수로 복호화를 수행합니다. 벡터값은 암호화할때마다 동일한 암호화결과가 나오지 않도록 하는 역활을 합니다.

const crypto = require('crypto');

const algorithm = 'aes-256-cbc';
const key = '12345678901234567890123456789012';
const iv = '1234567890123456';

const cipher = crypto.createCipheriv(algorithm, key, iv);
const pup = cipher.update('cliel', 'utf8', 'base64');
const pfl = cipher.final('base64');
console.log(pup + pfl);

const decipher = crypto.createDecipheriv(algorithm, key, iv);
const dup = decipher.update(pup + pfl, 'base64', 'utf8');
const dfl = decipher.final('utf8');
console.log(dup + dfl);

6. util

 

util은 여러가지 편리하게 사용할 수 있는 공통함수를 모아놓은 모듈인데 대략 2가지 정도를 알아보고자 합니다.

const util = require('util');

function warning(i, j)
{
    return i + j; // 본래 실행되어야할 함수나 필요한 처리를 실행
}

const mysum = util.deprecate(warning, '함수 종료 예정 - 사용하지 말것');

const result = mysum(10, 20);

console.log(result);

deprecate는 특정한 함수 사용시 사용자에게 경고를 표시할 수 있도록 합니다.

 

다른 하나는 promisify인데 콜백 함수를 promise로 바꿔주는 역활을 합니다.

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

const randomeBytesPromise = util.promisify(crypto.randomBytes);
randomeBytesPromise(128)
.then((buf) => {
    const p = crypto.pbkdf2Sync('abcd1234', buf, 100000, 128, 'sha512');
    console.log(p);
})
.catch((error) => {
    console.log(error);
});

위 에제는 암호화를 위해 사용한 raandomBytes함수를 promise로 바꾼것입니다. promise로 변환되었기에 promise에서 사용하던 then과 catch를 그대로 구현하고 있습니다.

 

다만 모든 메서드를 promise로 바꿀 수 있는것은 아니고 콜백함수의 매개변수 방식이 (err, data) 형식을 갖추어야 합니다. 대부분의 콜백함수가 (err, data)형태를 취하고 있습니다.


7. worker_threads

 

worker_threads는 node.js에서 멀티스레드를 구현하기 위해 마련된 것입니다.

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

if (isMainThread) {
    const worker = new Worker(__filename);

    worker.on('message', message => console.log(message));
    worker.on('exit', () => console.log('exit'));

    worker.postMessage('data1');
}
else {
    parentPort.on('message', (value) => {
        console.log(value);

        parentPort.postMessage('data2');
        parentPort.close();
    });
}

이와 같은 작업을 위해서는 당연히 worker_threads모듈을 가져와야 하며 기본적으로 worker와 isMainThread, parentPort를 가져올 수 있습니다.

 

isMainThread는 현재 실행이 메인스레드인지 별도로 생성한 작업스레드인지를 구분하는 것이며 메인스레드인지 작업스레드인지에 따라 처리내용을 달리 해주면 됩니다.

 

작업스레드는 new Worker를 통해서 생성되며 postMessage를 통해 메인스레드에서 작업스레드로 데이터를 보낼 수 있습니다. 작업스레드또한 portMessage를 통해 데이터를 메인스레드로 보낼 수 있습니다. 이런식으로 스레드간 서로건의 메세지 전송을 가능하게 합니다.

 

서로 보낸 메세지는 on('message')를 통해 받을 수 있습니다.

 

경우에 따라 스레드를 여러개 생성할 수도 있는데

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

if (isMainThread) {
    const threads = new Set();

    for (var i = 0; i <= 2; i++) {
        threads.add(new Worker(__filename, { workerData: { number: i } }));
    }

    for (var worker of threads) {
        worker.on('exit', () => {
            threads.delete(worker);

            if (threads.length == 0) {
                console.log('all exit');
            }
        });

        worker.on('message', message => {
            console.log(message);
        });
    }
}
else {
    parentPort.postMessage(workerData.number + 100);
}

스레드의 관리를 위해 threads배열을 만든다음 생성하고자 하는 작업만큼 스레드를 생성해 주면 됩니다. 그리고 for문을 통해 threads에서 각각의 스레드를 순회하면서 전달되어 오는 메세지와 스레드종료여부를 확인해 처리합니다.

 

데이터를 보내는 경우에도 workerData를 통해서 postMessage가 아닌 new Worker생성자를 통해서 필요한 객체를 전달할 수 있습니다.

 

실행순서에 주목해 주세요. 스레드는 순서대로 생성할 수 있지만 실행완료 시점은 보장할 수 없습니다. 참고로 에제에는 없지만 오류가 나는 경우를 대비해 work.on('error') 처리를 해주는 것이 좋습니다.

for (var worker of threads) {
    worker.on('error', (error) => {
        throw error;
    });

    worker.on('exit', () => {
        threads.delete(worker);

        if (threads.length == 0) {
            console.log('all exit');
        }
    });

    worker.on('message', message => {
        console.log(message);
    });
}

참고로 node.js에서는 최대 4개의 스레드만을 동시에 실행할 수 있도록 되어 있습니다. 만약 이 설정을 바꾸려면 윈도우에서는 'SET UV_THREADPOOL_SIZE=숫자'헝식의 명령을 통해서, 리눅스와 맥은 REPL에서 'UV_THREADPOOL_SIZE=숫자'로 생성가능한 스레드 수를 설정할 수 있습니다.


8. child_process

 

child_process를 이용하면 시스템의 명령어나 외부 프로그램을 호출해 실행을 넘길 수 있습니다.

const { exec } = require('child_process');

var process = exec('ls -al');

process.stdout.on('data', data => {
    console.log(data);
});

child_process에서 exec를 가져와 리눅스의 외부 명령어인 'ls -al'을 실행하고 있습니다. 실행의 결과는 stdout으로 가져옵니다. 또한 오류가 나는 경우를 대비해 stderr을 추가할 수도 있습니다.

const { spawn } = require('child_process');

var process = spawn('./sample', ['param']);

process.stdout.on('data', data => {
    console.log(data.toString());
});

외부 프로그램 실행시 특정 매개변수가 필요한 경우에는 spawn을 사용합니다. 참고로 stdout을 통해 받은 내용을 출력하는 경우에는 toString()을 줘야 정상적으로 호출될 수 있습니다.

 

 

728x90

'Server > node.js' 카테고리의 다른 글

[node.js] 설치 (Windows WSL2)  (0) 2021.03.03
[node.js] fs (파일시스템 다루기)  (0) 2021.03.03
[node.js] 기본모듈  (0) 2021.03.03
[node.js] 전역객체  (0) 2021.03.02
[node.js] 모듈(Module)  (0) 2021.03.02
[node.js] REPL  (0) 2021.03.02

관련글 더보기

댓글 영역