상세 컨텐츠

본문 제목

[Node.js 1] 노드 기능 알아보기 (2)

23-24/Node.js 1

by Hetbahn 2023. 11. 3. 10:00

본문

728x90

 

노드 기능 알아보기

 

os 모듈

웹 브라우저에 사용되는 자바스크립트는 운영체제의 정보를 가져올 수 없지만, 노드는 os 모듈에 정보가 담겨 있어 정보를 가져올 수 있다.

const os = require('os');

 

일반적인 웹 서비스를 제작할 때는 사용 빈도가 높지 않지만 운영체제별로 다른 서비스를 제공하고 싶을 때 os 모듈을 사용한다. 

 

path 모듈

경로 구분자는 윈도 타입(\)과 POSIX 타입(/)으로 구분된다. 

운영체제별로 경로 구분자가 다르기 때문에 폴더와 파일의 경로를 쉽게 조작하도록 도와준다.

const path = require('path');
  • 윈도에서 POSIX 스타일 경로를 이용하고자 할 때 path.posix.join(), path.posix.sep
  • POSIX에서 윈도 스타일 경로를 이용하고자 할 때 path.win32.sep, path.win24,join()

 

url

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

WHATWG 방식의 url과 예전부터 노드에서 사용하던 방식의 url이 있으며 요즘은 WHATWG 방식만 사용한다.

const url = require('url');

url 모듈 안에 URL 생성자가 있다. 

URL은 노드 내장 객체이기도 하여 requrie 할 필요는 없다.

해당 생성자에 주소를 넣어 객체로 만들면 주소가 부분별로 정리된다.

const myURL = new URL('http://www.gilbut.co.kr/book/bookList.aspx?sercate1=001001000#anchor');

주소가 host 부분 없이 pathname만 오는 경우 new URL('/book/bookList.apsx', 'https://www.gilbut.co.kr') 두 번째 인수로 host를 적어주어야 한다.

 

생성된 객체에는  username, password, origin, searchParams 속성이 존재한다.

console.log('new URL():', myURL);

/*new URL(): URL {
  href: 'http://www.gilbut.co.kr/book/bookList.aspx?sercate 1 = 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: '?sercate 1 = 001001000 ',
  searchParams: URLSearchParams { 'sercate 1 ' => '001001000 ' },
  hash: '#anchor'
}*/

 

 url.format(객체): 분해되었던 url 객체를 다시 원래 상태로 조립한다.

console.log('url.format():', url.format(myURL));

  searchParams 객체

search는 물음표(?)로 시작하고, 그 뒤에 키=값 형식으로 데이터를 전달한다.

new URLSearchParams(myURL.search)로도 같은 결괏값을 얻을 수 있다.

const myURL = new URL('http://www.gilbut.co.kr/?page=3&limit=10&category=nodejs&category=javascript');

 

dns

도메인을 통해 IP나 기타 DNS 정보를 얻고자 할 때 사용한다.

import dns from 'dns/promises';

ip 주소는 간단하게 dns.lookup 이나 dns.resolve(도메인)으로 얻을 수 있다.

const ip = await dns.lookup('gilbut.co.kr');
console.log('IP', ip);
// IP { address: ' 49 . 236 . 151 . 220 ', family: 4 }

 

crypto

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

const crypto = require('crypto');

 

단방향 암호화

복호화할 수 없는 암호화 방식 →  해시 함수라고 불린다

어떠한 문자열을 고정된 길이의 다른 문자열로 바꿔버리는 방식인 해시 기법을 사용한다.

console.log('base64:', crypto.createHash('sha512').update('비밀번호').digest('base64'));
// base64: dvfV6nyLRRt3NxKSlTHOkkEGgqW2HRtfu19Ou/psUXvwlebbXCboxIPmDYOFRIpqav2eUTBFuHaZri5x+usy1g==

 createHash(알고리즘): 사용할 해시 알고리즘을 넣는다. 현재는 sha512 정도로 충분하다.

 update(문자열): 변환할 문자열을 넣는다.

 digest(인코딩): 인코딩할 알고리즘을 넣는다.  base64가 결과 문자열이 가장 짧아서 주로 사용된다. 결과물로 변환된 문자열을 반환한다.

 

양방향 암호화

