Node.js 교과서 : 3. 노드 기능 (1)

REPL 사용하기

  • READ(읽기) / EVAL(해석) / PRINT(반환) / LOOP(반복)
  • 정보를 읽어 해석한 것을 출력하는 과정을 반복하는 것을 의미
  • 미리 컴파일하지 않아도 콘솔을 통해 사용 가능함

JS 파일 실행하기

function helloWorld() {
    console.log('Hello World');
    helloNode();
}

function helloNode() {
    console.log('Hello Node');
}

helloWorld();
$ node helloWorld
Hello World
Hello Node

모듈 만들기

  • 모듈 : 특정한 기능을 하는 함수나 변수들의 집합
    • 모듈을 만들어두면 필요한 기능의 코드를 재사용할 수 있음
  • ES2015 모듈 : 자체 모듈 시스템 문법
    • require -> import / module.exports -> export default
    • 파일 확장자를 mjs로 바꾸거나 package.json에 type: "module" 속성을 넣어야함
  • var.js
const odd = '홀수';
const even = '짝수';

// 파일 불러오면 export default(module.exports)에 대입된 값 사용
export default {
    odd,
    even,
};
  • func.js
// const { odd, even } = require('./var');
import { odd, even } from './var.mjs';

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

export default checkOddOrEven;
  • index.js
import { odd, even } from './var.mjs';
import checkNumber from './func.mjs';

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

console.log(checkNumber(10));
console.log(checkStringOddOrEven('Hello'));

노드 내장 객체 알아보기

global
  • 브라우저의 window와 같은 전역 객체
  • 모든 파일에서 접근 가능
  • window.open(window.console)을 open(console)으로 호출하는 것 처럼 global도 생략 가능
$ node
> global
{
    global: [Circular *1],
    clearInterval: [Function: clearInterval]
    clearTimeout: [Function: clearTimeout]
    ...
}
> global.console
{
    log: [Function: bound consoleCall],
    warn: [Function: bound consoleCall],
    dir: [Function: bound consoleCall],
    ...
}
import A from './globalA.mjs';

global.message = '안녕하세요';
console.log(A());
export default () => global.message;
$ node globalB
안녕하세요
console
  • 디버깅을 위해 사용
  • console.time(레이블) / console.timeEnd(레이블) : 서로 대응되며, 같은 레이블을 가진 time과 timeEnd 사이 시간 측정
  • console.log(내용) : 로그들을 콘솔에 표시
  • console.error(에러 내용) : 에러를 콘솔에 표시
  • console.table(배열) : 배열 요소로 객체 리터럴을 넣으면 객체 속성들이 테이블 형식으로 표현
  • console.dir(객체, 옵션) : 객체를 콘솔에 표시할 떄 사용
    • 첫 번째 인수 : 표시할 객체
    • 두 번째 인수 : 옵션
      • colors : 경우 true로 할 경우 콘솔에 색이 추가
      • depth : 객체 안의 객체를 몇 단계까지 보여줄 지 결정(기본값 : 2)
  • console.trace(레이블) : 에러가 어디서 발생했는지 추적
const string = 'abc';
const number = 1;
const boolean = true;
const obj = {
    outside: {
        inside: {
            key: 'value'
        },
    },
};
console.time('전체 시간');
console.log('쉼표로 구분해 여러 값을 찍을 수 있습니다.');
console.log(string, number, boolean);
console.error('에러 메시지');

console.table([{
    name: '제로',
    birth: 1994
}, {
    name: 'hero',
    birth: 1998
}]);

console.dir(obj, {
    colors: false,
    depth: 2,
});
console.dir(obj, {
    colors: true,
    depth: 1,
});

console.time('시간 측정');
for (let i = 0; i < 100000; i++) { }
console.timeEnd('시간 측정');

function b() {
    console.trace('에러 위치 추적');
}
function a() {
    b();
}
a();

console.timeEnd('전체 시간');

