새소식

인기 검색어

Language/Javascript

[Javascript] Node js 기초 3장

  • -
반응형

REPL 사용하기

javascript는 스크립트 언어이므로 미리 컴파일하지 않아도 바로 실행이 가능하다.

노드의 콘솔을 REPL이라고 부르는데 R(read) E(eval) P(print) L(loop)라서 REPL이라고 부른다.

node를 입력하면 js 코드를 입력할 수 있는 창이 나온다.

그리고 REPL은 js 파일도 실행시킬 수 있다.

function add(x, y) {
    return x + y;
}

const result = add(1, 2);

console.log(result);

js 파일을 만들어서 코드를 작성하고 node ${filename}을 입력하면 해당 파일의 결과가 출력된다.

Module로 만들기

노드는 코드를 모듈로 만들 수 있다. 모듈이란 특정한 기능을 하는 함수나 변수들의 집합을 의미한다. 예를 들어 수학과 관련된 코드들만 모아서 하나의 모듈을 만들 수 있다. 모듈은 자체로도 하나의 프로그램이면서 다른 프로그램의 부품으로도 사용할 수 있다.

모듈로 만들어두면 여러 프로그램에 해당 모듈을 재사용할 수 있다.

보통 파일 하나가 모듈 하나가 되는데, 파일별로 코드를 모듈화 할 수 있어 관리하기 편하다.

그러면 한 번 모듈을 만들어 보자.

우리는 숫자와 문자열 길이의 홀짝을 구분하는 코드를 작성해보자.

// var.js
const odd = "odd number";
const even = "even number";

module.exports = {
   odd, even;
}

var.js에 변수 두 개를 선언했다. 그리고 module.exports에 변수들을 담은 객체를 대입했다. 그러면 이 파일은 이제 모듈이며 변수들을 모아둔 모듈이 된다.

// func.js

const {odd, even} = require("./var");

function checkOddorEven(num) {
    if(num%2) {
        return odd;
    }
    return even;
}

module.exports = checkOddorEven;

require 함수 안에 불러올 모듈의 경로를 적는다.

var.js에서 변수를 불러온 뒤 숫자의 홀짝을 판별하는 함수를 선언했다. 

그리고 다시 module.exports에 함수를 대입했다.

// index.js

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

function checkStringOddOrEven(str) {
    if(str.length % 2) {
        return odd;
    }
    return even;
}

console.log(checkNumber(10));
console.log(checkStringOddOrEven("hello world"));

마지막으로 index.js를 작성했다.

node index의 결과가 출력되었다.

노드 내장 객체

노드에서는 기본적인 내장 객체와 내장 모듈을 제공한다.

global

global 객체는 브라우저의 window와 같은 전역 객체이다. 전역 객체이므로 모든 파일에서 접근할 수 있고, 생략도 할 수 있다.

process

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

process.env

process.env는 환경 변수를 출력한다.

시스템 환경 변수는 노드에 직접 영향을 미치기도 한다. 대표적으로 UV_THREADPOOL_SIZE와 NODE_OPTIONS이 있다.

NODE_OPTIONS은 노드를 실행할 때의 옵션들을 입력받는 환경변수이다.

NODE_OPTIONS = --max-old-space-size=8192에서 해당 값은 노드의 메모리를 8GB까지 사용할 수 있게 한다.

UV_THREADPOOL_SIZE는 노드에서 기본적으로 사용하는 스레드풀의 스레드 개수를 조절할 수 있게 한다.

시스템 환경 변수 외에도 임의로 환경 변수를 저장할 수 있는데, process.env는 서비스의 중요한 키를 저장하는 공간으로도 사용된다.

따라서 중요한 비밀번호는 다음과 같이 process.env 속성으로 대체한다.

const secretId = process.env.SECRET_ID;
const secretCode = process.env.SECRET_CODE;

process.nextTick(콜백)