암호화된 문자열을 복호화할 수 있으며, 키(열쇠)라는 것을 사용해 복호화한다.

 crypto.createCipheriv(알고리즘, 키, iv): 암호화 알고리즘과 키, iv를 넣는다. 

 cipher.update(문자열, 인코딩, 출력 인코딩): 암호화할 대상과 대상의 인코딩, 출력 결과물의 인코딩을 넣는다.

 cipher.final(출력 인코딩): 출력 결과물의 인코딩을 넣으면 암호화가 완료된다.

 crypto.createDecipheriv(알고리즘, 키, iv): 복호화할 때 사용합니다. 암호화할 때 사용했던 알고리즘과 키, iv를 그대로 넣어야 한다.

 decipher.update(문자열, 인코딩, 출력 인코딩): 암호화된 문장, 그 문장의 인코딩, 복호화할 인코딩을 넣는다. createCipheriv update()에서 넣은 순서와 반대로 넣는다.

 decipher.final(출력 인코딩): 복호화 결과물의 인코딩을 넣는다.

 

util

각종 편의 기능을 모아둔 모듈이다.

const util = require('util');

 util.deprecate: 함수가 deprecated 처리되었음을 알린다. 첫 번째 인수로 넣은 함수를 사용했을 때 경고 메시지가 출력된다. 두 번째 인수로 경고 메시지 내용을 넣으면 된다. 함수가 조만간 사라지거나 변경될 때 알려줄 수 있어 유용하다.

 util.promisify: 콜백 패턴을 프로미스 패턴으로 바꾼다. 바꿀 함수를 인수로 제공하면 된다. 이렇게 바꿔두면 async/await 패턴까지 사용할 수 있다.

 

 

worker_threads

노드에서 멀티 스레드 방식으로 작업할 수 있다.

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

isMainThread : 현재 코드가 메인 스레드 에서 실행되는지, 아니면 우리가 생성한 워커 스레드에서 실행되는지 구분된다.

if (isMainThread) { // 부모일 때
  const worker = new Worker(__filename);  // 현재 파일을 new Worker을 워커 스레드에서 실행시킴 
  worker.on('message', message => console.log('from worker', message));
  worker.on('exit', () => console.log('worker exit'));  // 워커와 연결이 종료될 때 실행됨 
  worker.postMessage('ping');  // worker.postMessage - 워커에 데이터를 보냄 
} else { // 워커일 때
  parentPort.on('message', (value) => {  // parentPort.on('message') - 부모로부터 메세지를 받음 
    console.log('from parent', value);
    parentPort.postMessage('pong'); // parentPort.postMessage - 부모에게 메세지를 보냄 
    parentPort.close();  // 부모와의 연결 종료 
  });
}
/*
from parent ping
from worker pong
worker exit
*/
  1. 메인 스레드에서 실행
  2. 워커 스레드 생성
  3. 메인 스레드에서 'ping' 메세지를 워커 스레드로 보냄
  4. 워커 스레드에서 'ping'메세지를 받고 출력
  5. 메인 스레드에서 'pong' 메세지를 받고 출력
  6. 워커 스레드가 종료되고 worker exit 출력

여러 개의 워커 스레드에 데이터를 넘김

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

if (isMainThread) { // 부모일 때
  const threads = new Set();
  threads.add(new Worker(__filename, {  // 워커 스레드 1
    workerData: { start: 1 },
  }));
  threads.add(new Worker(__filename, {
    workerData: { start: 2 },  // 워커 스레드 2
  }));
  for (let worker of threads) {  // 워커 1 메세지, 워커 2 메세지 
    worker.on('message', message => console.log('from worker', message));
    worker.on('exit', () => {
      threads.delete(worker);
      if (threads.size === 0) {  // 워커 스레드가 모두 종료되었을 때 
        console.log('job done');
      }
    });
  }
} else { // 워커일 때
  const data = workerData;
  parentPort.postMessage(data.start + 100);
}
/*
from worker 101
from worker 102
job done
*/

 

child_process

노드에서 다른 프로그램을 실행하고 싶거나 명령어를 수행하고 싶을 때 사용하는 모듈이다.

= 다른 언어의 코드를 실행하고 결괏값을 받을 수 있다.

 

- dir 실행

const exec = require('child_process').exec;
const process = exec('넣고 싶은 명령어');  // dir

// 결과는 stdout(표준출력)과 stderr(표준에러)에 붙여둔 data 이벤트 리스너에 버퍼 형태로 전달됨 
process.stdout.on('data', function(data) { // 외부 명령어의 표준 출력(stdout)을 감지
  console.log(data.toString());
}); // 실행 결과