타이머
  • setTimeout(콜백 함수, 밀리초) : 주어진 밀리초 이후에 콜백 함수 실행
  • setInterval(콜백 함수, 밀리초) : 주어진 밀리초마다 콜백 함수 반복 실행
  • setImmediate(콜백 함수) : 콜백 함수 즉시 실행
  • clearTimeout(아이디) : setTimeout을 취소
  • clearInterval(아이디) : setInterval을 취소
  • clearImmediate(아이디) : setImmediate를 취소
const timeout = setTimeout(() => {
    console.log('1.5초 후 실행');
}, 1500);

const interval = setInterval(() => {
    console.log('1초마다 실행');
}, 1000);

const timeout2 = setTimeout(() => {
    console.log('실행되지 않습니다.');
}, 3000);

setTimeout(() => {
    clearTimeout(timeout2);
    clearInterval(interval);
}, 2500);

const immediate = setImmediate(() => {
    console.log('즉시 실행');
});

const immediate2 = setImmediate(() => {
    console.log('실행되지 않습니다.');
});

clearImmediate(immediate2);
즉시 실행
1초마다 실행
1.5초 후 실행
1초마다 실행
__filename, __dirname
  • 현재 파일명과 파일 경로에 대한 정보 확인 가능
console.log(__filename);
console.log(__dirname);
c:\Users\Laeti\StudyProjects\Node_js-Textbook\Script\Chapter3\node3_4\filename.js
c:\Users\Laeti\StudyProjects\Node_js-Textbook\Script\Chapter3\node3_4
module, exports, require
  • 모듈을 만드는 객체
  • this 특징
    • 최상위 스코프에 존재하는 this는 module.exports를 가리킴
    • 함수 선언문 내부 this는 global 객체를 가리킴
      console.log(this);
      console.log(this === module.exports);
      console.log(this === exports);
      
      function whatIsThis() {
          console.log('function', this === exports, this === global);
      }
      
      whatIsThis();
      
      {}
      true
      true
      function false true
      
  • require
    • cache
      • 파일에 대한 속성이나 모듈 관계 등 cache 정보 확인
      • 한 번 require한 파일은 require.cache에 저장
      • 새로 require할 경우 require.cache의 속성을 제거해야 함
    • main : 노드 실행 시 첫 모듈을 가리킴
      console.log("require가 가장 위에 올 필요가 없습니다.");
      module.expports = "저를 찾아보세요.";
      
      require('./var');
      
      console.log('require.cache입니다.');
      console.log(require.cache); // 파일명에 대한 정보 출력
      console.log('require.main입니다.');
      console.log(require.main === module); // 현재 파일이 첫 모듈인지 확인
      console.log(require.main.filename); // 첫 모듈 파일명 확인
      
  • 순환 참조가 있을 경우 대상을 빈 객체로 표시
    • dep1.js
    const dep2 = require('./dep2');
    console.log('require dep2', dep2);
    module.exports = () => {
        console.log('dep2', dep2);
    };
    
    • dep2.js
    const dep1 = require('./dep1');
    console.log('require dep1', dep1);
    module.exports = () => {
    console.log('dep1', dep1);
    };
    
    dep-run.js
    const dep1 = require('./dep1');
    const dep2 = require('./dep2');
    
    dep1();
    dep2();
    
require dep1 {}
require dep2 [Function (anonymous)]
dep2 [Function (anonymous)]
dep1 {}
process
  • 현재 실행되고 있는 노드 프로세스에 대한 정보를 담고 있음

  • process.version : 설치된 노드 버전 확인

  • process.arch : 프로세서 아키텍처 정보 (arm, ia32 등의 값)

  • process.platform : 운영체제 플랫폼, linux나 darwin, freebsd 등의 값

  • process.pid : 현재 프로세스 아이디, 프로세스 여러 개를 가질 때 구분할 수 있음

  • process.uptime() : 프로세스가 시작된 후 흐른 시간, 초단위로 진행

  • process.exePath : 노드의 경로

  • process.cwd() : 현재 프로세스가 실행되는 위치

  • process.cpuUsage : 현재 cpu 사용량

  • process.env : 시스템 환경 변수

  • process.nextTick(콜백) : 이벤트 루프가 다른 콜백 함수들보다 nextTick의 콜백 함수를 우선으로 처리하도록 만듦

    • process.nextTick와 Promise를 마이크로태스크(microtask)라 부름
    • 이벤트 루프는 마이크로태스크를 우선하기 때문에 재귀 호출은 다른 콜백 함수들이 실행되지 않을 수 있음
