REPL
자바스크립트는 스크립트 언어이므로 미리 컴파일을 하지 않아도 즉석에서 코드를 실행할 수 있다.
코드를 읽고(Read), 해석하고(Eval), 결과물을 반환하고(Print), 종료할 때까지 반복한다(Loop)고 해서 REPL(Read Eval Print Loop)이라고 부른다.
하지만 REPL로 원하는 결과를 만들어내기 쉽지 않을 것이다.
그래서 자바스크립트 파일을 만들어 사용한다.
노드는 코드를 모듈로도 만들 수 있다. 모듈은 자체로도 하나의 프로그램이면서 다른 프로그램의 부품으로도 사용할 수 있다. 간단한 모듈을 하나 만들어보자.
const odd = '홀수';
const even = '짝수';
module.exports = {
odd,
even,
};
var.js
var.js에 변수 두 개를 선언했다. 그리고 module.exports에 변수들을 담은 객체를 대입했다. 이제 이 파일은 모듈로서 기능한다. 이를 다른 파일에서 사용해보자.
const { odd, even } = require('./var');
function checkOddEven(num){
if (num % 2){
return odd;
}else{
return even;
}
}
module.exports = checkOddEven;
func.js
require 함수 안에 불러올 모듈의 경로를 적으면 모듈을 불러올 수 있다.
이 두 모듈을 사용하는 파일을 하나 만들어보자.
const { odd, even } =require('./var');
const checkNumber =require('./func');
function checkStringOddEven(str){
if (str.length % 2){
return odd;
}else{
return even;
}
}
console.log(checkNumber(10));
console.log(checkStringOddEven('hello'));
index.js
위의 모듈을 참조하였다. 한번 실행해보자.
이렇게 여러 파일에 걸쳐 재사용되는 함수나 변수를 모듈로 만들어두면 편리하다.
노드 내장 객체
노드에서는 기본적인 내장 객체와 내장 모듈을 제공한다.
global
전역 객체이며 모든 파일에서 접근할 수 있다. 또한 global은 생략할 수 있다.
console, require 또한 global이 생략된 메서드이다.
타이머
타이머 기능을 제공하는 함수인 setTimeout, setInterval, setImmediate는 노드에서 window 대신 global 객체 안에 들어 있다.
- setTimeout(콜백, 밀리초): 주어진 밀리초 이후 콜백 함수 실행
- setInterval(콜백, 밀리초): 주어진 밀리초마다 콜백 함수를 반복 실행
- setImmediate(콜백): 콜백 함수를 즉시 실행
- clearTimeout(아이디): setTimeout을 취소
- clearInterval(아이디): setInterval을 취소
- clearImmediate(아이디): setImmediate를 취소
__filename, __dirname
노드에서는 파일 사이에 모듈 관계가 있는 경우가 많으므로 현재 파일의 경로나 파일명을 알아야 할 때가 있다.
노드는 __filename, __dirname이라는 키워드로 경로에 대한 정보를 제공한다.
module, exports, require
module.exports로만 모듈을 내보낼 수 있지만 exports 객체로도 모듈을 만들 수 있다.
exports 객체와 module.eports가 사실은 같은 객체를 참조한다. exports 객체 사용 시에는 module.exports와의 참조 관계가 깨지지 않도록 주의해야 한다. module.exports에는 어떤 값이든 대입해도 되지만, exports에는 반드시 객체처럼 속성명과 속성 값을 대입해야 한다. 다른 값을 대입하면 객체의 참조 관계가 끊겨 더 이상 모듈로 기능을 하지 않는다.
최상위 스코프에 존재하는 this는 module.exports를 가리킨다. 또한, 함수 선언문 내부의 this는 global 객체를 가리킨다.
require가 반드시 파일 최상단에 위치할 필요는 없다.
require.cache 객체에는 파일 이름이 속성명으로 들어 있다. 한 번 require한 파일은 require.cache에 저장된다.
다음번에 require 할 때는 새로 불러오지 않고 재사용한다.
여기서 여러 모듈이 서로를 순환적으로 참조하는 순환 참조는 피하는 것이 좋다.
노드 내장 모듈
OS
웹 브라우저에 사용되는 자바스크립트는 운영체제의 정보를 가져올 수 없지만 노드는 os 모듈에 정보가 담겨 있어 정보를 가져올 수 있다.
path
path 모듈을 이용하여 폴더와 파일의 경로를 쉽게 조작할 수 있다.
url
인터넷 주소를 쉽게 조작하도록 도와주는 모듈이다. url 처리에는 크게 두 가지 방식이 있다.
노드 버전 7에서 추가돤 WHATWG방식의 url과 예전부터 사용하던 방식이 있다.
위에가 WHATWG, 아래가 기존 노드 방식의 주소 체계이다.
WHATWG에만 username, password, origin, searchParams 속성이 있다.
기존 노드 방식에서는 두 메서드를 주로 사용한다.
- url.parse(주소): 주소를 분해한다. WHATWG 방식과 비교하면 username과 password 대신 auth 속성이 있고 searchParams 대신 query가 있다.
- url.format(객체): WHATWG 방식 url과 기존 노드의 url을 모두 사용할 수 있다. 분해되었던 url 객체를 다시 원래 상태로 조립한다.
두 방식은 취향으로 선택해서 사용해도 되지만 노드 url형식을 꼭 사용해야 하는 경우가 있다.
host 부분 없이 pathname 부분만 오는 주소인 경우에는 WHATWG 방식이 처리할 수 없다.
WHATWG 방식은 search 부분을 searchParams라는 특수한 객체로 반환하므로 유용하다. search 부분은 보통 주소를 통해 데이터를 전달할 때 사용된다. search는 물음표(?)로 시작하고, 그 뒤에 키=값 형식으로 데이터를 전달한다.
querystring
WHATWG 방식의 url 대신 기존 노드의 url을 사용할 때, search 부분을 사용하기 쉽게 객체로 만드는 모듈이다.
const url = require('url');
const querystring = require('querystring');
const parsedUrl = url.parse('http://www.github.co.kr/?page=3&limit=10&category=nodejs&category=javascript');
const query = querystring.parse(parsedUrl.query);
console.log('querystring.parse(): ', query);
console.log('querystring.parse(): ', querystring.stringify(query));
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'));
- createHash(알고리즘): 사용할 해시 알고리즘을 넣고 해시함수를 적용
- update(문자열): 변환할 문자열을 넣는다.
- digset(인코딩): 인코딩할 알고리즘을 넣는다.
가끔 충돌이 발생할 수도 있다. 이럴 때는 입력이 달라도 결과가 같기 때문에 다른 입력이 있더라도 접근 권한을 얻을 수 있다. 현재는 주로 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('pwd: ', key.toString('base64'));
});
});
randomBytes로 매번 다른 난수를 생성하여 salt로 적용시키므로 매번 다른 결과가 나온다. 따라서 salt를 잘 보관해야 한다.
양방향 암호화
양방향 암호화는 암호화, 복호화 모두 가능한 암호화 방식을 말한다. 이때 대칭형 암호화라면 같은 키를 사용한다.
const crypto = require('crypto');
const algorithm = 'aes-256-cbc';
const key = 'abcdefghijklmnopqrstuvwxyz123456';
const iv = '1234567890123456';
const cipher = crypto.createCipheriv(algorithm, key, iv);
let result = cipher.update('암호화할 문장', 'urf8', 'base64');
result += cipher.final('base64');
console.log('암호화: ', result);
const decipher = crypto.createDecipheriv(algorithm, key, iv);
let result2 = decipher.update(result, 'base64', 'utf8');
result2 += decipher.final('utf8');
console.log('복호화: ',result2);
- createCipheriv(알고리즘, 키, iv): 암호화 알고리즘, 키, iv를 넣는다. iv는 암호화할 때 사용하는 초기화 벡터이다.
- update(문자열, 인코딩, 출력 인코딩): 암호화를 하는 함수
- final(출력 인코딩): 출력 결과물의 인코딩을 넣으면 암호화가 완료된다.
util
util이라는 이름처럼 각종 편의 기능을 모아둔 모듈이다.
const util = require('util');
const crypto = require('crypto');
const dontUseMe = util.deprecate((x, y) => {
console.log(x + y);
},'deprecated!! do not use!!');
dontUseMe(1, 2);
const randomBytesPromise = util.promisify(crypto.randomBytes);
randomBytesPromise(64)
.then((buf) => {
console.log(buf.toString('base64'));
})
.catch((err) => {
console.error(err);
});
- deprecate: 함수가 deprecated 처리되었음을 알린다.
- promisify: 콜백 패턴을 프로미드 패턴으로 바꾼다.
worker_threads
노드에서 멀티 스레드 방식으로 작업하는 방법이다,
const { worker } = require('cluster');
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를 만들어 메시지를 전달한 뒤 종료한다.
그렇지 않으면 message를 전달받고 출력한다.
child_process
노드에서 다른 프로그램을 실행하고 싶거나 명령어를 수행하고 싶을 때 사용하는 모듈이다.
const exec = require('child_process').exec;
const process = exec('dir');
process.stdout.on('data', function(data) {
console.log(data.toString());
});
process.stderr.on('data', function(data){
console.error(data.toString());
});
현재 폴더의 파일 목록이 표시될 것이다.
const spawn = require('child_process').spawn;
const 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());
});
spawn을 통해 파이썬 코드를 실행할 수도 있다.