JavaScript 디버깅의 숨겨진 보석: error.cause
Grace Collins
Solutions Engineer · Leapcell

디버깅의 어려움
디버깅에서 가장 큰 어려움은 무엇일까요? 그 중 하나는 의심할 여지 없이 오류의 원인을 추적하는 것입니다.
다음 시나리오를 상상해 보세요.
const func = () => { doSth('A'); doSth('B'); };
func
가 오류를 발생시키면 어떤 단계에서 오류가 발생했는지 어떻게 식별할 수 있을까요? doSth('A')
, doSth('B')
또는 func
자체로 인해 발생했습니까? 분명히 오류에 충분한 컨텍스트가 없습니다.
일반적인 해결 방법
이 문제를 해결하기 위한 일반적인 접근 방식은 다음과 같습니다.
const func = () => { try { doSth('A'); } catch (error) { throw new Error('A에서 발생한 오류', error); } try { doSth('B'); } catch (error) { throw new Error('B에서 발생한 오류', error); } };
이 방법을 사용하면 오류의 원인을 더 쉽게 찾을 수 있습니다. 그러나 이 솔루션에는 몇 가지 제한 사항이 있습니다.
-
오류 세부 정보 손실: 오류에 광범위한 정보(예: 페이로드, HTTP 상태 코드, 오류 코드)가 포함된 경우 이 방법은
doSth
의 오류 메시지만 새로 생성된 오류에 추가합니다. 원래 스택 추적을 포함한 다른 중요한 세부 정보는 손실됩니다. -
로그 가독성 저하: 잠재적인 오류 지점이 두 개 이상인 경우 로그가 어수선해지고 해석하기 어려울 수 있습니다.
-
의도 표현의 모호성: 코드는 새 오류가 특정
doSth
함수로 인해 발생했다는 것을 명시적으로 전달하지 않으므로 코드 가독성을 개선할 여지가 있습니다.
error.cause
소개
이러한 문제를 해결하기 위해 ECMAScript 2022에서는 error.cause
를 도입했습니다.
이 기능을 사용하면 개발자가 새 오류 객체를 생성할 때 오류의 근본 원인을 지정할 수 있습니다. error.cause
를 사용하면 오류 체인을 설정하여 문제의 근본 원인을 더 쉽게 디버깅하고 추적할 수 있습니다.
다음은 간단한 예입니다.
try { // 오류를 발생시킬 수 있는 작업 } catch (error) { throw new Error('문제가 발생했습니다.', { cause: error }); }
이 방법을 사용하면 오류 간의 인과 관계를 구축할 수 있습니다. 예를 들어 다음과 같습니다.
const func = () => { try { doSth('A'); } catch (error) { throw new Error('A에서 발생한 오류', { cause: error }); } try { doSth('B'); } catch (error) { throw new Error('B에서 발생한 오류', { cause: error }); } };
이를 통해 하위 수준 함수(예: doSth('A')
)에서 발생한 오류를 포착하고 관련 컨텍스트를 추가하는 새 오류를 발생시키고(예: "doSth('A')
를 실행하는 동안 오류가 발생했습니다.") 원래 오류 세부 정보(예: "A는 잘못된 인수입니다.")를 보존할 수 있습니다.
오류 체인 구축
error.cause
의 또 다른 장점은 연결된 오류 체인을 생성하여 개발자가 애플리케이션의 여러 계층을 통해 문제를 추적할 수 있다는 것입니다.
const func = () => { try { try { try { doSth('A'); } catch (error) { throw new Error('깊이 3에서 오류', { cause: error }); } } catch (error) { throw new Error('깊이 2에서 오류', { cause: error }); } } catch (error) { throw new Error('깊이 1에서 오류', { cause: error }); } }; console.log(error.cause.cause); // 깊이 3에서 오류
Node.js에서 cause
가 있는 오류는 콘솔에서 특별히 처리됩니다. 모든 관련 오류 스택이 출력됩니다.
const cause = new Error('원격 HTTP 서버가 500 상태로 응답했습니다.'); const symptom = new Error('메시지 전송에 실패했습니다.', { cause }); console.log(symptom); // 출력: // Error: 메시지 전송에 실패했습니다. // at REPL2:1:17 // at Script.runInThisContext (node:vm:130:12) // ... 7 cause 스택 추적과 일치하는 라인 ... // at [_line] [as _line] (node:internal/readline/interface:886:18) { // [cause]: Error: 원격 HTTP 서버가 500 상태로 응답했습니다. // at REPL1:1:15 // at Script.runInThisContext (node:vm:130:12) // at REPLServer.defaultEval (node:repl:574:29) // at bound (node:domain:426:15) // at REPLServer.runBound [as eval] (node:domain:437:12) // at REPLServer.onLine (node:repl:902:10) // at REPLServer.emit (node:events:549:35) // at REPLServer.emit (node:domain:482:12) // at [_onLine] [as _onLine] (node:internal/readline/interface:425:12) // at [_line] [as _line] (node:internal/readline/interface:886:18)
결론
- 오류 컨텍스트와 세부 정보에 즉시 액세스할 수 있으면 디버깅이 훨씬 쉬워집니다.
- 이를 달성하는 효과적인 방법 중 하나는
error.cause
기능을 사용하여 "catch + 컨텍스트와 함께 다시 throw" 패턴을 채택하는 것입니다.
try { doSth(); } catch (error) { throw new Error('doSth 관련 컨텍스트', { cause: error }); }
이 방법은 오류 추적 가능성을 향상시킬 뿐만 아니라 코드의 가독성과 유지 관리성도 향상시킵니다.
Leapcell은 Node.js 프로젝트를 클라우드에 배포하기 위한 최고의 선택입니다.
Leapcell은 웹 호스팅, 비동기 작업 및 Redis를 위한 차세대 서버리스 플랫폼입니다.
다국어 지원
- Node.js, Python, Go 또는 Rust로 개발하세요.
무료로 무제한 프로젝트 배포
- 사용량에 대해서만 비용을 지불하세요. 요청이나 요금이 없습니다.
최고의 비용 효율성
- 유휴 요금 없이 사용한 만큼만 지불하세요.
- 예: $25는 평균 응답 시간 60ms에서 694만 건의 요청을 지원합니다.
간소화된 개발자 경험
- 간편한 설정을 위한 직관적인 UI.
- 완전 자동화된 CI/CD 파이프라인 및 GitOps 통합.
- 실행 가능한 통찰력을 위한 실시간 메트릭 및 로깅.
손쉬운 확장성 및 고성능
- 고도의 동시성을 쉽게 처리할 수 있는 자동 스케일링.
- 운영 오버헤드가 제로이므로 구축에만 집중하세요.
설명서에서 자세히 알아보세요!
X에서 팔로우하세요: @LeapcellHQ