setImmediate(() => {
    console.log('immediate');
});
process.nextTick(() => {
    console.log('nextTick');
});
setTimeout(() => {
    console.log('timeout');
}, 0);
Promise.resolve().then(() => console.log('promise'));
$ node nextTick
nextTick
promise
timeout
immediate
process.exit(노드 프로세스)
  • 실행중인 노드 프로세스 종료
let i = 1;
setInterval(() => {
    if (i === 5) { // i가 5가 되었을 때 종료
        console.log('종료!');
        process.exit();
    }
    console.log(i);
    i += 1;
}, 1000);

노드 내장 모듈

os
  • 사용자 컴퓨터의 운영체제 정보를 가져옴
  • os.arch() : process.arch와 동일, 프로세서 아키텍처 정보 (arm, ia32 등의 값)
  • os.platform() : process.platform과 동일, 운영체제 플랫폼, linux나 darwin, freebsd 등의 값
  • os.type() : 운영체제의 종류
  • os.uptime() : 운영체제 부팅 이후 흐른 시간(초), 노드의 실행 시간
  • os.hostname() : 컴퓨터의 이름
  • os.release() : 운영체제 버전
  • 경로
  • os.homedir() : 홈 디렉터리 경로
  • os.tmpdir() : 임시 파일 저장 경로
  • CPU 정보
  • os.cpus() : 컴퓨터의 코어 정보
  • os.freemem() : 사용 가능한 메모리(RAM)
  • os.totalmem() : 전체 메모리 용량
path
  • 폴더와 파일의 경로를 쉽게 조작하도록 도와주는 모듈
  • path.sep : 경로의 구분자, 윈도 : \로 경로 구분, POSIX : /로 경로 구분
  • path.delimiter : 환경 변수의 구분자
  • path.dirname(경로) : 파일이 위치한 폴더 경로
  • path.extname(경로) : 파일의 확장자
  • path.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()과 다르게 절대경로로 인식하여 앞의 경로 무시
    • path.join('/a', '/b'. '/c'); /* 결과 : /a/b/c/ */
    • path.resolve('/a', '/b', 'c'); /* 결과 : /b/c */
url
  • 인터넷 주소를 쉽게 조작하도록 도와주는 모듈
  • 노드 버전 7에서 추가된 WHATWG(웹 표준 단체) 방식 url과 예전 노드에서 사용한 방식 url이 존재
  • url.parse(주소) : 주소를 분해, WHATWG 방식과 비교하면 username과 password 대신 auth 속성이 있고, searchParams 대신 query가 있음
  • url.format(객체) : WHATWG 방식 url과 기존 노드의 url을 모두 사용할 수 있음
const url = require('url'); // WHATWG 방식 url

// 기존 방식 url
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));
new URL(): URL {
  href: 'http://www.gilbut.co.kr/book/bookList.aspx?sercate1=001001000#anchor',
  origin: 'http://www.gilbut.co.kr',
  protocol: 'http:',
  username: '',
  password: '',
  host: 'www.gilbut.co.kr',
  hostname: 'www.gilbut.co.kr',
  port: '',
  pathname: '/book/bookList.aspx',
  search: '?sercate1=001001000',
  searchParams: URLSearchParams { 'sercate1' => '001001000' },
  hash: '#anchor'
}
url.format(): http://www.gilbut.co.kr/book/bookList.aspx?sercate1=001001000#anchor
  • searchParams 객체 : WHATWG 방식에서 반환되는 객체
    • search는 주소를 통해 데이터 전달할 때 사용, 물음표(?)로 시작해 그 뒤에 키=값 형식으로 데이터 전달, 여러 키인 경우 &로 구분
