본문 바로가기
JavaScript/Node.js

Node) 노드 기능 알아보기 - 이벤트 이해하기, 예외 처리하기

by 박채니 2022. 12. 27.
안녕하세요, 코린이의 코딩 학습기 채니 입니다.
[Node.js 교과서]의 책을 참고하여 포스팅한 개인 공부 내용입니다.

 

노드 기능 알아보기

 

이벤트 이해하기

 

event.js

const EventEmitter = require("events");

const myEvent = new EventEmitter();

myEvent.addListener("event1", () => {
  console.log("이벤트1");
});

myEvent.on("event2", () => {
  console.log("이벤트2");
});

myEvent.on("event2", () => {
  console.log("이벤트2 추가");
});

// 한번만 실행
myEvent.once("event3", () => {
  console.log("이벤트3");
});

//이벤트 호출
myEvent.emit("event1");
myEvent.emit("event2");

myEvent.emit("event3");
myEvent.emit("event3"); // 실행 안됨

myEvent.on("event4", () => {
  console.log("이벤트4");
});
myEvent.removeAllListeners("event4");
myEvent.emit("event4"); // 실행 안됨

const listener = () => {
  console.log("이벤트5");
};
myEvent.on("event5", listener);
myEvent.removeListener("event5", listener); // 반드시 listener 넣어야함
myEvent.emit("event5"); // 실행 안됨

console.log(myEvent.listenerCount("event2"));

@콘솔출력값
$ node part3/event

이벤트1
이벤트2
이벤트2 추가
이벤트3
2

 

  • on(이벤트명, 콜백)
    : 이벤트 이름과 이벤트 발생 시 콜백 연결 (이벤트 리스닝)
    이벤트 하나에 이벤트 여러 개를 달아줄 수도 있음
  • addListener(이벤트명, 콜백)
    : on과 동일한 기능
  • emit(이벤트명)
    : 이벤트 호출하는 메소드, 이벤트 이름을 인수로 넣으면 등록해둔 이벤트 콜백 실행
  • once(이벤트명, 콜백)
    : 한 번만 실행되는 이벤트
  • removeAllListeners(이벤트명)
    : 이벤트에 연결된 모든 이벤트 리스너 제거
  • removeListener(이벤트명, 리스너)
    : 이벤트에 연결된 리스너를 하나씩 제거, 반드시 리스너를 넣어야함
  • off(이벤트명, 콜백)
    : removeListener와 동일한 기능
  • listenerCount(이벤트명)
    : 현재 리스너가 몇 개 연결되어 있는지 확인

 


예외 처리하기

- 예외는 처리하지 못한 에러를 의미 → 실행 중인 노드 프로세스를 멈추게 함

- 에러 로그가 기록되더라도 작업은 계속 진행 되어야 함

 

error1.js

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

@콘솔출력값
$ node part3/error1

시작
Error: 서버를 고장내주마
    at Timeout._onTimeout (/Users/parkchaeeun/project/playground-cepark/part3/error1.js:4:11)
    at listOnTimeout (node:internal/timers:564:17)
    at process.processTimers (node:internal/timers:507:7)
시작
Error: 서버를 고장내주마
    at Timeout._onTimeout (/Users/parkchaeeun/project/playground-cepark/part3/error1.js:4:11)
    at listOnTimeout (node:internal/timers:564:17)
    at process.processTimers (node:internal/timers:507:7)
... 반복

setInterval를 이용해 프로세스가 멈추는 지 확인해보니, 에러가 발생해도 멈추지 않고 try/catch로 에러를 잡아 정상 실행되는 것을 확인할 수 있습니다.

 

error2.js

const fs = require("fs"); // 파일 시스템에 접근하는 모듈

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

@콘솔출력값
$ node part3/error2

[Error: ENOENT: no such file or directory, unlink './abcdefg.js'] {
  errno: -2,
  code: 'ENOENT',
  syscall: 'unlink',
  path: './abcdefg.js'
}
[Error: ENOENT: no such file or directory, unlink './abcdefg.js'] {
  errno: -2,
  code: 'ENOENT',
  syscall: 'unlink',
  path: './abcdefg.js'
}
... 반복

fs.unlink로 존재하지 않는 파일을 삭제하고 있습니다.

에러가 발생하지만 노드 내장 모듈의 에러는 실행 중인 프로세스를 멈추게 하지 않아 정상 실행 되는 것을 확인할 수 있습니다.\\

 

error3.js

const fs = require("fs").promises;

setInterval(() => {
  fs.unlink("./abcdefg.js").catch(console.error);
}, 1000);

@콘솔출력값
$ node part3/error3

