예외 처리하기
노드에서는 예외 처리가 정말 중요합니다. 예외란 보통 처리하지 못한 에러를 가리킵니다. 이러한 예외들은 살행 중인 노드 프로세스를 멈추게 만듭니다.
멀티 스레드 프로그램에서는 스레드 하나가 멈추면 그 일을 다른 스레드가 대신합니다. 하지만 노드의 메인 스레드는 하나뿐이므로 그 하나를 소중히 보호해야 합니다. 메인 스레드가 에러로 인해 멈춘다고 것은 스레드를 갖고 있는 프로세스가 멈춘다는 뜻이고, 전체 서버도 멈춘다는 뜻과 같습니다. 아무리 신중을 가해 만들었다고 해도 항상 예기치 못한 에러는 발생하는 법입니다.
error.js
setInterval(() => {
console.log("시작");
try {
throw new Error("서버를 고장내주마!");
} catch (err) {
console.error(err);
}
}, 1000);
setInterval을 사용한 것은 프로세스가 멈추는지 여부를 체크하기 위해서입니다. 프로세스가 에러로 인해 멈추면 setInterval도 멈출 것입니다. setInterval 내부에 throw new Error()를 써서 에러를 강제로 발생시켰습니다.
콘솔
$ node error1
시작
Error: 서버를 고장내주마!
...
시작
Error: 서버를 고장내주마!
...
// 계속 반복
에러는 발생하지만 try/catch로 잡을 수 있고 setInterval도 직접 멈추기 전(Ctrl + C)까지 계속 실행됩니다. 이렇게 에러가 발생할 것 같은 부분을 미리 try/catch로 감싸면 됩니다.
error2.js
const fs = require("fs");
setInterval(() => {
fs.unlink("./abcdefg/js", (err) => {
if (err) {
console.error(err);
}
});
}, 1000);
fs.unlink로 존재하지 않는 파일을 지우고 있습니다. 에러가 발생하지만 다행히 노드 내장 모듈의 에러는 실행 중인 프로세스를 멈추지 않습니다. 에러 로그를 기록해두고 나중에 원인을 찾아 수정하면 됩니다.
$ node error2
[Error: ENOENT: no such file or directory, unlink 'D:\공부\Nodejs\3-8\abcdefg\js'] {
errno: -4058,
code: 'ENOENT',
syscall: 'unlink',
path: 'D:\\공부\\Nodejs\\3-8\\abcdefg\\js'
}
[Error: ENOENT: no such file or directory, unlink 'D:\공부\Nodejs\3-8\abcdefg\js'] {
errno: -4058,
code: 'ENOENT',
syscall: 'unlink',
path: 'D:\\공부\\Nodejs\\3-8\\abcdefg\\js'
}
// 계속 반복
프로미스의 에러는 catch하지 않아도 알아서 처리합니다.
error3.js
const fs = require("fs").promises;
setInterval(() => {
fs.unlink("./abcdefg.js");
}, 1000);
콘솔
$ node error3
node:internal/fs/promises:1064
return await PromisePrototypeThen(
^
Error: ENOENT: no such file or directory, unlink 'D:\공부\Nodejs\3-8\abcdefg.js'
at async Object.unlink (node:internal/fs/promises:1064:10) {
errno: -4058,
code: 'ENOENT',
syscall: 'unlink',
path: 'D:\\공부\\Nodejs\\3-8\\abcdefg.js'
}
Node.js v22.13.0
다만 프로미스의 에러를 알아서 처리하는 동작은 노드 버전이 올라감에 따라 바뀔 수 있습니다. 따라서 프로미스를 사용할 때는 항상 catch를 붙여주는 것을 권장합니다.
error4.js
process.on("uncaughtException", (err) => {
console.error("예기치 못한 에러", err);
});
setInterval(() => {
throw new Error("서버를 고장내주마!");
}, 1000);
setTimeout(() => {
console.log("실행됩니다");
}, 2000);
process 객체에 uncaughtException 이벤트 리스너를 달았습니다. 처리하지 못한 에러가 발생했을 때 이벤트 리스너가 실행되고 프로세스가 유지됩니다. 이 부분이 없다면 위 예제에서는 setTimeout이 실행되지 않습니다. 실행 후 1초만에 setInterval에서 에러가 발생하여 프로세스가 멈추기 때문입니다. 하지만 uncaughtException 이벤트 리스너가 연결되어 있으므로 프로세스가 멈추지 않습니다.
콘솔
$ node error4
예기치 못한 에러 Error: 서버를 고장내주마!
...
실행됩니다
예기치 못한 에러 Error: 서버를 고장내주마!
예기치 못한 에러 Error: 서버를 고장내주마!
// 계속 반복
try/catch로 처리하지 못한 에러가 발생했지만 코드가 제대로 실행하지만 코드가 제대로 실행되었습니다.
uncaughtException은 단순히 에러 내용을 기록하는 정도로 사용하고, 에러를 기록한 후 process.exit()으로 프로세스를 종료하는 것이 좋습니다. 에러가 발생하는 코드를 수정하지 않는 이상, 프로세스가 실행되는 동안 에러는 계속 발생할 것입니다.
서버 운영은 에러와의 싸움입니다. 모든 에러 상황에 대비하는 것이 최선이지만, 시간이나 비용, 인력 등의 제약으로 미처 대비하지 못한 에러가 발생할 수 있습니다. 따라서 에러 발생 시 철저히 기록(로깅)하는 습관을 들이고, 주기적으로 로그를 확인하면서 보완해나가야 합니다.
'프로그래밍 언어 > NODE JS' 카테고리의 다른 글
| 요청과 응답 이해하기 (0) | 2025.05.16 |
|---|---|
| 자주 발생하는 에러들 (0) | 2025.05.13 |
| 이벤트 이해하기 (0) | 2025.05.07 |
| 스레드 풀 알아보기 (0) | 2025.05.04 |
| 기타 fs 메서드 알아보기 (0) | 2025.05.01 |