process.stderr.on('data', function(data) {  // 외부 명령어의 표준에러(stderr)를 감지
  console.error(data.toString());
}); // 실행 에러
/*
chcp 65001
*/

- python 실행

const spawn = require('child_process').spawn;  // spawn - 파이썬 코드를 실행하는 명령어

const process = spawn('python', ['test.py']);  // spawn 첫번쩨 인수로 명령어, 두번째 인수로 옵션 배열을 넣음 

// 결과는 exec과 마찬가지로 stdout, stderr의 데이터로 나옴 
process.stdout.on('data', function(data) {
  console.log(data.toString());
}); // 실행 결과

process.stderr.on('data', function(data) {
  console.error(data.toString());
}); // 실행 에러
  •  exec은 셸을 실행해서 명령어를 수행한다.
  • spawn은 새로운 프로세스를 띄우면서 명령어를 실행하며 세 번째 인수 { shell: true }을 제공하면 exec처럼 수행한다. 

 

기타 모듈들

 async_hooks: 비동기 코드의 흐름을 추적할 수 있는 실험적인 모듈이다.

 dgram: UDP와 관련된 작업을 할 때 사용한다.

 net: HTTP보다 로우 레벨인 TCP나 IPC 통신을 할 때 사용한다.

 perf_hooks: 성능 측정을 할 때 console.time보다 더 정교하게 측정한다.

 querystring: URLSearchParams가 나오기 이전에 쿼리스트링을 다루기 위해 사용했던 모듈이다.

 string_decoder: 버퍼 데이터를 문자열로 바꾸는 데 사용한다.

 tls: TLS와 SSL에 관련된 작업을 할 때 사용 한다.

 tty: 터미널과 관련된 작업을 할 때 사용 한다.

 v8: v8 엔진에 직접 접근할 때 사용 한다.

 vm: 가상 머신에 직접 접근할 때 사용 한다.

 wasi: 웹어셈블리를 실행할 때 사용하는 실험적인 모듈이다..

 

파일 시스템 접근하기

 

동기 메서드와 비동기 메서드

const fs = require('fs');
console.log('시작');
fs.readFile('./readme2.txt', (err, data) => {
  if (err) {
    throw err;
  }
  console.log('1번', data.toString());
});
fs.readFile('./readme2.txt', (err, data) => {
  if (err) {
    throw err;
  }
  console.log('2번', data.toString());
});
fs.readFile('./readme2.txt', (err, data) => {
  if (err) {
    throw err;
  }
  console.log('3번', data.toString());
});
console.log('끝');
/*
시작
끝
2번 저를 여러 번 읽어보세요.
3번 저를 여러 번 읽어보세요.
1번 저를 여러 번 읽어보세요.
*/

비동기 메서드들은 백그라운드에 해당 파일을 읽으라고만 요청하고 다음 작업으로 넘어간다.

파일 읽기 요청만 세 번을 보낸 후 끝 출력

백그라운드에서는 요청 세 개를 거의 동시에 실행한다.

동기 순차적으로 실행
이전 작업이 완료될 때까지 대기
백그라운드 작업 완료 확인 여부
비동기 다른 작업을 기다리지 않고 백그라운드에서 병렬로 실행
블로킹 결과가 나올때까지 제어를 반환하지 않고 대기 함수가 바로 return 되는지 여부 
논블로킹 결과가 나오지 않아도 제어를 즉시 반환하여 다른 작업을 실행 

 비동기 fs 메서드를 사용하면 백그라운드가 동시에 작업할 수도 있고, 메인 스레드는 다음 작업을 처리할 수 있다.

 

async/await을 이용해 비동기 방식을 이용하여 순차적으로 실행시킬 수도 있다.

console.log('시작');
fs.readFile('./readme2.txt')
  .then((data) => {
    console.log('1번', data.toString());
    return fs.readFile('./readme2.txt');
  })
  .then((data) => {
    console.log('2번', data.toString());
    return fs.readFile('./readme2.txt');
  })
  .then((data) => {
    console.log('3번', data.toString());
    console.log('끝');
  })
  .catch((err) => {
    console.error(err);
});

 

버퍼와 스트림 이해하기

노드는 파일을 읽을 때 메모리에 파일 크기만큼 공간을 마련해 두며 파일 데이터를 메모리에 저장한 뒤 사용자가 조작할 수 있도록 한다.