const myURL = new URL('http://www.gilbut.co.kr/?page=3&limit=10&category=nodejs&category=javascript');
console.log('searchParams:', myURL.searchParams);
console.log('searchParams.getAll():', myURL.searchParams.getAll('category'));
console.log('searchParams.get():', myURL.searchParams.get('limit'));
console.log('searchParams.has():', myURL.searchParams.has('page'));

console.log('searchParams.keys():', myURL.searchParams.keys());
console.log('searchParams.values():', myURL.searchParams.values());

myURL.searchParams.append('filter', 'es3');
myURL.searchParams.append('filter', 'es5');
console.log(myURL.searchParams.getAll('filter'));

myURL.searchParams.set('filter', 'es6');
console.log(myURL.searchParams.getAll('filter'));

myURL.searchParams.delete('filter');
console.log(myURL.searchParams.getAll('filter'));

console.log('searchParams.toString():', myURL.searchParams.toString());
myURL.search = myURL.searchParams.toString();
searchParams: URLSearchParams {
  'page' => '3',
  'limit' => '10',
  'category' => 'nodejs',
  'category' => 'javascript' }
searchParams.getAll(): [ 'nodejs', 'javascript' ]
searchParams.get(): 10
searchParams.has(): true
searchParams.keys(): URLSearchParams Iterator { 'page', 'limit', 'category', 'category' }
searchParams.values(): URLSearchParams Iterator { '3', '10', 'nodejs', 'javascript' }
[ 'es3', 'es5' ]
[ 'es6' ]
[]
searchParams.toString(): page=3&limit=10&category=nodejs&category=javascript
객체 조작 메서드
  • getAll(키) : 키에 해당하는 모든 값들을 가져옴
  • get(키) : 키에 해당하는 첫 번째 값만 가져옴
  • has(키) : 해당 키가 있는지 없는지 검사
  • keys() : searchParams의 모든 키를 반복기(iterator) 객체로 가져옴
  • values() : searchParams의 모든 값을 반복기 객체로 가져옴
  • append(키, 값) : 키를 추가, 같은 값의 키가 있으면 유지하고 하나 더 추가
  • set(키, 값) : 같은 값 키를 모두 지우고 새로 추가
  • delete(키) : 해당 키 제거
  • toString() : 조작한 searchParams 객체를 다시 문자열로 만듦, search에 대입하면 주소 객체에 반영
querystring
  • 기존 방식 url을 사용할 때, search부분을 사용하기 쉽게 객체로 만드는 모듈
  • querystring.parse(쿼리) : url query 부분을 자바스크립트 객체로 분해
  • querystring.stringify(객체) : 분해된 query 객체를 문자열로 다시 조립
const url = require('url');
const querystring = require('querystring');

