시퀄라이즈 orm의 API는 대부분 promise를 반환한다.
내 프로젝트 코드에는 프로미스 에러처리가 하나도 되어있지 않았었다.
때문에, 비동기 함수가 에러를 던지는 경우 적절한 응답을 반환하지 않거나 서버가 죽어버렸다.
스택오버플로우에 express의 비동기 에러처리를 검색했고, 가장 많은 추천을 받은 두가지 해결법을 적절히 섞어서 적용했다.
https://stackoverflow.com/questions/51391080/handling-errors-in-express-async-middleware
처음 적용한 방법은 에러 핸들링 모듈이다.
사용방법이 간단하지만 내가 원하는 비동기 에러처리에 특화된 모듈이어서 가장 먼저 적용해보았다.
app.use((err, req, res, next) => {
console.error(err.message);
res.sendStatus(500);
});
에러를 받는 미들웨어를 app을 생성한 파일에 작성해준다.
import "express-async-errors"
비동기 에러 발생이 예상되는 파일에서 express-async-errors
를 import 해준다.
여기까지 완료하면 비동기 에러가 발생했을 때 던져진 에러는 에러처리 미들웨어에 잡혀 처리가 된다.
방법1은 적용하기 간편한 것이 장점이지만 각각의 에러를 하나의 미들웨어로 밖에 관리하지 못한다는 단점이 있다.
따라서 예측이 가능한 에러는 try...catch
문을 사용해서 대응코드를 작성해주거나 에러 상황을 던지도록 했다.
던져진 에러는 express-async-errors가 최종 에러처리 미들웨어로 전달하고 에러 로그가 기록이 된다. 이 방식은 로그를 어떻게 남기면 되는지에 대해 정리된 블로그를 참고한 중앙집중식 핸들링 방식을 적용한 것이다.
중앙집중식 핸들링 방식을 사용하면 반복적인 코드 작성을 피할 수 있고 공통적인 에러 처리를 한번에 관리할 수 있다. 예를 들어 모든 에러처리가 콘솔에 로그를 기록하고 있었는데 콘솔 로그의 비효율성을 해결하기 위해 winston모듈로 교체했을 때 모든 try...catch
문을 찾아다니며 코드 변경을 할 필요가 없다는 것이다.
실제로 나는 로그를 console
을 사용하다가 winston모듈 사용에 대해 정리한 블로그를 참고해서 winston모듈로 교체하여 기록했다. nodeJS의 console
API는 log와 error가 콘솔에 찍히는 것에 큰 차이가 없어서 분간이 어려웠고, 실제 서버의 콘솔에는 로그를 찍지 않을 것이기 때문에 파일로 기록하는 코드를 직접 짜야했다.
반면, winston은 format.colorize()
로 로그의 레벨에 따라 출력되는 로그에 구분 색칠을 해주고 winston-daily-rotate-file
모듈을 함께 사용해서 레벨에 따른 기록 파일을 생성하고 유효기간을 설정해 줄 수 있다. 이외에도 로그와 관련된 편리하고 유용한 기능이 많아서 winston을 사용하기로 결정했다.
모든 비동기 함수에 에러처리를 작성하면 프로그램 가독성이 떨어질 수 있다.
오류가 발생할 수 있을 것이라고 가정되는 코드는 try
문의 시작과 끝에 작성하고 오류가 발생하지 않는다고 가정할 수 있을 코드는 시작과 끝의 가운데에 배치해서 try...catch
가 어떤 함수의 오류를 대응하는지 확인하기 쉽도록 한다.
catch
문에서는 에러 핸들러에게 던지는 오류가 어디서 발생했는지 알기 쉽도록 실패한 연산이나 유형의 이름을 함께 던져준다.
d.ts
파일을 확인해보니 에러는 위와같이 이름과 메시지를 필수 프로퍼티로 가지고있다.
에러를 템플릿 리터럴로 문자열에 포함시켜 출력하면 name : message
형태로 출력이 되었다.
나는 이것을 이용해서 catch
가 에러를 잡고 새로운 에러를 중앙으로 던질 때, 템플릿 리터럴 안에 에러상황과 에러를 함께 넣어서 던져주었고 결과적으로 다음과 같이 콘솔에 출력을 시킬 수 있었다.