
(Read, Evaluate, Print, Loop)


// var.js
const odd = '홀수';
const even = '짝수'
module.exports = {
odd,
even,
};
// func.js
const value = require('./var'); //require : node에서 제공
console.log(value); // 출력 : { odd: '홀수', even: '짝수' }
func.js | index.js |
|---|
// var.js
const odd = '홀수';
const even = '짝수'
export default { odd, even }; // export default 사용
//func.js
import { odd, even } from './var'; // import, from 사용
function checkStringOddOrEven(number) {
if (number % 2) {
return odd;
} else {
return even;
}
}
export default checkStringOddOrEven;

const odd = '홀수';
const even = '짝수'
exports.odd = odd;
exports.even = even;


console.log(this);
function a() {
console.log(this === global);
}
a();
이 코드를 실행하면 다음과 같이 출력된다.
console.log(this === module.exports); // 고침
function a() {
console.log(this === global);
}
a();
전역 this는 module.exports와 같다.
변수를 export할 때 this를 사용할 수도 있다(잘 쓰이진 않음)
const odd = '홀수';
const even = '짝수';
this.odd = odd;
this.even = even;
require('./var');
이 방식으로 var파일을 가져온 후 console.log로 실행하면 var파일 안에 어떤 것이 내장되어 있는지 확인 가능하다
(cache는 최초로 var파일을 불러온 값을 저장한 후, var를 다시 읽을 때 새로 파일을 불러올 필요 없이 cache에 저장된 값을 불러온다)
(exports는 module.exports이다
파일 두개가 서로를 참조하는 현상
-> 노드가 알아서 빈 객체로 바꿔줌
: 공식적인 자바스크립트 모듈 형식(ES모듈)
사용 형식은 다음과 같다.
mjs(ECMAScript)

cjs(CommonJS)

mjs에서는 cjs와 다르게 변수 선언 바로 앞에 export를 작성해 줄 수 있다
import는 다음과 같다.
import { odd, even } from './var.mjs';
function checkOddOrEven(num) {
if (num % 2) {
return odd;
}
return even;
}
export default checkOddOrEven; // 위에서 선언한 모듈을 export한다
import checkNumber from './func.mjs';
...
console.log(checkNumber(10)); // 사용은 일반 function처럼 사용하면 된다
// var.mjs
export const odd = 'MJS 홀수입니다';
export const even = 'MJS 짝수입니다';
// func.mjs
import { odd, even } from './var.mjs';
// 이 파일에서 odd, even으로 이름이 정해짐
// var.cjs
function checkOddOrEven(num) {
if (num % 2) {
return odd;
}
return even;
}
export default checkOddOrEven;
// func.cjs
import checkNumber from './func.mjs';
...
console.log(checkNumber(10));
// var.cjs에서 checkOddOrEven라 지었어도 func.cjs에서 checkNumber이라 부를수 있음
node index.mjs
mjs 확장자 대신 js확장자를 사용 가능
-> package.json에 type: "module" 속성 추가
ECMAScript 모듈은 CommonJS 모듈과 다르게 js, mjs 확장자를 생략할 수 x
폴더 내부의 index.js도 생략x

(CommonJS모듈은 가능, ES모듈은 불가)
: if문 안에 require를 넣는것
// dynamic.js
const a = false;
if(a) {
require('./func');
}
console.log('성공');
실행하면 콘솔에 '성공'이라고 출력된다.
// dynamic.mjs
if(a) {
import './func.mjs'; // if문 안에서 import는 실행되지 않음
}
// dynamic.mjs
if(a) {
const m1 = await import ('./func.mjs');
// import는 Promise -> await 사용
// 여기서 top level await까지 적용
}
ES모듈에서는 둘 다 사용불가
대신 import.meta.url로 경로를 가져올 수 있다.
// filename.mjs
console.log(import.meta.url); // 경로 가져옴
console.log(__filename); // 에러
require, module 객체는 선언없이 사용 가능한 내장 객체
-> 노드 내장 객체
: 모든 파일에서 접근 가능한 전역 객체
// globalA.js
module.exports = () => global.message;
// globalB.js
const A = require('./globalA');
global.message = '안녕하세요';
console.log(A()); // globalB를 실행하면 콘솔에 '안녕하세요' 출력
: 터미널 = 콘솔
: set 메서드에 clear 메서드가 대응됨
비동기코드
- setTimeout(콜백 함수, 밀리초0) : 밀리초 이후에 콜백 함수 실행
- setInterval(콜백 함수, 밀리초) : (변수에 담아서 사용) 밀리초 마다 콜백 함수 실행
- setImmediate(콜백 함수) : 콜백 함수 즉시 실행, setTimeout(--, 0)과 비슷하지만 실행 순서 다름
const timeout = setTimeout(() => {
console.log('1.5초 후 실행');
}, 1500);
// 이하 setInterval, setImmediate 모두 같은 방식으로 작성
setTimeout(() => {
clearTimeout(timeout);
}, 2500);
setImmediate는 즉시 실행이지만 호출 스택 -> 백그라운드 -> 이벤트 큐 -> 호출 스택으로 이동 후 실행되기 때문에 바로 취소하면 실행되지 않는다.
const immediate = setImmediate(() => {
console.log('실행되지 않습니다');
});
clearImmediate(immediate); // setImmediate 취소됨
실행 순서 :
: 현재 실행중인 노드 프로세스에 대한 정보를 담고 있음
이 코드를 콘솔창에 입력해서 실행한다.
process.--
명령어
: 시스템 환경 변수들이 들어있는 객체
const secretId = process.env.SECRET_ID;
const secretCode = process.env.SECRET_CODE;
NODE_OPTIONS=--max-old-space-size=8192 //메모리 크기 지정하는 옵션
UV_THREADPOOL_SIZE=8 // 동시에 돌릴 스레드풀 개수 지정(디폴트4)
: 다른 콜백 함수들보다 우선적으로 처리됨

: 현재의 프로세스를 멈춤
: 운영체제의 정보
// os.js
const os = require('os');
console.log(os.cpus());
코드를 실행시키면 CPU정보가 출력된다.

const path = require('path');
path.join(__dirname, '..', 'var.js');
path.resolve(__dirname, '..', './var.js');
// 둘 다 파일 경로를 표시(window와 linux간 파일 경로 표기가 다르기 때문에 사용)
// './var.js'에서 .은 파일 경로를 축약한 것
// .이 없으면 절대 경로로 취급된다

WHATWG와 노드의 주소 체계

콘솔에는 다음과 같이 출력된다.
: search를 객체화


주소

dns.lookup : ip주소
기타

멀티스레드로 돌아감
단방향 암호화 대표 : 해시 기법(비밀번호에 많이 사용)
같은 비밀번호 -> 같은 해시
해시 -> 비밀번호는 매우 어려움
createHash(알고리즘) : 사용할 해시 알고리즘 대입
update(문자열) : 변환할 문자열을 넣어줌
digest(인코딩) : 인코딩할 알고리즘 넣어줌(base64, hex, latin1 등)
기존 암호화 알고리즘 위협 -> 새로운 알고리즘 사용

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'));
});
});

