다음은 코드 예제 및 선택된 블로그 게시물의 인용문을 포함하여이 주제에 대한 여러 소스에서 요약 및 정리 한 것입니다. 모범 사례의 전체 목록은 여기에서 찾을 수 있습니다.
Node.JS 오류 처리 우수 사례
Number1 : 비동기 오류 처리에 약속 사용
TL; DR : 콜백 스타일에서 비동기 오류를 처리하는 것이 아마도 가장 빠른 방법 일 것입니다 (일명 운명의 피라미드). 코드에 줄 수있는 최고의 선물은 try-catch와 같이 매우 작고 친숙한 코드 구문을 제공하는 평판 좋은 약속 라이브러리를 사용하는 것입니다.
그렇지 않은 경우 : Node.JS 콜백 스타일, function (err, response)은 오류 코드와 캐주얼 코드, 과도한 중첩 및 어색한 코딩 패턴의 혼합으로 인해 유지 관리 할 수없는 코드에 유망한 방법입니다.
코드 예-양호
doWork()
.then(doWork)
.then(doError)
.then(doWork)
.catch(errorHandler)
.then(verify);
코드 예제 안티 패턴 – 콜백 스타일 오류 처리
getData(someParameter, function(err, result){
if(err != null)
//do something like calling the given callback function and pass the error
getMoreData(a, function(err, result){
if(err != null)
//do something like calling the given callback function and pass the error
getMoreData(b, function(c){
getMoreData(d, function(e){
...
});
});
});
});
});
블로그 인용구 : "약속에 문제가 있습니다."
(블로그 pouchdb에서 키워드 "노드 약속"으로 11 위)
"… 그리고 실제로 콜백은 훨씬 더 사악한 일을합니다. 스택은 우리가 프로그래밍 언어에서 당연한 것으로 간주하는 스택을 박탈합니다. 스택없이 코드를 작성하는 것은 브레이크 페달없이 자동차를 운전하는 것과 매우 흡사합니다. . 당신이 그것을 도달하고 거기 때까지 당신이 그것을 필요로하는지 심하게 모르고 약속의 요점은 우리가 비동기 갔을 때 우리가 잃어버린 언어 기본 백업 제공하는 것입니다. 반환, 던져, 스택을하지만 약속을 활용하려면 약속을 올바르게 사용하는 방법을 알아야합니다. "
Number2 : 기본 제공 오류 개체 만 사용
TL; DR : 오류를 문자열 또는 사용자 정의 유형으로 발생시키는 코드를 보는 것이 일반적입니다 . 이는 오류 처리 논리와 모듈 간의 상호 운용성을 복잡하게합니다. 약속 거부, 예외 발생 또는 오류 발생 여부 – Node.JS 내장 오류 개체를 사용하면 균일 성이 향상되고 오류 정보가 손실되지 않습니다.
그렇지 않은 경우 : 일부 모듈을 실행할 때 어떤 유형의 오류가 반환되는지 확실하지 않으면 향후 예외에 대해 추론하고 처리하기가 훨씬 어렵습니다. 심지어 가치를 정의하기 위해 사용자 정의 유형을 사용하면 스택 추적과 같은 중요한 오류 정보가 손실 될 수 있습니다!
코드 예-올바르게 수행
//throwing an Error from typical function, whether sync or async
if(!productToAdd)
throw new Error("How can I add new product when no value provided?");
//'throwing' an Error from EventEmitter
const myEmitter = new MyEmitter();
myEmitter.emit('error', new Error('whoops!'));
//'throwing' an Error from a Promise
return new promise(function (resolve, reject) {
DAL.getProduct(productToAdd.id).then((existingProduct) =>{
if(existingProduct != null)
return reject(new Error("Why fooling us and trying to add an existing product?"));
코드 예제 안티 패턴
//throwing a String lacks any stack trace information and other important properties
if(!productToAdd)
throw ("How can I add new product when no value provided?");
블로그 인용문 : "문자열은 오류가 아닙니다"
(블로그에서 "Node.JS 오류 객체"라는 키워드로 6 위를 차지했습니다)
"… 오류 대신 문자열을 전달하면 모듈 간의 상호 운용성이 줄어 듭니다. 오류 확인 인스턴스를 수행하거나 오류에 대해 더 알고 싶은 API와의 계약을 위반합니다 . 오류 개체는 다음과 같습니다. 메시지를 생성자에게 전달하는 것 외에도 최신 JavaScript 엔진의 흥미로운 속성 .. "
3 번 : 운영 오류와 프로그래머 오류 구별
TL : DR : 작업 오류 (예 : API에 유효하지 않은 입력이 수신 됨)는 오류 영향을 완전히 이해하고 신중하게 처리 할 수있는 알려진 사례를 나타냅니다. 반면에 프로그래머 오류 (예 : 정의되지 않은 변수를 읽으려고하는 경우)는 응용 프로그램을 정상적으로 다시 시작해야한다는 알 수없는 코드 오류를 나타냅니다.
그렇지 않은 경우 : 오류가 발생하면 항상 응용 프로그램을 다시 시작할 수 있지만 사소하고 예측 된 오류 (작동 오류)로 인해 최대 5000 명의 온라인 사용자가 중단되는 이유는 무엇입니까? 반대의 경우도 이상적이지 않습니다. 알 수없는 문제 (프로그래머 오류)가 발생했을 때 응용 프로그램을 유지하면 예기치 않은 동작이 발생할 수 있습니다. 두 가지를 차별화하면 전술적으로 행동하고 균형 잡힌 접근법을 적용 할 수 있습니다.
코드 예-올바르게 수행
//throwing an Error from typical function, whether sync or async
if(!productToAdd)
throw new Error("How can I add new product when no value provided?");
//'throwing' an Error from EventEmitter
const myEmitter = new MyEmitter();
myEmitter.emit('error', new Error('whoops!'));
//'throwing' an Error from a Promise
return new promise(function (resolve, reject) {
DAL.getProduct(productToAdd.id).then((existingProduct) =>{
if(existingProduct != null)
return reject(new Error("Why fooling us and trying to add an existing product?"));
코드 예-오류를 작동 가능으로 표시 (신뢰할 수있는)
//marking an error object as operational
var myError = new Error("How can I add new product when no value provided?");
myError.isOperational = true;
//or if you're using some centralized error factory (see other examples at the bullet "Use only the built-in Error object")
function appError(commonType, description, isOperational) {
Error.call(this);
Error.captureStackTrace(this);
this.commonType = commonType;
this.description = description;
this.isOperational = isOperational;
};
throw new appError(errorManagement.commonErrors.InvalidInput, "Describe here what happened", true);
//error handling code within middleware
process.on('uncaughtException', function(error) {
if(!error.isOperational)
process.exit(1);
});
블로그 인용구 : "그렇지 않으면 상태를 위험에 빠뜨립니다"(블로그에서 "Node.JS uncaught exception"키워드에 대해 순위가 3 인 블로그)
" ... JavaScript에서 throw가 작동하는 방식의 본질 상, 참조를 유출하거나 정의되지 않은 다른 취성 상태를 만들지 않고 안전하게"중지 된 곳을 "찾을 수있는 방법은 거의 없습니다. 던진 오류 프로세스를 종료하는 것입니다 . 물론, 정상적인 웹 서버에서, 당신은 많은 연결이 열려있을 수 있습니다, 그리고 오류가 다른 사람에 의해 촉발 되었기 때문에 갑자기 그 아래를 종료하는 것이 합리적이 아니다. 더 나은 접근 방법이다 "다른 사람들이 정상적인 시간을 끝내도록하고 그 작업자의 새 요청을 듣지 못하게하고 오류를 발생시킨 요청에 오류 응답을 보냅니다."
Number4 : 미들웨어를 통하지 않고 중앙에서 오류를 처리
TL; DR : 관리 대상 메일 및 로깅과 같은 오류 처리 논리는 오류가 발생할 때 모든 엔드 포인트 (예 : Express 미들웨어, 크론 작업, 단위 테스트)가 호출하는 중앙 집중식 전용 개체에 캡슐화되어야합니다.
그렇지 않으면 : 한 곳에서 오류를 처리하지 않으면 코드가 중복되어 잘못 처리 된 오류가 발생할 수 있습니다.
코드 예-일반적인 오류 흐름
//DAL layer, we don't handle errors here
DB.addDocument(newCustomer, (error, result) => {
if (error)
throw new Error("Great error explanation comes here", other useful parameters)
});
//API route code, we catch both sync and async errors and forward to the middleware
try {
customerService.addNew(req.body).then(function (result) {
res.status(200).json(result);
}).catch((error) => {
next(error)
});
}
catch (error) {
next(error);
}
//Error handling middleware, we delegate the handling to the centrzlied error handler
app.use(function (err, req, res, next) {
errorHandler.handleError(err).then((isOperationalError) => {
if (!isOperationalError)
next(err);
});
});
블로그 인용문 : "때때로 낮은 수준에서는 호출자에게 오류를 전파하는 것 외에는 유용한 기능을 수행 할 수 없습니다"(Joyent 블로그에서 키워드 "Node.JS 오류 처리")
"… 여러 레벨의 스택에서 동일한 오류를 처리하게 될 수 있습니다. 이는 하위 수준이 오류를 호출자에게 전파하여 오류를 호출자에게 전파하는 것 외에는 유용한 작업을 수행 할 수 없을 때 발생합니다. 작업을 다시 시도하거나 사용자에게 오류를보고하는 등의 방법으로 최상위 응답자 만 적절한 응답을 알고 있지만 모든 오류를 단일 최상위 수준으로보고하려고하는 것은 아닙니다. 콜백 자체는 어떤 컨텍스트에서 오류가 발생했는지 알 수 없기 때문에 "
Number5 : Swagger를 사용하여 문서 API 오류
TL; DR : API 호출자에게 오류가 발생할 수있는 오류를 알려주므로 충돌없이 오류를 신중하게 처리 할 수 있습니다. 일반적으로 Swagger와 같은 REST API 문서 프레임 워크를 사용하여 수행됩니다.
그렇지 않으면 : API 클라이언트는 이해할 수없는 오류를 수신했기 때문에 충돌 및 재시작을 결정할 수 있습니다. 참고 : API 호출자는 귀하 일 수 있습니다 (마이크로 서비스 환경에서 매우 일반적 임)
블로그 인용문 : "호출자에게 어떤 오류가 발생할 수 있는지 알려 주어야합니다"(Joyent 블로그에서 키워드 "Node.JS logging"에 대해 1 위)
… 오류를 처리하는 방법에 대해 이야기했지만 새 함수를 작성할 때 함수를 호출 한 코드에 오류를 어떻게 전달합니까? … 어떤 오류가 발생할 수 있는지 또는 무슨 뜻인지 모르는 경우 우연히 프로그램을 수정할 수 없습니다. 따라서 새로운 함수를 작성하는 경우 호출자에게 어떤 오류가 발생할 수 있고 어떤 조치를 취해야하는지 알려야합니다.
6 번 : 낯선 사람이 마을에 올 때 프로세스를 정상적으로 종료하십시오
TL; DR : 알 수없는 오류가 발생하면 (개발자 오류, 모범 사례 번호 3 참조)-응용 프로그램 상태에 대한 불확실성이 있습니다. 일반적으로 Forever 및 PM2와 같은 '다시 시작'도구를 사용하여 프로세스를 신중하게 다시 시작하는 것이 좋습니다.
그렇지 않은 경우 : 익숙하지 않은 예외가 발견되면 일부 오브젝트에 결함이있는 상태 (예 : 전역 적으로 사용되며 일부 내부 장애로 인해 더 이상 이벤트를 발생시키지 않는 이벤트 이미 터)에있을 수 있으며 이후의 모든 요청이 실패하거나 미치게 작동 할 수 있습니다.
코드 예-충돌 여부 결정
//deciding whether to crash when an uncaught exception arrives
//Assuming developers mark known operational errors with error.isOperational=true, read best practice #3
process.on('uncaughtException', function(error) {
errorManagement.handler.handleError(error);
if(!errorManagement.handler.isTrustedError(error))
process.exit(1)
});
//centralized error handler encapsulates error-handling related logic
function errorHandler(){
this.handleError = function (error) {
return logger.logError(err).then(sendMailToAdminIfCritical).then(saveInOpsQueueIfCritical).then(determineIfOperationalError);
}
this.isTrustedError = function(error)
{
return error.isOperational;
}
블로그 인용문 : "오류 처리에 관한 세 가지 생각 학교가있다"(블로그 jsrecipes에서)
… 오류 처리에 대한 세 가지 생각 학교는 주로 다음과 같습니다. 1. 응용 프로그램이 중단 된 후 다시 시작합니다. 2. 가능한 모든 오류를 처리하고 충돌하지 마십시오. 3. 둘 사이의 균형 잡힌 접근
Number7 : 성숙한 로거를 사용하여 오류 가시성 향상
TL; DR : Winston, Bunyan 또는 Log4J와 같은 성숙한 로깅 도구 세트는 오류 발견 및 이해 속도를 높입니다. 따라서 console.log는 잊어 버리십시오.
그렇지 않은 경우 : console.logs를 통해 감추거나 쿼리 도구 나 적절한 로그 뷰어를 사용하지 않고 지저분한 텍스트 파일을 통해 수동으로 늦게까지 업무에 바쁠 수 있습니다
코드 예-작동중인 Winston 로거
//your centralized logger object
var logger = new winston.Logger({
level: 'info',
transports: [
new (winston.transports.Console)(),
new (winston.transports.File)({ filename: 'somefile.log' })
]
});
//custom code somewhere using the logger
logger.log('info', 'Test Log Message with some parameter %s', 'some parameter', { anything: 'This is metadata' });
블로그 인용문 : "로거의 몇 가지 요구 사항을 식별 할 수 있습니다."(블로그 strongblog에서)
… 로거의 몇 가지 요구 사항을 식별 할 수 있습니다. 1. 각 로그 라인에 타임 스탬프가 있습니다. 이것은 매우 자명 한 것으로 각 로그 항목이 언제 발생했는지 알 수 있어야합니다. 2. 로깅 형식은 기계는 물론 사람도 쉽게 소화 할 수 있어야합니다. 3. 여러 구성 가능한 대상 스트림을 허용합니다. 예를 들어, 추적 로그를 하나의 파일에 쓰는 중일 수 있지만 오류가 발생하면 같은 파일에 쓴 다음 오류 파일에 쓰고 동시에 전자 메일을 보냅니다.
Number8 : APM 제품을 사용한 오류 및 다운 타임 발견
TL; DR : 모니터링 및 성능 제품 (일명 APM)은 코드베이스 또는 API를 사전에 측정하여 누락 된 오류, 충돌 및 느린 부분을 자동으로 강조 표시 할 수 있습니다.
그렇지 않으면 : API 성능 및 다운 타임을 측정하는 데 많은 노력을 기울일 수 있습니다 . 실제 시나리오에서 가장 느린 코드 부분과 이것이 UX에 어떤 영향을 미치는지 알지 못할 것입니다.
블로그 인용 : "APM 제품 세그먼트"(블로그에서 Yoni Goldberg)
"… APM 제품은 3 가지 주요 부문으로 구성됩니다. 1. 웹 사이트 또는 API 모니터링 – HTTP 요청을 통해 가동 시간 및 성능을 지속적으로 모니터링하는 외부 서비스입니다. 몇 분 안에 설정할 수 있습니다. Pingdom, Uptime Robot 및 New Relic
2 코드 계측 – 기능이 느린 코드 감지, 예외 통계, 성능 모니터링 등을 위해 애플리케이션 내에 에이전트를 포함해야하는 제품군 : New Relic, App Dynamics
3. 운영 인텔리전스 대시 보드 –이 제품군은 애플리케이션 성능을 쉽게 유지하는 데 도움이되는 메트릭 및 선별 된 컨텐츠로 운영 팀을 용이하게하는 데 중점을 둡니다. 여기에는 일반적으로 여러 정보 소스 (응용 프로그램 로그, DB 로그, 서버 로그 등)와 선행 대시 보드 디자인 작업이 집계됩니다. 다음은 선정 된 경쟁자입니다. Datadog, Splunk "
위의 단축 버전입니다- 더 많은 모범 사례 및 예제를 참조하십시오.