메모리에 저장된 데이터가 바로 버퍼이다.

Buffer클래스

 from(문자열): 문자열을 버퍼로 바꾼다.

 toString(버퍼): 버퍼를 다시 문자열로 바꾼다.

 concat(배열): 배열 안에 든 버퍼들을 하나로 합친다.

 alloc(바이트): 바이트를 인수로 넣으면 해당 크기의 버퍼가 생성된다.

 

파일을 읽는 createReadStream 메서드

const readStream = fs.createReadStream('./readme3.txt', { highWaterMark: 16 });
// 첫 번째 인수는 파일 경로, 두 번째 인수는 옵션 객체
// highWaterMark라는 옵션이 버퍼의 크기(바이트 단위)를 정할 수 있는 옵션
readStream.on('data', (chunk) => {  // data 이벤트 리스너 - 파일을 읽기 시작할 때 발생 
  data.push(chunk);
  console.log('data :', chunk, chunk.length);
});

readStream.on('end', () => {  // end 이벤트 리스너 - 파일을 다 읽으면 발생 
  console.log('end :', Buffer.concat(data).toString());
});

readStream.on('error', (err) => {  // error 이벤트 리스너 - 파일을 읽는 도중 에러 발생 
  console.log('error :', err);
});

readStream은 이벤트 리스너를 붙여서 사용한다.

const writeStream = fs.createWriteStream('./writeme2.txt');
writeStream.on('finish', () => {
  console.log('파일 쓰기 완료');
});

writeStream.write('이 글을 씁니다.'); // write 메서드로 넣을 데이터를 작성 
writeStream.write('한 번 더 씁니다.'); 
writeStream.end();  // 데이터를 다 썼다면 종료를 알림 -> finish 이벤트 발생

파일 복사 

- writeFileSync 사용 → 파일 용량만큼 메모리가 필요하다. 

const data1 = fs.readFileSync('./big.txt');
fs.writeFileSync('./big2.txt', data1);

- 스트림 사용 →  큰 파일을 조각내어 작은 버퍼 단위로 옮긴다.

const readStream = fs.createReadStream('readme4.txt');
const writeStream = fs.createWriteStream('writeme3.txt');
readStream.pipe(writeStream);  // on, writeStream.write를 하지 않아도 됨

미리 읽기 스트림과 쓰기 스트림을 만들어둔 후 두 개의 스트림 사이를 pipe 메서드로 연결하면 저절로 데이터가 넘어간다. 

스트림을 사용해 파일을 복사하는 방법은 큰 파일을 조각내어 작은 버퍼 단위로 옮기기 때문에 writeFileSync 방식보다 메모리 차지가 적다.

 

pipeline메서드를 이용해 여러 개의 파이프를 연결할 수 있다.

import { pipeline } from 'stream/promises';
import zlib from 'zlib';
import fs from 'fs';

const ac = new AbortController(); // AbortController을 이용해 원할 때 파이프 중단 가능
const signal = ac.signal;

setTimeout(() => ac.abort(), 1); // 1ms 뒤에 중단
await pipeline(
  fs.createReadStream('./readme4.txt'),
  zlib.createGzip(),
  fs.createWriteStream('./readme4.txt.gz'),
  { signal },
);

이때 pipeline의 마지막 인수로 { signal }을 추가하해 원하는 시점에 ac.abort()를 호출하면 중단할 수 있다.

 

기타 fs 메서드 알아보기

파일을 생성하고 삭제할 수도 있으며 폴더를 생성하고 삭제할 수도 있다.

const fs = require('fs').promises;
const constants = require('fs').constants;
fs.access('./folder', constants.F_OK | constants.W_OK | constants.R_OK)

 fs.access(경로, 옵션, 콜백): 폴더나 파일에 접근할 수 있는지를 체크한다. 두 번째 인수로 상수들(constants를 통해 가져옴)을 넣는다. F_OK는 파일 존재 여부, R_OK는 읽기 권한 여부, W_OK는 쓰기 권한 여부를 체크한다. 파일/폴더나 권한이 없다면 에러가 발생하는데, 파일/폴더가 없을 때의 에러 코드는 ENOENT이다.

return fs.mkdir('./folder');

 fs.mkdir(경로, 콜백): 폴더를 만드는 메서드이다. 이미 폴더가 있다면 에러가 발생하므로 먼저 access 메서드를 호출해서 확인해야 한다.