const parsedUrl = url.parse("http://www.gilbut.co.kr/?page=3&limit=10&category=nodejs&category=javascript");
const query = querystring.parse(parsedUrl.query);
console.log('querystring.parse() : ', query);
console.log('querystring.stringify() : ', querystring.stringify(query));
querystring.parse() :  [Object: null prototype] {
  page: '3',
  limit: '10',
  category: [ 'nodejs', 'javascript' ]
}
querystring.stringify() :  page=3&limit=10&category=nodejs&category=javascript
crypto
  • 다양한 방식의 암호화를 도와주는 모듈

  • 단방향 암호화 : 비밀번호에 사용하는 암호화, 복호화할 수 없는 암호화 방식을 의미. 해시 기법을 주로 사용

    • 해시 기법 : 어떠한 문자열을 고정된 길이의 다른 문자열로 바꿔버리는 방식
      • createHash(알고리즘) : 사용할 해시 알고리즘을 넣음, md5와 sha1은 이미 취약점이 발견
      • update(문자열) : 변환할 문자열을 넣음
      • digest(인코딩) : 인코딩할 알고리즘을 넣음, base64, hex, latin1 등이 있음
    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'));
    
    base64: dvfV6nyLRRt3NxKSlTHOkkEGgqW2HRtfu19Ou/psUXvwlebbXCboxIPmDYOFRIpqav2eUTBFuHaZri5x+usy1g==
    hex: 76f7d5ea7c8b451b773712929531ce92410682a5b61d1b5fbb5f4ebbfa6c517bf095e6db5c26e8c483e60d8385448a6a6afd9e513045b87699ae2e71faeb32d6
    base64: cx49cjC8ctKtMzwJGBY853itZeb6qxzXGvuUJkbWTGn5VXAFbAwXGEOxU2Qksoj+aM2GWPhc1O7mmkyohXMsQw==
    
    • 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) => { // 알고리즘 10만회 반복해서 적용
        console.log('password:', key.toString('base64'));
      });
    });
    
    salt: nt/WvMfQuxy87m3G6yGZvuyamTvmhmx54ME5ns7YZq38Gq1UHkULcOKpmzyiL+49wAOuVZXQIN2THidlJl3U3w==
    password: PAtnwM8inzx5PxmOCDv78bYnQEx9K4dd9O8AbL9AYViTYfBG6J5EjeOaNsAdr/6YTEwjtD93n2p9jIuW9b0kew==
    
  • 양방향 암호화 : 키를 통해 암호화된 문자열을 복호화할 수 있음

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('암호화할 문장', '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);
암호화: iiopeG2GsYlk6ccoBoFvEH2EBDMWv1kK9bNuDjYxiN0=
복호화: 암호화할 문장
util
  • 각종 편의 기능을 모아둔 모듈
  • util.deprecate : deprecate 처리된 함수임을 알림
  • util.promisify : 콜백 패턴을 프로미스 패턴으로 바꿈
const util = require('util');
const crypto = require('crypto');

const dontUseMe = util.deprecate((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);
  });

3
(node:96184) DeprecationWarning: dontUseMe 함수는 deprecated되었으니 더 이상 사용하지 마세요!
(Use `node --trace-deprecation ...` to show where the warning was created)
aBVbBkVkNuz/uHCxXp3H2j5k6mLpVN1HktwUK3WYykU48ZfiOaA7/r+OyJuoUxU21VosJRDzRz/iLciq1SAg3w==
worker_threads : 노드에서 멀티 스레드 방식으로 작업
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();
  });
}
from parent ping
from worker pong
worker exit
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());
}); // 실행 에러
2022-12-15  ���� 03:55    <DIR>          .
2022-12-14  ���� 08:32    <DIR>          ..
2022-12-29  ���� 11:20    <DIR>          Image
2022-12-29  ���� 11:20    <DIR>          Markdown
2022-12-15  ���� 01:49    <DIR>          Script
               0�� ����                   0 ����Ʈ
               5�� ���͸�  250,540,572,672 ����Ʈ ����
기타 모듈
  • assert : 값을 비교하여 프로그램이 제대로 동작하는지 테스트
  • dns : 도메인 이름에 대한 IP 주소를 얻어냄
  • net : HTTP보다 로우 레벨인 TCP나 IPC 통신을 할 때 사용
  • string_decoder : 버퍼 데이터를 문자열로 바꾸는 데 사용
  • tls : TLS와 SSL에 관련한 작업을 할 때 사용
  • tty : 터미널과 관련된 작업을 할 때 사용
  • dgram : UDP와 관련된 작업을 할 때 사용
  • v8 : V8 엔진에 직접 접근할 때 사용
  • vm : 가상 머신에 직접 접근할 때 사용

참고 문헌

조현영, 『Node.js 교과서』, 개정 2판, 길벗출판사, 2020

이 글이 도움이 되었나요?

0 minutes ago
작성된 댓글이 없습니다. 첫 댓글을 달아보세요!
    댓글을 작성하려면 로그인이 필요합니다.