이벤트 루프가 다른 콜백 함수들보다 nextTict의 콜백 함수를 우선으로 처리하도록 만들었다.

setImmediate(() => {
    console.log('immediate');
});

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

setTimeout(() => {
    console.log('timeout');
}, 0);
Promise.resolve().then(() => console.log('promise'));

// output
/*
nextTick
promise
timeout
immediate
*/

process.nextTick은 setImmediate나 setTimeout보다 먼저 실행된다.

코드 맨 밑에 Promise를 넣은 것은 resolve도 nextTick처럼 다른 콜백보다 우선시되기 때문이다. 그래서 process.nextTick과 Promise를 마이크로태스크라고 따로 구분 지어 부른다.

노드 내장 모듈

OS

노드는 os 모듈에 정보가 담겨 있어 정보를 가져올 수 있다.

const os = require('os');

console.log('---------------OS information---------------');
console.log('os.arch(): ', os.arch());
console.log('os.platform(): ', os.platform());
console.log('os.type(): ', os.type());
console.log('os.uptime(): ', os.uptime());
console.log('os.hostname(): ', os.hostname());
console.log('os.release(): ', os.release());

console.log('---------------Path---------------');
console.log('os.homedir(): ', os.homedir());
console.log('os.tmpdir(): ', os.tmpdir());

console.log('---------------CPU information---------------')
console.log('os.cpus(): ', os.cpus());
console.log('os.cpus().length: ', os.cpus().length);

console.log('---------------memory information---------------');
console.log('os.freemem(): ', os.freemem());
console.log('os.totalmem(): ', os.totalmem());

PATH

폴더와 파일의 경로를 쉽게 조작도록 도와주는 모듈이다.

const path = require('path');

console.log('path.sep:', path.sep);
console.log('path.delimiter:', path.delimiter);
console.log('-------------------------------------');
console.log('path.dirname():', path.dirname(__filename));
console.log('path.extname():', path.extname(__filename));
console.log('path.basename():', path.basename(__filename));
console.log('path.basename - extname:', path.basename(__filename, path.extname(__filename)));
console.log('-------------------------------------');
console.log('path.parse():', path.parse(__filename));
console.log('path.format():', path.format({
    dir: 'C:\\users\\rhenus',
    name: 'path',
    ext: '.js',
}));
console.log('path.normalize():', path.normalize('C:\\users\\rhenus\\path.js'));
console.log('-------------------------------------');
console.log('path.isAbsolute(C:\\):', path.isAbsolute('C:\\'));
console.log('path.isAbsolute(./home):', path.isAbsolute('./home'));
console.log('path.relative():', path.relative('C:\\users\\rhenus\\path.js', 'C:\\'));
console.log('path.join():', path.join(__dirname, '..', '..', '/users', '.', '/rhenus'));
console.log('path.resolve():', path.resolve(__dirname, '..', 'users', '.', '/rhenus'));

URL

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

url 처리에는 크게 두 가지 방식이 있다.

기존 URL방식과 WHATWG방식이 있다. 

url 모듈은 주로 parse와 format 메서드를 사용한다.

url.parse(주소)

주소를 분해한다. WHATWG 방식과 비교하면 username과 password 대신 auth 속성이 있고 searchParams 대신 query가 있다.

url.format(객체)

WHATWG 방식 URL과 기존 노드의 URL을 모두 사용할 수 있다.

crypto

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

비밀번호는 보통 단방향 암호화 알고리즘을 사용해서 암호화한다. 단방향 암호화 알고리즘은 주로 해시 기법을 사용한다.

const crypto = require('crypto');

const plain = 'rhenus';
console.log('base64:', crypto.createHash('sha512').update(plain).digest('base64'));
console.log('hex:', crypto.createHash('sha512').update(plain).digest('hex'));
  • createHash(alg): 사용할 해시 알고리즘을 입력한다. md5, sha1, sha256, sha512 등 다양하다.
  • update(string): 변환할 문자열을 입력한다.
  • digest(encoding): 인코딩할 알고리즘을 입력한다. base64, hex, latin1이 주로 사용된다.

