이 README에는 JavaScript 오류 처리, 서버에 보고, 이 모든 것을 정말 어렵게 만들 수 있는 많은 버그 탐색에 대해 수년 동안 배운 정보가 포함되어 있습니다. 브라우저는 이 영역에서 개선되었지만 모든 애플리케이션이 발생하는 모든 오류를 분별 있고 건전하게 처리할 수 있도록 개선할 여지가 여전히 남아 있습니다.
이 가이드에 있는 콘텐츠에 대한 테스트 사례는 https://mknichel.github.io/javascript-errors/에서 확인할 수 있습니다.
목차
소개
JavaScript 오류 분석
JavaScript 오류 생성
오류 메시지
스택 추적 형식
JavaScript 오류 잡기
window.onerror
시도/잡기
보호된 진입점
약속
웹 작업자
크롬 확장 프로그램
오류 포착, 보고 및 수정은 애플리케이션의 상태와 안정성을 보장하기 위한 모든 애플리케이션의 중요한 부분입니다. JavaScript 코드는 클라이언트 및 다양한 브라우저 환경에서도 실행되므로 애플리케이션에서 JS 오류를 파악하는 것도 어려울 수 있습니다. 각 브라우저의 구현에 차이를 일으키는 JS 오류를 보고하는 방법에 대한 공식적인 웹 사양은 없습니다. 또한 브라우저의 JavaScript 오류 구현에도 많은 버그가 있어 이를 더욱 어렵게 만들었습니다. 이 페이지에서는 미래의 개발자가 오류를 더 잘 처리할 수 있고 브라우저가 표준화된 솔루션에 수렴할 수 있도록 JS 오류의 이러한 측면을 탐색합니다.
JavaScript 오류는 오류 메시지 와 스택 추적이라는 두 가지 주요 부분으로 구성됩니다. 오류 메시지는 무엇이 잘못되었는지 설명하는 문자열이고, 스택 추적은 코드에서 오류가 발생한 위치를 설명합니다. JS 오류는 브라우저 자체에서 생성되거나 애플리케이션 코드에서 발생할 수 있습니다.
JS 오류는 코드 조각이 제대로 실행되지 않을 때 브라우저에서 발생하거나 코드에서 직접 발생할 수 있습니다.
예를 들어:
var a = 3;a();
이 예에서는 실제로 숫자인 변수를 함수로 호출할 수 없습니다. 브라우저는 TypeError: a is not a function
.
개발자는 특정 전제 조건이 충족되지 않으면 코드 조각에 오류가 발생하기를 원할 수도 있습니다. 예를 들어
if (!checkPrecondition()) { throw new Error("전제조건을 충족하지 않습니다!");}
이 경우 오류는 Error: Doesn't meet precondition!
. 이 오류에는 해당 라인을 가리키는 스택 추적도 포함됩니다. 브라우저와 애플리케이션 코드에서 발생하는 오류는 동일하게 처리될 수 있습니다.
개발자가 JavaScript에서 오류를 발생시킬 수 있는 방법은 여러 가지가 있습니다.
throw new Error('Problem description.')
throw Error('Problem description.')
<-- 첫 번째 것과 동일
throw 'Problem description.'
<-- 나쁘다
throw null
<-- 심지어 더 나쁘다
문자열이나 null을 던지는 것은 브라우저가 해당 오류에 스택 추적을 첨부하지 않아 코드에서 해당 오류가 발생한 위치에 대한 컨텍스트를 잃기 때문에 실제로 권장되지 않습니다. 오류 메시지와 오류가 발생한 올바른 코드 줄을 가리키는 스택 추적이 포함된 실제 Error 객체를 던지는 것이 가장 좋습니다.
각 브라우저에는 위의 비함수 호출 시도 예시와 같이 내장된 예외에 사용하는 고유한 메시지 세트가 있습니다. 브라우저는 동일한 메시지를 사용하려고 시도하지만 사양이 없으므로 보장되지 않습니다. 예를 들어 Chrome과 Firefox에서는 모두 위 예의 {0} is not a function
사용하지만 IE11은 Function expected
보고합니다(특히 어떤 변수가 호출되려고 시도했는지 보고하지 않음).
그러나 브라우저도 자주 갈라지는 경향이 있습니다. switch
문에 여러 개의 기본 문이 있는 경우 Chrome은 "More than one default clause in switch statement"
표시하고 Firefox는 "more than one switch default"
보고합니다. 웹에 새로운 기능이 추가되면 이러한 오류 메시지도 업데이트되어야 합니다. 이러한 차이점은 나중에 난독화된 코드에서 보고된 오류를 처리하려고 할 때 적용될 수 있습니다.
브라우저가 오류 메시지에 사용하는 템플릿은 다음에서 찾을 수 있습니다.
파이어폭스 - http://mxr.mozilla.org/mozilla1.9.1/source/js/src/js.msg
크롬 - https://code.google.com/p/v8/source/browse/branches/bleeding_edge/src/messages.js
인터넷 익스플로러 - https://github.com/Microsoft/ChakraCore/blob/4e4d4f00f11b2ded23d1885e85fc26fcc96555da/lib/Parser/rterrors.h
브라우저는 일부 예외에 대해 다른 오류 메시지를 생성합니다.
스택 추적은 코드에서 오류가 발생한 위치에 대한 설명입니다. 이는 일련의 프레임으로 구성되며, 각 프레임은 코드의 특정 행을 설명합니다. 최상위 프레임은 오류가 발생한 위치이고, 후속 프레임은 함수 호출 스택 또는 오류가 발생한 지점에 도달하기 위해 코드가 실행된 방법입니다. JavaScript는 일반적으로 연결되고 축소되므로 주어진 줄에 여러 개의 문이 있을 때 정확한 문을 찾을 수 있도록 열 번호를 갖는 것도 중요합니다.
Chrome의 기본 스택 추적은 다음과 같습니다.
at throwError (http://mknichel.github.io/javascript-errors/throw-error-basic.html:8:9) at http://mknichel.github.io/javascript-errors/throw-error-basic.html:12:3
각 스택 프레임은 함수 이름(해당되는 경우 코드가 전역 범위에서 실행되지 않은 경우), 함수의 원본 스크립트, 코드의 줄 및 열 번호로 구성됩니다.
안타깝게도 스택 추적 형식에 대한 표준이 없으므로 이는 브라우저마다 다릅니다.
Microsoft Edge 및 IE 11의 스택 추적은 전역 코드를 명시적으로 나열한다는 점을 제외하면 Chrome의 스택 추적과 유사합니다.
at throwError (http://mknichel.github.io/javascript-errors/throw-error-basic.html:8:3) at Global code (http://mknichel.github.io/javascript-errors/throw-error-basic.html:12:3)
Firefox의 스택 추적은 다음과 같습니다.
throwError@http://mknichel.github.io/javascript-errors/throw-error-basic.html:8:9 @http://mknichel.github.io/javascript-errors/throw-error-basic.html:12:3
Safari의 형식은 Firefox의 형식과 유사하지만 약간 다릅니다.
throwError@http://mknichel.github.io/javascript-errors/throw-error-basic.html:8:18 global code@http://mknichel.github.io/javascript-errors/throw-error-basic.html:12:13
동일한 기본 정보가 있지만 형식이 다릅니다.
또한 Safari 예에서는 형식이 Chrome과 다른 점을 제외하고 열 번호가 Chrome 및 Firefox와 다릅니다. 또한 열 번호는 다양한 오류 상황에서 더 많이 벗어날 수 있습니다. 예를 들어 코드에서 (function namedFunction() { throwError(); })();
, Chrome은 throwError()
함수 호출에 대한 열을 보고하는 반면 IE11은 열 번호를 문자열의 시작으로 보고합니다. 이러한 차이점은 나중에 서버가 보고된 오류에 대한 스택 추적을 구문 분석하고 난독화된 스택 추적을 해독해야 할 때 다시 나타납니다.
오류의 스택 속성에 대한 자세한 내용은 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/Stack을 참조하세요. Error.stack 속성에 액세스할 때 Chrome은 오류 메시지를 스택의 일부로 포함하지만 Safari 10+는 포함하지 않습니다.
스택 추적의 형식은 사용되는 형식과 열 번호가 브라우저마다 다릅니다.
더 자세히 살펴보면 아래 섹션에서 설명하는 스택 추적 형식에 대한 많은 미묘한 차이가 있습니다.
기본적으로 익명 함수에는 이름이 없으며 스택 추적의 함수 이름에 빈 문자열 또는 "익명 함수"로 표시됩니다(브라우저에 따라 다름). 디버깅을 개선하려면 모든 함수에 이름을 추가하여 스택 프레임에 표시되도록 해야 합니다. 이를 수행하는 가장 쉬운 방법은 해당 이름이 다른 곳에서는 사용되지 않더라도 익명 함수에 이름을 지정하는 것입니다. 예를 들어:
setTimeout(함수 nameOfTheAnonymousFunction() { ... }, 0);
그러면 스택 추적이 다음에서 시작됩니다.
at http://mknichel.github.io/javascript-errors/javascript-errors.js:125:17
에게
at nameOfTheAnonymousFunction (http://mknichel.github.io/javascript-errors/javascript-errors.js:121:31)
Safari에서는 다음과 같이 진행됩니다.
https://mknichel.github.io/javascript-errors/javascript-errors.js:175:27
에게
nameOfTheAnonymousFunction@https://mknichel.github.io/javascript-errors/javascript-errors.js:171:41
이 방법을 사용하면 해당 함수 내부의 모든 코드에 대해 프레임에 nameOfTheAnonymousFunction
나타나므로 디버깅이 훨씬 쉬워집니다. 자세한 내용은 http://www.html5rocks.com/en/tutorials/developertools/async-call-stack/#toc-debugging-tips를 참조하세요.
브라우저는 함수 자체에 이름이 없는 경우 함수가 할당된 변수나 속성의 이름도 사용합니다. 예를 들어,
var fnVariableName = function() { ... };
브라우저는 스택 추적에서 함수 이름으로 fnVariableName
사용합니다.
at throwError (http://mknichel.github.io/javascript-errors/javascript-errors.js:27:9) at fnVariableName (http://mknichel.github.io/javascript-errors/javascript-errors.js:169:37)
그보다 더 미묘한 점은 이 변수가 다른 함수 내에서 정의된 경우 모든 브라우저는 스택 추적에서 변수 이름만 함수 이름으로 사용한다는 것입니다. Firefox는 제외하고 이름을 연결하는 다른 형식을 사용합니다. 내부 변수의 이름을 가진 외부 함수. 예:
함수 throwErrorFromInnerFunctionAssignedToVariable() { var fnVariableName = function() { throw new Error("foo"); }; fn변수이름();}
Firefox에서는 다음이 생성됩니다.
throwErrorFromInnerFunctionAssignedToVariable/fnVariableName@http://mknichel.github.io/javascript-errors/javascript-errors.js:169:37
다른 브라우저에서는 다음과 같습니다.
at fnVariableName (http://mknichel.github.io/javascript-errors/javascript-errors.js:169:37)
Firefox는 다른 함수 내에 정의된 함수에 대해 다른 스택 프레임 텍스트를 사용합니다.
함수의 표시 이름은 IE11을 제외한 모든 주요 브라우저의 displayName
속성으로 설정할 수도 있습니다. 이러한 브라우저에서는 displayName이 devtools 디버거에 표시되지만 Safari를 제외한 모든 브라우저에서는 오류 스택 추적에 사용되지 않습니다 (Safari는 오류와 관련된 스택 추적에서 displayName을 사용한다는 점에서 나머지 브라우저와 다릅니다).
var someFunction = function() {};someFunction.displayName = " # 함수에 대한 자세한 설명입니다.";
displayName 속성에 대한 공식 사양은 없지만 모든 주요 브라우저에서 지원됩니다. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/displayName 및 http://www.alertdebugging.com/2009/04/29/building-a-better를 참조하세요. -javascript-profiler-with-webkit/: displayName에 대한 자세한 내용을 확인하세요.
IE11은 displayName 속성을 지원하지 않습니다.
Safari는 오류 스택 추적에서 기호 이름으로 displayName 속성을 사용합니다.
스택 추적 없이 오류가 보고되면(이런 일이 발생하는 경우에 대한 자세한 내용은 아래 참조) 프로그래밍 방식으로 스택 추적을 캡처할 수 있습니다.
Chrome에서는 Error.captureStackTrace
API를 사용하면 이 작업을 정말 쉽게 수행할 수 있습니다. 이 API 사용에 대한 자세한 내용은 https://github.com/v8/v8/wiki/Stack%20Trace%20API를 참조하세요.
예를 들어:
함수 무시ThisFunctionInStackTrace() { var err = 새로운 오류(); Error.captureStackTrace(err,ignoreThisFunctionInStackTrace); err.stack을 반환합니다.}
다른 브라우저에서는 새 오류를 생성하고 해당 객체의 stack 속성에 액세스하여 스택 추적을 수집할 수도 있습니다.
var err = new Error('');return err.stack;
그러나 IE10은 실제로 오류가 발생한 경우에만 스택 추적을 채웁니다.
노력하다 { throw new Error('');} catch (e) { e.스택을 반환합니다.}
이러한 접근 방식 중 어느 것도 작동하지 않으면 arguments.callee.caller
객체를 반복하여 줄 번호나 열 없이 대략적인 스택 추적을 생성할 수 있습니다. 하지만 이는 ES5 Strict 모드에서는 작동하지 않으며 권장되는 접근 방식도 아닙니다.
코드에서 setTimeout
사용하거나 Promises를 사용하는 경우와 같이 비동기 지점이 JavaScript 코드에 삽입되는 것은 매우 일반적입니다. 이러한 비동기 진입점은 새로운 실행 컨텍스트를 형성하고 스택 추적이 처음부터 다시 시작되므로 스택 추적에 문제를 일으킬 수 있습니다.
Chrome DevTools는 비동기 스택 추적을 지원합니다. 즉, 오류의 스택 추적에 비동기 지점이 도입되기 전에 발생한 프레임도 표시되는지 확인합니다. setTimeout을 사용하면 누가 setTimeout 함수를 호출하여 결국 오류가 발생했는지 캡처합니다. 자세한 내용은 http://www.html5rocks.com/en/tutorials/developertools/async-call-stack/을 참조하세요.
비동기 스택 추적은 다음과 같습니다.
throwError @ throw-error.js:2 setTimeout (async) throwErrorAsync @ throw-error.js:10 (anonymous function) @ throw-error-basic.html:14
비동기 스택 추적은 현재 Chrome DevTools에서만 지원되며 DevTools가 열려 있을 때 발생하는 예외에 대해서만 지원됩니다. 코드의 Error 개체에서 액세스되는 스택 추적에는 비동기 스택 추적이 포함되지 않습니다 .
경우에 따라 비동기 스택 추적을 폴리필하는 것이 가능하지만 스택 추적을 캡처하는 비용이 저렴하지 않기 때문에 애플리케이션에 상당한 성능 저하가 발생할 수 있습니다.
Chrome DevTools만 기본적으로 비동기 스택 추적을 지원합니다.
HTML 페이지에 평가되거나 인라인된 코드에 대한 스택 추적은 실행된 코드에 대해 페이지의 URL과 줄/열 번호를 사용합니다.
예를 들어:
at throwError (http://mknichel.github.io/javascript-errors/throw-error-basic.html:8:9) at http://mknichel.github.io/javascript-errors/throw-error-basic.html:12:3
이러한 스크립트가 실제로 최적화 이유로 인라인된 스크립트에서 나온 경우 URL, 줄 및 열 번호가 잘못됩니다. 이 문제를 해결하기 위해 Chrome 및 Firefox는 //# sourceURL=
주석을 지원합니다(Safari, Edge 및 IE는 지원하지 않음). 이 주석에 지정된 URL은 모든 스택 추적의 URL로 사용되며 행과 열 번호는 HTML 문서 대신 태그의 시작을 기준으로 계산됩니다. 위와 동일한 오류에 대해 "inline.js" 값과 함께 sourceURL 주석을 사용하면 다음과 같은 스택 추적이 생성됩니다.
at throwError (http://mknichel.github.io/javascript-errors/inline.js:8:9) at http://mknichel.github.io/javascript-errors/inline.js:12:3
이는 인라인 스크립트와 평가판을 사용할 때에도 스택 추적이 여전히 올바른지 확인하는 정말 편리한 기술입니다.
http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl은 sourceURL 주석을 더 자세히 설명합니다.
Safari, Edge 및 IE는 인라인 스크립트 및 평가판 이름 지정을 위한 sourceURL 주석을 지원하지 않습니다. IE 또는 Safari에서 인라인 스크립트를 사용하고 코드를 난독화하는 경우 해당 스크립트에서 발생하는 오류를 난독화할 수 없습니다.
Chrome 42까지 Chrome은 sourceURL 주석을 사용하는 인라인 스크립트에 대해 줄 번호를 올바르게 계산하지 않았습니다. 자세한 내용은 https://bugs.chromium.org/p/v8/issues/detail?id=3920을 참조하세요.
인라인 스크립트의 스택 프레임에 대한 줄 번호는 인라인 스크립트 태그의 시작 대신 HTML 문서의 시작을 기준으로 하기 때문에 sourceURL 주석이 사용될 때 올바르지 않습니다(올바른 난독화 해제가 불가능함). https://code.google.com/p/chromium/issues/detail?id=578269
eval을 사용하는 코드의 경우 sourceURL 주석을 사용하는지 여부 외에 스택 추적에 다른 차이점이 있습니다. Chrome에서 eval에 사용된 문의 스택 추적은 다음과 같습니다.
Error: Error from eval at evaledFunction (eval at evalError (http://mknichel.github.io/javascript-errors/javascript-errors.js:137:3),:1:36) at eval (eval at evalError (http://mknichel.github.io/javascript-errors/javascript-errors.js:137:3), :1:68) at evalError (http://mknichel.github.io/javascript-errors/javascript-errors.js:137:3)
MS Edge 및 IE11에서는 다음과 같습니다.
Error from eval at evaledFunction (eval code:1:30) at eval code (eval code:1:2) at evalError (http://mknichel.github.io/javascript-errors/javascript-errors.js:137:3)
사파리에서:
Error from eval evaledFunction eval code eval@[native code] evalError@http://mknichel.github.io/javascript-errors/javascript-errors.js:137:7
그리고 파이어폭스에서는:
Error from eval evaledFunction@http://mknichel.github.io/javascript-errors/javascript-errors.js line 137 > eval:1:36 @http://mknichel.github.io/javascript-errors/javascript-errors.js line 137 > eval:1:11 evalError@http://mknichel.github.io/javascript-errors/javascript-errors.js:137:3
이러한 차이점으로 인해 모든 브라우저에서 평가 코드를 동일하게 구문 분석하기가 어려울 수 있습니다.
각 브라우저는 eval 내부에서 발생한 오류에 대해 서로 다른 스택 추적 형식을 사용합니다.
JavaScript 코드는 기본 코드에서 직접 호출될 수도 있습니다. Array.prototype.forEach
좋은 예입니다. forEach
에 함수를 전달하면 JS 엔진이 해당 함수를 호출합니다.
함수 throwErrorWithNativeFrame() { var arr = [0, 1, 2, 3]; arr.forEach(functionnamedFn(value) {throwError(); });}
이로 인해 다양한 브라우저에서 다양한 스택 추적이 생성됩니다. Chrome과 Safari는 다음과 같이 스택 추적 자체에 별도의 프레임으로 기본 함수 이름을 추가합니다.
(Chrome) at namedFn (http://mknichel.github.io/javascript-errors/javascript-errors.js:153:5) at Array.forEach (native) at throwErrorWithNativeFrame (http://mknichel.github.io/javascript-errors/javascript-errors.js:152:7) (Safari) namedFn@http://mknichel.github.io/javascript-errors/javascript-errors.js:153:15 forEach@[native code] throwErrorWithNativeFrame@http://mknichel.github.io/javascript-errors/javascript-errors.js:152:14 (Edge) at namedFn (http://mknichel.github.io/javascript-errors/javascript-errors.js:153:5) at Array.prototype.forEach (native code) at throwErrorWithNativeFrame (http://mknichel.github.io/javascript-errors/javascript-errors.js:152:7)
그러나 Firefox 및 IE11에서는 forEach
스택의 일부로 호출되었음을 표시하지 않습니다 .
namedFn@http://mknichel.github.io/javascript-errors/javascript-errors.js:153:5 throwErrorWithNativeFrame@http://mknichel.github.io/javascript-errors/javascript-errors.js:152:3
일부 브라우저는 스택 추적에 네이티브 코드 프레임을 포함하지만 다른 브라우저는 그렇지 않습니다.
애플리케이션에 오류가 있음을 감지하려면 일부 코드에서 해당 오류를 포착하고 이에 대해 보고할 수 있어야 합니다. 오류를 포착하는 기술에는 여러 가지가 있으며 각각 장단점이 있습니다.
window.onerror
는 오류 잡기를 시작하는 가장 쉽고 좋은 방법 중 하나입니다. window.onerror
함수에 할당하면 응용 프로그램의 다른 부분에서 발견되지 않은 모든 오류가 오류에 대한 일부 정보와 함께 이 함수에 보고됩니다. 예를 들어:
window.onerror = function(msg, url, line, col, err) { console.log('응용 프로그램에서 오류가 발생했습니다: ' + msg); console.log('스택 추적: ' + err.stack);}
https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onerror에 대해 자세히 설명되어 있습니다.
역사적으로 이 접근 방식에는 몇 가지 문제가 있었습니다.
제공된 오류 개체가 없습니다.
window.onerror
함수의 5번째 인수는 Error 객체로 간주됩니다. 이는 2013년 WHATWG 사양에 추가되었습니다: https://html.spec.whatwg.org/multipage/webappapis.html#errorevent. 이제 Chrome, Firefox 및 IE11은 중요한 스택 속성과 함께 Error 개체를 제대로 제공하지만 Safari, MS Edge 및 IE10은 그렇지 않습니다. 이는 Firefox 14(https://bugzilla.mozilla.org/show_bug.cgi?id=355430)부터 Firefox에서, 2013년 후반부터 Chrome(https://mikewest.org/2013/08/debugging-runtime-errors)에서 작동합니다. -with-window-onerror, https://code.google.com/p/chromium/issues/detail?id=147127). Safari 10은 window.onerror의 Error 개체에 대한 지원을 시작했습니다.
Safari(10 미만 버전), MS Edge 및 IE10은 window.onerror의 스택 추적이 있는 Error 개체를 지원하지 않습니다.
도메인 간 삭제
Chrome에서는 window.onerror 처리기의 다른 도메인에서 발생한 오류가 "Script error.", "", 0으로 정리됩니다. 사용자가 신경쓰지 않는 스크립트를 삭제하여 애플리케이션이 다음과 같은 오류를 필터링할 수 있도록 합니다. 그러나 이는 Firefox, Safari 또는 IE11에서는 발생하지 않으며 Chrome은 문제 코드를 래핑하는 try/catch 블록에 대해 이 작업을 수행하지 않습니다.
교차 도메인 스크립트의 충실도를 그대로 유지하면서 Chrome의 window.onerror
에서 오류를 수신하려면 해당 리소스가 적절한 교차 출처 헤더를 제공해야 합니다. 자세한 내용은 https://mikewest.org/2013/08/debugging-runtime-errors-with-window-onerror를 참조하세요.
Chrome은 다른 출처에서 발생한 오류를 삭제하는 유일한 브라우저입니다. 이러한 항목을 필터링하거나 적절한 헤더를 설정하도록 주의하세요.
크롬 확장 프로그램
이전 버전의 Chrome에서는 사용자 컴퓨터에 설치된 Chrome 확장 프로그램에서도 window.onerror에 보고되는 오류가 발생할 수 있습니다. 이 문제는 최신 버전의 Chrome에서 수정되었습니다. 아래의 전용 Chrome 확장 프로그램 섹션을 참조하세요.
window.addEventListener("error")
API는 window.onerror API와 동일하게 작동합니다. 이 접근 방식에 대한 자세한 내용은 http://www.w3.org/html/wg/drafts/html/master/webappapis.html#runtime-script-errors를 참조하세요.
window.onerror를 통해 오류를 포착해도 해당 오류가 DevTools 콘솔에 나타나는 것을 막지는 못합니다. 개발자가 오류를 쉽게 볼 수 있으므로 이는 개발에 적합한 동작일 가능성이 높습니다. 프로덕션 환경에서 최종 사용자에게 이러한 오류가 표시되는 것을 원하지 않는 경우 window.addEventListener 접근 방식을 사용하면 e.preventDefault()
호출할 수 있습니다.
window.onerror는 JS 오류를 포착하고 보고하는 최고의 도구입니다. 유효한 오류 개체 및 스택 추적이 있는 JS 오류만 서버에 다시 보고되는 것이 좋습니다. 그렇지 않으면 오류를 조사하기가 어렵거나 Chrome 확장 프로그램 또는 교차 도메인 스크립트에서 많은 스팸을 받을 수 있습니다.
위 섹션에서 불행하게도 모든 브라우저에서 window.onerror
사용하여 모든 오류 정보를 캡처하는 것은 불가능합니다. 로컬에서 예외를 포착하려면 try/catch 블록이 확실한 선택입니다. window.onerror로 포착할 수 없는 오류 정보를 캡처하기 위해 전체 JavaScript 파일을 try/catch 블록으로 래핑하는 것도 가능합니다. 이는 window.onerror를 지원하지 않는 브라우저의 상황을 개선하지만 몇 가지 단점도 있습니다.
try/catch 블록은 window.setTimeout
통해 비동기 코드 블록에서 발생하는 오류와 같은 프로그램의 모든 오류를 캡처하지 않습니다. Try/catch를 보호된 진입점과 함께 사용하여 공백을 메울 수 있습니다.
전체 애플리케이션을 래핑하는 try/catch 블록은 모든 오류를 포착하는 데 충분하지 않습니다.
V8의 이전 버전(및 잠재적으로 다른 JS 엔진), try/catch 블록을 포함하는 함수는 컴파일러(http://www.html5rocks.com/en/tutorials/speed/v8/)에 의해 최적화되지 않습니다. Chrome은 TurboFan(https://codereview.chromium.org/1996373002)에서 이 문제를 해결했습니다.
JavaScript의 "진입점"은 코드 실행을 시작할 수 있는 브라우저 API입니다. 예에는 setTimeout
, setInterval
, 이벤트 리스너, XHR, 웹 소켓 또는 약속이 포함됩니다. 이러한 진입점에서 발생한 오류는 window.onerror에 의해 포착되지만, window.onerror의 전체 Error 객체를 지원하지 않는 브라우저에서는 try/catch 메소드가 언급되어 있으므로 이러한 오류를 포착하기 위한 대체 메커니즘이 필요합니다. 위의 것들도 그들을 잡지 못할 것입니다.
고맙게도 JavaScript에서는 이러한 진입점을 래핑할 수 있으므로 함수가 호출되기 전에 try/catch 블록을 삽입하여 코드에서 발생하는 오류를 잡을 수 있습니다.
각 진입점은 진입점을 보호하기 위해 약간 다른 코드가 필요하지만 방법론의 요점은 다음과 같습니다.
함수 보호엔트리포인트(fn) { return function protectedFn() {try { return fn();} catch (e) { // 오류를 처리합니다.} }}_oldSetTimeout = window.setTimeout;window.setTimeout = 함수 protectedSetTimeout(fn, time) { return _oldSetTimeout.call(window, protectedEntryPoint(fn), time);};
안타깝게도 Promise에서 발생하는 오류는 관찰되지 않고 보고되지 않는 경우가 많습니다. Promise에서 발생했지만 거부 핸들러를 연결하여 처리되지 않은 오류는 다른 곳에서는 보고되지 않습니다. 즉, window.onerror
에 보고되지 않습니다 . Promise가 거부 핸들러를 연결하더라도 해당 코드 자체는 해당 오류가 기록되도록 수동으로 보고해야 합니다. 자세한 내용은 http://www.html5rocks.com/en/tutorials/es6/promises/#toc-error-handling을 참조하세요. 예를 들어:
window.onerror = 함수(...) { // 이는 Promise 코드에 의해 호출되지 않습니다.};var p = new Promise(...);p.then(function() { throw new Error("이 오류는 어디에서도 처리되지 않습니다.");});var p2 = new Promise(...);p2.then(function() { throw new Error("이 오류는 체인에서 처리됩니다.");}).catch(function(error) { // 사용자에게 오류 메시지 표시 // 해당되는 경우 이 코드는 서버에 기록되도록 오류를 수동으로 보고해야 합니다.});
더 많은 정보를 캡처하는 한 가지 접근 방식은 보호된 진입점을 사용하여 Promise 메서드 호출을 try/catch로 래핑하여 오류를 보고하는 것입니다. 이는 다음과 같을 수 있습니다:
var _oldPromiseThen = Promise.prototype.then; Promise.prototype.then = function protectedThen(callback, errorHandler) {return _oldPromiseThen.call(this, protectedEntryPoint(callback), protectedEntryPoint(errorHandler))); };
안타깝게도 Promise의 오류는 기본적으로 처리되지 않습니다.
Q, Bluebird 및 Closure와 같은 Promise 구현은 브라우저 Promise 구현의 오류 처리보다 더 나은 다양한 방식으로 오류를 처리합니다.
Q에서는 .done()
호출하여 Promise 체인을 "종료"할 수 있습니다. 그러면 오류가 체인에서 처리되지 않은 경우 다시 발생하고 보고됩니다. https://github.com/kriskowal/q#handling-errors를 참조하세요.
Bluebird에서는 처리되지 않은 거부가 즉시 기록되고 보고됩니다. http://bluebirdjs.com/docs/features.html#surfacing-unhandled-errors를 참조하세요.
Closure의 goog.Promise 구현에서는 Promise의 체인이 구성 가능한 시간 간격 내에 거부를 처리하지 않는 경우(프로그램의 나중에 코드에서 거부 핸들러를 추가할 수 있도록 하기 위해) 처리되지 않은 거부가 기록되고 보고됩니다.
위의 비동기 스택 추적 섹션에서는 Promise.prototype.then
호출과 같은 비동기 후크가 있을 때 브라우저가 스택 정보를 캡처하지 않는다는 점을 설명합니다. Promise 폴리필은 오류 진단을 훨씬 쉽게 만들어주는 비동기 스택 추적 지점을 캡처하는 방법을 제공합니다. 이 접근 방식은 비용이 많이 들지만 더 많은 디버그 정보를 캡처하는 데 정말 유용할 수 있습니다.
Q에서 Q.longStackSupport = true;
. https://github.com/kriskowal/q#long-stack-traces를 참조하세요.
Bluebird에서는 애플리케이션 어딘가에서 Promise.longStackTraces()
호출합니다. http://bluebirdjs.com/docs/features.html#long-stack-traces를 참조하세요.
Closure에서 goog.Promise.LONG_STACK_TRACES
true로 설정합니다.
Chrome 49에는 Promise가 거부될 때 전달되는 이벤트에 대한 지원이 추가되었습니다. 이를 통해 애플리케이션은 Promise 오류에 연결되어 나머지 오류와 함께 중앙에서 보고되도록 할 수 있습니다.
window.addEventListener('unhandledrejection', 이벤트 => { // event.reason에는 거부 이유가 포함되어 있습니다. 오류가 발생하면 이는 오류 개체입니다.});
자세한 내용은 https://googlechrome.github.io/samples/promise-rejection-events/ 및 https://www.chromestatus.com/feature/4805872211460096을 참조하세요.
다른 브라우저에서는 지원되지 않습니다.
전용 작업자, 공유 작업자, 서비스 작업자를 포함한 웹 작업자는 오늘날 애플리케이션에서 점점 더 대중화되고 있습니다. 이러한 작업자는 모두 기본 페이지와 별도의 스크립트이므로 각각 고유한 오류 처리 코드가 필요합니다. 작업자의 오류를 최대한 효율적으로 처리하려면 각 작업자 스크립트에 자체 오류 처리 및 보고 코드를 설치하는 것이 좋습니다.
전용 웹 작업자는 기본 페이지와 다른 실행 컨텍스트에서 실행되므로 작업자의 오류는 위 메커니즘에 의해 포착되지 않습니다. 페이지에서 작업자의 오류를 캡처하려면 추가 단계를 수행해야 합니다.
작업자가 생성되면 새 작업자에 onerror 속성을 설정할 수 있습니다.
var 작업자 = new Worker('worker.js');worker.onerror = function(errorEvent) { ... };
이는 https://html.spec.whatwg.org/multipage/workers.html#handler-abstractworker-onerror에 정의되어 있습니다. 작업자의 onerror
함수는 위에서 설명한 window.onerror
와 다른 서명을 갖습니다. 5개의 인수를 받아들이는 대신, worker.onerror
단일 인수, 즉 ErrorEvent
객체를 취합니다. 이 개체에 대한 API는 https://developer.mozilla.org/en-US/docs/Web/API/ErrorEvent에서 찾을 수 있습니다. 여기에는 메시지, 파일 이름, 줄 및 열이 포함되어 있지만 현재 안정적인 브라우저에는 스택 추적이 포함된 "Error" 개체가 포함되어 있지 않습니다(errorEvent.error는 null임). 이 API는 상위 페이지의 범위에서 실행되므로 상위 페이지와 동일한 보고 메커니즘을 사용하는 데 유용합니다. 불행하게도 스택 추적이 없기 때문에 이 API는 사용이 제한되어 있습니다.
작업자가 실행하는 JS 내에서 일반적인 window.onerror API(https://html.spec.whatwg.org/multipage/webappapis.html#onerroreventhandler)를 따르는 onerror API를 정의할 수도 있습니다. 작업자 코드에서:
self.onerror = function(message, filename, line, col, error) { ... };
이 API에 대한 논의는 대부분 위의 window.onerror에 대한 논의를 따릅니다. 그러나 지적해야 할 두 가지 주목할만한 사항이 있습니다.
Firefox와 Safari는 "오류" 개체를 함수의 5번째 인수로 보고하지 않으므로 이러한 브라우저는 작업자로부터 스택 추적을 얻지 못합니다(Chrome, MS Edge 및 IE11은 스택 추적을 얻습니다). 작업자 내의 onmessage
기능에 대한 보호된 진입점을 사용하여 이러한 브라우저에 대한 스택 추적 정보를 캡처할 수 있습니다.
이 코드는 작업자 내에서 실행되므로 코드는 오류를 서버에 다시 보고하는 방법을 선택해야 합니다. postMessage
사용하여 상위 페이지에 오류를 다시 전달하거나 XHR 오류 보고 메커니즘(아래에서 자세히 설명)을 설치해야 합니다. 노동자 그 자체.
Firefox, Safari 및 IE11(Chrome 제외)에서는 작업자 자체 onerror 및 페이지에서 설정한 onerror 이벤트 리스너가 호출된 후에 상위 페이지의 window.onerror
함수도 호출됩니다. 그러나 이 window.onerror에는 오류 개체도 포함되지 않으므로 스택 추적도 포함되지 않습니다. 또한 이러한 브라우저는 작업자의 오류를 여러 번 보고하지 않도록 주의해야 합니다.
Chrome과 Firefox는 여러 페이지에서 작업자를 공유하기 위해 SharedWorker API를 지원합니다. 작업자는 공유되므로 하나의 상위 페이지에만 연결되지 않습니다. SharedWorker는 대부분 전용 웹 작업자와 동일한 정보를 따르지만 이로 인해 오류 처리 방법에 약간의 차이가 발생합니다.
Chrome에서는 SharedWorker에 오류가 있는 경우 작업자 코드 자체 내의 작업자 자체 오류 처리만 호출됩니다(예: self.onerror
설정한 경우). 상위 페이지의 window.onerror
호출되지 않으며 Chrome은 사양에 정의된 대로 상위 페이지에서 호출할 수 있는 상속된 AbstractWorker.onerror
지원하지 않습니다.
Firefox에서는 이 동작이 다릅니다. 공유 워커의 오류로 인해 상위 페이지의 window.onerror가 호출되지만 오류 개체는 null이 됩니다. 또한 Firefox는 AbstractWorker.onerror
속성을 지원하므로 상위 페이지는 자체 오류 처리기를 작업자에 연결할 수 있습니다. 그러나 이 오류 처리기가 호출되면 오류 개체가 null이 되어 스택 추적이 없으므로 사용이 제한됩니다.
공유 작업자에 대한 오류 처리는 브라우저에 따라 다릅니다.
서비스 워커는 현재 최신 Chrome 및 Firefox 버전에서만 사용할 수 있는 새로운 사양입니다. 이 작업자는 전용 웹 작업자와 동일한 논의를 따릅니다.
서비스 워커는 navigator.serviceWorker.register
함수를 호출하여 설치됩니다. 이 함수는 초기화 중에 오류가 발생하는 등 서비스 워커를 설치하는 중에 오류가 발생한 경우 거부되는 Promise를 반환합니다. 이 오류에는 문자열 메시지만 포함되며 다른 내용은 포함되지 않습니다. 또한 Promise는 window.onerror
핸들러에 오류를 보고하지 않으므로 애플리케이션 자체에서 오류를 포착하기 위해 Promise에 catch 블록을 추가해야 합니다.
navigator.serviceWorker.register('service-worker-installation-error.js').catch(function(error) { // 문자열의 오류 유형});
다른 워커와 마찬가지로 서비스 워커도 오류를 포착하기 위해 서비스 워커 내에 self.onerror
함수를 설정할 수 있습니다. 서비스 워커의 설치 오류는 onerror 함수에 보고되지만 안타깝게도 오류 개체나 스택 추적은 포함되지 않습니다.
서비스 작업자 API에는 AbstractWorker 인터페이스에서 상속된 onerror 속성이 포함되어 있지만 Chrome은 아무 작업도 수행하지 않습니다.