return fs.open('./folder/file.js', 'w');

 fs.open(경로, 옵션, 콜백): 파일의 아이디(fd 변수)를 가져오는 메서드이다. 파일이 없다면 파일을 생성한 뒤 그 아이디를 가져온다. 가져온 아이디를 사용해 fs.read 또는 fs.write로 읽거나 쓸 수 있다. 두 번째 인수로 어떤 동작을 할 것인지를 설정할 수 있다. 쓰려면 w, 읽으려면 r, 기존 파일에 추가하려면 a이다.

return fs.rename('./folder/file.js', './folder/newfile.js');

 fs.rename(기존 경로, 새 경로, 콜백): 파일의 이름을 바꾸는 메서드이다. 기존 파일 위치와 새로운 파일 위치를 적으면 된다.

fs.readdir('./folder')

 fs.readdir(경로, 콜백): 폴더 안의 내용물을 확인할 수 있다. 배열 안에 내부 파일과 폴더명이 나온다.

return fs.unlink('./folder/newfile.js');

 fs.unlink(경로, 콜백): 파일을 지울 수 있다. 파일이 없다면 에러가 발생하므로 먼저 파일이 있는지를 꼭 확인해야 한다.

return fs.rmdir('./folder');

 fs.rmdir(경로, 콜백): 폴더를 지울 수 있다. 폴더 안에 파일들이 있다면 에러가 발생하므로 먼저 내부 파일을 모두 지우고 호출해야 한다.

 

copyfile로 간단하게 파일 복사

fs.copyFile('readme4.txt', 'writeme4.txt')
  .then(() => {
    console.log('복사 완료');
  })
  .catch((error) => {
    console.error(error);
});

 watch 메서드파일/폴더의 변경 사항을 감시

fs.watch('./target.txt', (eventType, filename) => {
  console.log(eventType, filename);
});

내용물을 수정할 때는 change 이벤트가 발생하고, 파일명을 변경하거나 파일을 삭제하면 rename 이벤트가 발생한다. 

rename 이벤트가 발생한 후에는 더 이상 watch가 수행되지 않는다

 

스레드 풀 알아보기

비동기 메서드들은 백그라운드에서 실행되고, 실행된 후에는 다시 메인 스레드의 콜백 함수나 프로미스의 then 부분이 실행된다.

→ fs 메서드를 여러 번 실행해도 백그라운드에서 동시에 처리되는데, 바로 스레드 풀 때문이다.

기본적인 스레드 풀의 개수에 따라 작업을 처리하며 SET UV_THREADPOOL_SIZE에 따라 처리 양이 달라진다.

 

이벤트 이해하기

const EventEmitter = require('events');

events 모듈을 사용하면 된다.

 on(이벤트명, 콜백): 이벤트 이름과 이벤트 발생 시의 콜백을 연결한다. 이렇게 연결하는 동작을 이벤트 리스닝이라고 한다.

 addListener(이벤트명, 콜백): on과 기능이 같다.

 emit(이벤트명): 이벤트를 호출하는 메서드이다. 이벤트 이름을 인수로 넣으면 미리 등록해 뒀던 이벤트 콜백이 실행된다.

 once(이벤트명, 콜백): 한 번만 실행되는 이벤트이다. myEvent.emit('event3')을 두 번 연속 호출해도 콜백이 한 번만 실행된다.

 removeAllListeners(이벤트명): 이벤트에 연결된 모든 이벤트 리스너를 제거한다.

 removeListener(이벤트명, 리스너): 이벤트에 연결된 리스너를 하나씩 제거한다. 

 off(이벤트명, 콜백): 노드 10 버전에서 추가된 메서드로, removeListener와 기능이 같다.

 listenerCount(이벤트명): 현재 리스너가 몇 개 연결되어 있는지 확인한다.

  • on('data') - 겉으로 이 이벤트를 호출하는 코드는 없지만, 내부적으로는 chunk를 전달할 때마다 data 이벤트를 emit한다.

 

예외 처리하기

예외란 보통 처리하지 못한 에러를 말한다.

- try/catch 문

setInterval(() => {
  console.log('시작');
  try {
    throw new Error('서버를 고장내주마!');
  } catch (err) {
    console.error(err);
  }
}, 1000);

- 노드 자체에서 잡아주는 에러