기존 해시 알고리즘이 취약해지자 현재는 주로 pbkdf2나 bcrypt, scrpyt라는 알고리즘으로 비밀번호를 암호화하고 있다.

pbkdf2

pbkd2f 알고리즘은 비밀번호에 salt라고 불리는 문자열을 붙인 후 해시 알고리즘을 반복해서 적용하는 것이다.

const crypto = require('crypto');

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

/*
salt: pJD7Dm53XwmnoYjZ5Z01iWNrVNGRjzsM+CKnVJndiZ1+GB7r5RGRKj8QqthZrQxUXHv6Lp4nCW9+IQhJyc920g==
password: VyIJIXPBQD5J+qqnXU4h/IC7Cyqda4NKqeK4b83dGCV1ikjoWAPl/SkohP5Piik11rcY0WqHbDAvnQhnpDeJ0Q==
*/

randomBytes 메서드로 64byte 길이의 문자열을 만든다. 이것이 salt가 된다.

pbkdf2 메서드는 비밀번호, salt, 반복 횟수, 출력 바이트, 해시 알고리즘을 입력한다. 즉 위 코드는 password라는 문자열에 salt를 붙여서 sha512 해시를 100000번 반복한 후 64byte 출력하는 것을 의미한다.

bcrypt

bcrypt는 레인보우 테이블 공격 방지를 위해 salt를 통합한 적응형 함수이다.

bcrypt.hashpw(password, bcrypt.gensalt())

scrypt

2009년에 발표된 키 파생 기능 함수이다.

hashlib.scrypt(password, *, salt, n, r, p, maxmem=0, dklen=64)

 

암호화 방식은 양뱡향 대칭형 암호화도 사용한다.

암호화된 문자열을 복호화할 수 있으며, 키를 사용한다.

const crypto = require('crypto');

const algorithm = 'aes-256-cbc';
const key = crypto.randomBytes(32);
const iv = crypto.randomBytes(16);

console.log('key:', key.toString('base64'));
console.log('iv:', iv.toString('base64'));

const cipher = crypto.createCipheriv(algorithm, key, iv);
let result = cipher.update('password', 'utf8', 'base64');
result += cipher.final('base64');
console.log('encrypt:', result);

const decipher = crypto.createDecipheriv(algorithm, key, iv);
let result2 = decipher.update(result, 'base64', 'utf8');
result2 += decipher.final('utf8');
console.log('decrypt:', result2);

/*
key: 6Ey8XFkdfaEoQYjcKwQr+ffBZBB3q3/0cahAk5b+mJs=
iv: U+ChiZu7AMbWQqO6WUlG/A==
encrypt: bcb7Ew/dP1VK3OPOVTua1Q==
decrypt: password
*/
  • crypto.createCipheriv(algorithm, key, iv): 암호화 알고리즘과 키, iv를 입력한다. 알고리즘은 aes-256-cbc를 사용했다. aes-256-cbc 같은 경우는 key는 32byte, iv는 16byte여야 한다.
  • cipher.update(string, encoding, output encoding): 암호화할 대상과 대상의 인코딩, 출력 결과의 인코딩을 입력한다.
  • cipher.final(output encoding): 출력된 문자열의 인코딩을 입력하면 암호화가 된다.
  • crypto.createDecipheriv(algorithm, key, iv): 복호화할 때 사용한다.
  • dicipher.update(string, encoding, output encoding): 암호화된 문장과 그 문장의 인코딩, 출력 문자열의 인코딩을 입력한다.
  • decipher.final(output encoding): 복호화 문자열의 인코딩을 입력한다.

 

반응형

'Language > Javascript' 카테고리의 다른 글

[Javascript] Node js 기초 2장  (0) 2023.08.22
[Javascrpit] Node js 기초 1장  (0) 2023.08.20
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.