const crypto = require('crypto');
const algorithm = 'aes-256-cvc';
const key = 'qwertyuiop123456789';
const iv = '12456789123456789'; // 초기화 백터 공격 방지
// 암호화
const cipher = crypto.createCipheriv(algorithm, key, iv);
let result = cipher.update('암호화할 문장', 'utf8', '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);
추천하는 암호화 알고리즘
단방향 암호화 : SHA512
대칭형 암호화 : AES
비대칭형 암호화 : RSA
: 각종 편의 기능 모아둔 모듈
const dontUseMe = util.deprecated((x, y) => {
console.log(x + y);
}, 'dontUseMe 함수는 deprecated되었으니 더 이상 사용하지 마세요!');
dontUseMe(1, 2);
const randomBytesPromise = util.promisify(crypto.randomBytes);
randomBytesPromise(64)
.then((buf) => {
console.log(buf.toString('base64'));
})
.catch((error) => {
console.error(error);
});
: 노드에서 멀티 스레드 방식으로 작업하게 하는 모듈
const { Worker, isMainThread, parentPort } = require('worker_threads');
if(isMainThread) { //분기처리 - 메인쓰레드
// 처음에 메인 실행 - 워커 생성 - 분배 - 메인에서 총집합
const worker = new Worker(__filename);
worker.postMessage('message', (value) => console.log('워커로부터', value));
worker.on('exit', () => console.log('워커 끝~'));
worker.postMessage('ping')
} else { // 워커 쓰레드
parentPort.oon('message', (value) => {
console.log('부모로부터', value);
parentPort.postMessage('pong');
parentPort.close();
})
}
메인쓰레드인 경우와 워커 쓰레드인 경우를 분기처리
처음에는 메인쓰레드 실행 후 워커 생성해서 분배
워커에서의 결과를 메인으로 총집합
const { Worker, isMainThread, parentPort } = require('worker_threads');
if(isMainThread) { //분기처리 - 메인쓰레드
// 처음에 메인 실행 - 워커 생성 - 분배 - 메인에서 총집합
const threads = new Set();
// 워커쓰레드 2개
threads.add(new Worker(__filename, {
workerData: {start : 1},
}));
threads.add(new Worker(__filename, {
workerData: {start : 2},
}));
for(let worker of threads) {
worker.postMessage('message', (value) => console.log('워커로부터', value));
worker.on('exit', () => {
// worker 하나 끝날때마다 삭제
threads.delete(worker);
if(threads.size === 0) { // 모든 worker 종료
console.log('워커 끝~')
}
});
}
worker.postMessage('ping')
} else { // 워커 쓰레드
parentPort.on('message', (value) => {
console.log('부모로부터', value);
parentPort.postMessage('pong');
parentPort.close();
})
}
const { Worker, isMainThread, parentPort, workerData } = require('worker_threads');
if(isMainThread) { ... }
else { // 워커 쓰레드
const data = workerData; // 데이터 받아옴
parentPort.postMessage(data.start + 100); // 데이터에 + 100
}
// prime.js
const min = 2;
const max = 10000000; //천만
const primes = [];
function generatePrimes(start, range) {
let isPrime = true;
const end = start + range;
for(let i = start; i < end; i++) {
for(let j = min; j < Math.sqrt(end); j++) {
if(i !== j && i % j === 0) {
isPrime = false;
break;
}
}
if(isPrime) {
primes.push(i);
}
isPrime = true;
}
}
console.time('prime');
generatePrimes(min, max);
console.timeEnd('prime');
console.log(primes.length);
이 경우에 코드를 실행시키면 콘솔창에 다음과 같이 출력된다.
이런 경우에 멀티스레드를 사용하면 좀 더 빠르게 계산할 수 있다.
// prime-worker.js
const {Worker, isMainThread, parentPort, workerData } = require('worker_threads');
const min = 2;
let primes = [];
function findPrimes(start, range) {
let isPrime = true;
const end = start + range;
for(let i = start; i < end; i++) {
for(let j = min; j < Math.sqrt(end); j++) {
if(i !== j && i % j === 0) {
isPrime = false;
break;
}
}
if(isPrime) {
primes.push(i);
}
isPrime = true;
}
}
if(isMainThread) {
const max = 10000000;
const threadCount = 8; // 8조각으로 나눠서 계산
const threads = new Set();
const range = Math.ceil((max - min) / threadCount);
let start = min;
console.time('prime');
for(let i = 0; i < threadCount - 1; i++) {
const wStart = start;
threads.add(new Worker(__filename, { workerData: { start: wStart, range}}));
}
threads.add(new Worker(__filename, {workerData: { start, range: range + ((max - min + 1) % threadCount)}}));
for(let worker of threads) {
worker.on('error', (err) => {
throw err;
})
worker.on('exit', () => {
threads.delete(worker);
if(threads.size === 0) {
console.timeEnd('prime');
console.log(primes.length);
}
});
worker.on('message', (msg) => {
// 워커 스레드에서 나온 값을 primes에 합침
primes = primes.concat(msg);
})
}
} else {
findPrimes(workerData.start, workerData.range);
parentPort.postMessage(primes);
}
이 경우에 코드를 실행시키면 콘솔창에 다음과 같이 출력된다.
시간이 6.3초에서 0.87초로 줄어들었다.
1/8로 줄어들지는 않고, 분배하는 계산을 하거나 컴퓨터 사양에 따라 동시에 도는 쓰레드의 종류가 다르기 때문
: 다른 언어 호출
// exec.js
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());
exec가 아닌 spawn을 이용해서 다른 언어를 호출할 수도 있다(여기선 파이썬)
// spawn.js
const spawn = require('child_process').spawn;
// 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());
});

: 파일, 폴더 생성, 삭제, 읽기, 쓰기 기능

이 코드에서 promises를 사용하고 싶으면 fs에서 제공하는 promises를 가져오면 된다.




결과는 다음과 같다.