const fs = require('fs');

setInterval(() => {
  fs.unlink('./abcdefg.js', (err) => {
    if (err) {
      console.error(err);
    }
  });
}, 1000);

에러가 발생하지만 노드 내장 모듈의 에러는 실행 중인 프로세스를 멈추지 않는다.

에러를 throw하면 노드 프로세스가 멈춰버리기 때문에 이 경우는 try/catch문으로 throw한 에러를 잡아야 한다.

 

예측 불가능한 에러 처리

process.on('uncaughtException', (err) => {  // process 객체에 uncaughtException 이벤트 리스너를 단다.
  console.error('예기치 못한 에러', err);
});

setInterval(() => {
  throw new Error('서버를 고장내주마!');
}, 1000);
setTimeout(() => {
  console.log('실행됩니다');
}, 2000);

처리하지 못한 에러가 발생했을 때 uncaughtException 이벤트 리스너가 실행되고 프로세스가 유지된다.

그러나 uncaughException 이벤트 발생 후 다음 동작이 제대로 동작하는지를 보증하지 않는다.

uncaughtException은 단순히 에러 내용을 기록하는 정도로 사용하고, 에러를 기록한 후 process.exit()으로 프로세스를 종료하는 것이 적절하다.

 

빈칸 채우기

  1. path 모듈이 필요한 이유 중 하나는 운영체제별로 경로 구분자가 다르기 때문이다. 크게 ( 윈도 ) 타입과 ( POSIX ) 타입으로 구분된다.
  2. search는 ( 물음표(?) ) 로 시작하고, 그 뒤에 ( 키=값 ) 형식으로 데이터를 전달한다.
  3. ( 단방향 암호화 )란 복호화할 수 없는 암호화 방식을 뜻하고 ( 양방향 암호화 )는 암호화된 문자열을 복호화할 수 있으며, 키(열쇠)라는 것이 사용되는 방식이다.
  4. 노드에서 다른 프로그램을 실행하고 싶거나 명령어를 수행하고 싶을 때 사용하는 모듈은 ( child_process )이다
  5. 노드는 파일을 읽을 때 메모리에 파일 크기만큼 공간을 마련해 두며 파일 데이터를 메모리에 저장한 뒤 사용자가 조작할 수 있도록 한다. 이때 메모리에 저장된 데이터가 바로 ( 버퍼 )이다.
  6. 읽기 스트림과 쓰기 스트림을 만들어둔 후 두 개의 스트림 사이를 ( pipe ) 메서드로 연결하면 저절로 데이터가 writeStream으로 넘어간다.
  7. 파일의 내용물을 수정할 때는 ( change ) 이벤트가 발생하고, 파일명을 변경하거나 파일을 삭제하면 ( rename )이벤트가 발생한다.
  8. ( on(이벤트명, 콜백) )은 이벤트 이름과 이벤트 발생 시의 콜백을 연결하며 addListener(이벤트명, 콜백)과 기능이 같다.

 

코드 작성 문제 1

  setInterval(() => {
    throw new Error('서버를 고장내주마!');
  }, 1000);
  setTimeout(() => {
    console.log('누구맘대로!');
  }, 2000);

정말 예측이 불가능한 에러가 발생한 상황에서 에러 처리를 하려면?

process.on('uncaughtException',(err) => {

    console.error("예기치 못한 에러 발생", err);

 });

코드 작성 문제 2

const {
    Worker, isMainThread, parentPort,
  } = require('worker_threads');
  
  if (   ) { // 현재 스레드가 메인 스레드인지 아니면 워커 스레드인지 확인
    const worker = new Worker(__filename);
    worker.on('message', message => console.log('from worker', message));
    worker.on('exit', () => console.log('worker 종료'));
    // 워커 스레드에 'hello' 메세지 전달
  } else { 
    parentPort.on('message', (value) => {
      console.log('from parent', value);
      // 부모 스레드에 'I love you' 메세지 전달
      parentPort.close();
    });
}

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 종료'));
    worker.postMessage('hello');
  } else {
    parentPort.on('message', (value) => {
      console.log('from parent', value);
      parentPort.postMessage('I love you');
      parentPort.close();
    });
}

출처: 조현영 ,  『Node.js 교과서』 개정판 3판, 길벗, 3.5장 ~ 3.9장

Node.js #1

Editor : 7b은서

728x90

관련글 더보기