[Error: ENOENT: no such file or directory, unlink './abcdefg.js'] {
  errno: -2,
  code: 'ENOENT',
  syscall: 'unlink',
  path: './abcdefg.js'
}
[Error: ENOENT: no such file or directory, unlink './abcdefg.js'] {
  errno: -2,
  code: 'ENOENT',
  syscall: 'unlink',
  path: './abcdefg.js'
}

프로미스의 에러는 반드시 catch 해야합니다. 그렇지 않으면 에러와 함께 노드 프로세스가 종료됩니다.

프로미스의 에러를 catch로 잡으니 에러는 발생 되지만 정상 실행 되는 것을 확인할 수 있습니다.

 

error4.js

process.on("uncaughtException", (err) => {
  console.error("예기치 못한 에러", err);
});

setInterval(() => {
  throw new Error("서버를 고장내주마~");
}, 1000);

setInterval(() => {
  console.log("실행됩니다.");
}, 2000);

@콘솔출력값
$ node part3/error4

예기치 못한 에러 Error: 서버를 고장내주마~
    at Timeout._onTimeout (/Users/parkchaeeun/project/playground-cepark/part3/error4.js:6:9)
    at listOnTimeout (node:internal/timers:564:17)
    at process.processTimers (node:internal/timers:507:7)
실행됩니다.
예기치 못한 에러 Error: 서버를 고장내주마~
    at Timeout._onTimeout (/Users/parkchaeeun/project/playground-cepark/part3/error4.js:6:9)
    at listOnTimeout (node:internal/timers:564:17)
    at process.processTimers (node:internal/timers:507:7)
예기치 못한 에러 Error: 서버를 고장내주마~
    at Timeout._onTimeout (/Users/parkchaeeun/project/playground-cepark/part3/error4.js:6:9)
    at listOnTimeout (node:internal/timers:564:17)
    at process.processTimers (node:internal/timers:507:7)
실행됩니다.
... 반복

process 객체에 uncaughtException 이벤트 리스너를 달았고, 처리하지 못한 에러가 발생 했을 때 이벤트 리스너가 실행되고 프로세스가유지되는 것을 확인할 수 있습니다.

try/catch로 에러를 잡지 않아도 코드가 제대로 실행되었습니다.

 

uncaughtException 이벤트 리스너로 모든 에러를 처리할 수 있을 것 같지만, 공식 문서에는 해당 방법을 최후의 수단으로 사용하라고 합니다.

해당 이벤트 발생 후 다음 동작이 제대로 동작하는지를 보증하지 않기 때문입니다.

따라서 uncaughtException은 에러 내용을 기록하는 정도로 사용하고 process.exit()로 프로세스를 종료하는 것이 좋다고 합니다.

 

자주 발생하는 에러들

 

  • node: command not found
    : 노드를 설치했지만, 해당 에러가 발생하는 경우 환경 변수가 제대로 설정되어 있지 않은 것
    환경 변수에는 노드가 설치된 경로가 포함되어야 하고, node 외 다른 명령어도 마찬가지!
  • ReferenceError: 모듈 is not defined
    : 모듈을 require 했는지 확인
  • Error [ERR_MODULE_NOT_FOUNT]
    : 존재하지 않는 모듈을 불러오려 할 때 발생
  • Error: Can't set headers after they are sent
    : 요청에 대한 응답을 보낼 때 응답을 두 번 이상 보낼 때 발생 (요청에 대한 응답은 한 번만 보내야 함)
    응답을 보내는 메소드를 두 번 이상 사용하지 않았는지 확인
  • FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory
    : 코드를 실행할 때 메모리가 부족해서 스크립트가 정상적으로 작동하지 않는 경우 발생
    코드는 정상이지만 노드가 활요할 수 있는 메모리가 부족한 경우, 노드의 메모리를 늘릴 수 있음
  • UnhandledPromiseRejectionWarning: Unhandled promise rejection
    : 프로미스 사용 시 catch 메소드를 붙이지 않을 때 발생
  • EADDRINUSE 포트번호
    : 해당 포트번호에 이미 다른 프로세스가 연결되어있을 때 발생
    해당 프로세스를 종료하거나 다른 포트 번호 사용
  • EACCES 또는 EPERM
    : 노드가 작업을 수행하는 데 권한이 충분하지 않을 때 발생
    파일/폴더 수정, 삭제, 생성 권한을 확인하거나 맥이나 리눅스라면 명령어 앞에 sudo를 붙이는 것도 방법
  • EJSONPARSE
    : package.json등의 JSON 파일에 문법 오류가 있을 때 발생
  • ECONNREFUSED
    : 요청을 보냈으나 연결이 성립하지 않을 때 발생
  • ETARGET
    : package.json에 기록한 패키지 버전이 존재하지 않을 때 발생
  • ETIMEOUT
    : 요청을 보냈으나 응답이 시간 내에 오지 않을 때 발생
  • ENOENT: no such file or directory
    : 지정한 폴더나 파일이 존재하지 않는 경우 발생