worker_threads
노드에서 멀티 스레스 방식으로 작업하는 방법
child_process
노드에서 다른 프로그램을 실행하고 싶거나 명령어를 수행하고 싶을때 사용하는 모듈입니다.
이 모듈을 통해 다른언어의 코드(파이썬등)를 실해앟고 결괏값을 받을 수 있습니다.
현재 노드 프로세스 외에 새로운 프로세스를 띄워서 명령을 수행하고 노드 프로세스에 결과를 알려줍니다.
파일 시스템 접근하기
fs모듈을 파일 시스템에 접근하는 모듈입니다.
파일을 생성하거나 삭제하고, 읽거나 쓸 수 있고, 폴더도 만들더나 지울 수 있습니다.
fs는 기본적으로 콜백형식의 모듈이라 실무에서 사용하기 불편합니다.
따라서 fs 모듈을 프로미스 형식으로 바꿔주는 방식을 사용합니다.
const fs = require('fs').promises;
fs.readFile('./readme.txt')
.then((data) => {
console.log(data);
console.log(data.toString());
})
.catch((err) => {
console.error(err);
});
이번에는 파일을 만들어 보겠습니다.
const fs = require('fs');
fs.writeFile('./writeme.txt', '글이 입력됩니다', (err) => {
if (err) {
throw err;
}
fs.readFile('./writeme.txt', (err, data) => {
if (err) {
throw err;
}
console.log(data.toString());
});
});
$ node writeFile
글이 입력됩니다.
동기 메서드와 비동기 메서드
setTimeout같은 타이머와 process.nextTick외에도 노드는 대부분의 메서드를 비동기 방식으로 처리합니다.
fs같은 모듈들은 동기방식으로 사용할 수 있는 메서드를 많이 가지고 있습니다.
동기와 비동기, 블로킹과 논블로킹
• 동기와 비동기: 백그라운드 작업 완료 확인 여부
• 블로킹과 논블로킹: 함수가 바로 return되는지 여부
노드에서는 동기-블로킹 방식과 비동기-논블로킹 방식이 대부분입니다. 동기-논블로킹이나 비동기-블로킹은 없다고 봐도 됩니다.
동기-블로킹 방식에서는 백그라운드 작업 완료 여부를 계속 확인하며, 호출한 함수가 바로 return되지 않고 백그라운드 작업이 끝나야 return합니다.
비동기-논블로킹 방식에서는 호출한 함수가 바로 return되어 다음 작업으로 넘어가고, 백그라운드 작업 완료 여부는 신경 쓰지 않고 나중에 백그라운드가 알림을 줄 때 비로소 처리합니다.
버퍼와 스트림
파일을 읽거나 쓰는 방식에는 두가지 방식이 있습니다.(버퍼, 스트림)
Buffer객체
from(문자열): 문자열을 버퍼로 바꿀 수 있습니다. length 속성은 버퍼의 크기를 알립니다. 바이트 단위입니다.
toString(버퍼): 버퍼를 다시 문자열로 바꿀 수 있습니다. 이때 base64나 hex를 인수로 넣으면 해당 인코딩으로도 변환 가능합니다.
concat(배열): 배열 안에 든 버퍼들을 하나로 합칩니다.
alloc(바이트): 빈 버퍼를 생성합니다. 바이트를 인수로 넣으면 해당 크기의 버퍼가 생성됩니다.
메모리 문제가 발생할 수도 있고, 모든내용을 다 쓴 후에야 다음 동작으로 넘어가 파일읽기, 압축, 파일쓰기등의 조작을 연달아 할때
전체용량을 버퍼로 처리해야 다음단계로 넘어갈 수 있습니다.
그래서 버퍼의 크기를 작게 만들고 여러 번에 걸쳐 나눠보내는 방식이 등장했습니다.
버퍼 1MB를 만든 후 100MB파일을 100번에 걸쳐 나눠 보내는 것입니다.
메모리 1MB로 100MB파일을 전송할 수 있습니다. 이를 편리하게 만든 것이 스트림입니다.
파일을 읽는 스트림 메서드로는 createReadStream이 있습니다.
스트림끼리 연결하는 것을 파이핑한다라고 표현하는데 pipe메서드가 있습니다.
pipe는 스트림 사이에 여러번 연결할 수 있습니다.
스레드 풀
fs메서드를 여러 번 실행해도 백그라운드에서 동시에 처리되는데, 스레드 풀이 있기때문입니다.
스레드 풀은 직접 컨트롤 할 수 없지만 개수는 조절할 수 잇습니다.
이벤트
• on(이벤트명, 콜백): 이벤트 이름과 이벤트 발생 시의 콜백을 연결합니다. 이렇게 연결하는 동작을 이벤트 리스닝이라고 합니다. event2처럼 이벤트 하나에 이벤트 여러 개를 달아줄 수도 있습니다.
• addListener(이벤트명, 콜백): on과 기능이 같습니다.
• emit(이벤트명): 이벤트를 호출하는 메서드입니다. 이벤트 이름을 인수로 넣으면 미리 등록해뒀던 이벤트 콜백이 실행됩니다.
• once(이벤트명, 콜백): 한 번만 실행되는 이벤트입니다. myEvent.emit('event3')을 두 번 연속 호출했지만 콜백이 한 번만 실행됩니다.
• removeAllListeners(이벤트명): 이벤트에 연결된 모든 이벤트 리스너를 제거합니다. event4가 호출되기 전에 리스너를 제거했으므로 event4의 콜백은 호출되지 않습니다.
• removeListener(이벤트명, 리스너): 이벤트에 연결된 리스너를 하나씩 제거합니다. 리스너를 넣어야 한다는 것을 잊지 마세요. 역시 event5의 콜백도 호출되지 않습니다.
• off(이벤트명, 콜백): 노드 10 버전에서 추가된 메서드로, removeListener와 기능이 같습니다.
• listenerCount(이벤트명): 현재 리스너가 몇 개 연결되어 있는지 확인합니다.
예외처리
노드 16 부터 프로미스의 에러는 반드시 catch해야합니다.
catch하지 않으면 에러와 함께 노드 프로세스가 종료됩니다.
자주 발생하는 에러
• node: command not found: 노드를 설치했지만 이 에러가 발생하는 경우는 환경 변수가 제대로 설정되어 있지 않은 것입니다. 환경 변수에는 노드가 설치된 경로가 포함되어야 합니다. node 외의 다른 명령어도 마찬가지입니다. 그 명령어를 수행할 수 있는 파일이 환경 변수에 들어 있어야 명령어를 콘솔에서 사용할 수 있습니다.
• ReferenceError: 모듈 is not defined: 모듈을 require했는지 확인합니다.
• Error: Cannot find module 모듈명: 해당 모듈을 require했지만 설치하지 않았습니다. npm i 명령어로 설치하세요.
• Error [ERR_MODULE_NOT_FOUND]: 존재하지 않는 모듈을 불러오려 할 때 발생합니다.
• Error: Can't set headers after they are sent: 요청에 대한 응답을 보낼 때 응답을 두 번 이상 보냈습니다. 요청에 대한 응답은 한 번만 보내야 합니다. 응답을 보내는 메서드를 두 번 이상 사용하지 않았는지 체크해보세요.
• FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed- JavaScript heap out of memory: 코드를 실행할 때 메모리가 부족해서 스크립트가 정상적으로 작동하지 않는 경우입니다. 코드가 잘못 구현되었을 확률이 높으므로 코드를 점검해보세요. 만약 코드는 정상이지만 노드가 활용할 수 있는 메모리가 부족한 경우라면 노드의 메모리를 늘릴 수 있습니다. 노드를 실행할 때 node --max-old-space-size=4096 파일명과 같은 명령어를 사용하면 됩니다. 4096은 4GB를 의미합니다. 여기에 원하는 용량을 적으면 됩니다.
• UnhandledPromiseRejectionWarning: Unhandled promise rejection: 프로미스 사용 시 catch 메서드를 붙이지 않으면 발생합니다. 항상 catch를 붙여 에러가 발생하는 상황에 대비하세요.
• EADDRINUSE 포트 번호: 해당 포트 번호에 이미 다른 프로세스가 연결되어 있습니다. 그 프로세스는 노드 프로세스일 수도 있고 다른 프로그램일 수도 있습니다. 그 프로세스를 종료하거나 다른 포트 번호를 사용해야 합니다.
• EACCES 또는 EPERM: 노드가 작업을 수행하는 데 권한이 충분하지 않습니다. 파일/폴더 수정, 삭제, 생성 권한을 확인해보는 것이 좋습니다. 맥이나 리눅스 운영체제라면 명령어 앞에 sudo를 붙이는 것도 방법입니다.
• EJSONPARSE: package.json 등의 JSON 파일에 문법 오류가 있을 때 발생합니다. 자바스크립트 객체와는 형식이 조금 다르니 쉼표 같은 게 빠지거나 추가되지는 않았는지 확인해보세요.
• ECONNREFUSED: 요청을 보냈으나 연결이 성립하지 않을 때 발생합니다. 요청을 받는 서버의 주소가 올바른지, 서버가 꺼져 있지는 않은지 등을 확인해봐야 합니다.
• ETARGET: package.json에 기록한 패키지 버전이 존재하지 않을 때 발생합니다. 해당 버전이 존재하는지 확인하세요.
• ETIMEOUT: 요청을 보냈으나 응답이 시간 내에 오지 않을 때 발생합니다. 역시 요청을 받는 서버의 상태를 점검해봐야 합니다.
• ENOENT: no such file or directory: 지정한 폴더나 파일이 존재하지 않는 경우입니다. 맥이나 리눅스 운영체제에서는 대소문자도 구별하므로 확인해봐야 합니다.