target: es2022, lib: ["es2022"] 필수try {
JSON.parse('{ bad json }');
} catch (err) {
throw new Error('Something went wrong: ' + err.message);
}
문제점:
→ 실제 서비스 계층 구성(서비스 → 래퍼 → 인프라)에서 디버깅 난이도 증가
try {
try {
JSON.parse('{ bad json }');
} catch (err) {
throw new Error('Something went wrong', { cause: err });
}
} catch (err) {
console.error(err.stack);
console.error('Caused by:', err.cause.stack);
}
결과:
Error: Something went wrong
...
Caused by: SyntaxError: Unexpected token b...
function fetchUserData() {
try {
JSON.parse('{ broken: true }');
} catch (parseError) {
throw new Error('Failed to fetch user data', { cause: parseError });
}
}
try {
fetchUserData();
} catch (err) {
console.error(err.message); // 상위 메시지
console.error(err.cause); // SyntaxError
}
throw new Error("x failed: " + err);
→ 타입 유실 / 스택 유실
err.original = originalErr;
→ 통일되지 않은 관례 / 라이브러리별 혼란
→ 여전히 타입·스택 추적이 수동적
Error.cause는 이런 패턴을 대체하는 공식 해법.
class DatabaseError extends Error {
constructor(message, { cause } = {}) {
super(message, { cause });
this.name = 'DatabaseError';
}
}
{
"compilerOptions": {
"target": "es2022",
"lib": ["es2022"]
}
}
안 하면 { cause }에 타입 에러 발생.
expect(err.cause).toBeInstanceOf(ValidationError);
DevTools는 cause를 자동 출력하지 않음.
console.error(err);
console.error("Caused by:", err.cause);
function logErrorChain(err, level = 0) {
if (!err) return;
console.error(' '.repeat(level * 2) + `${err.name}: ${err.message}`);
if (err.cause instanceof Error) {
logErrorChain(err.cause, level + 1);
} else if (err.cause) {
console.error(' '.repeat((level + 1) * 2) + String(err.cause));
}
}
function logFullErrorChain(err) {
let current = err;
while (current) {
console.error(current.stack);
current = current.cause instanceof Error ? current.cause : null;
}
}
→ 마이크로서비스·대규모 계층 구조에서 필수
class ConnectionTimeoutError extends Error {}
class DatabaseError extends Error {}
class ServiceUnavailableError extends Error {}
try {
try {
try {
throw new ConnectionTimeoutError('DB connection timed out');
} catch (networkErr) {
throw new DatabaseError('Failed to connect', { cause: networkErr });
}
} catch (dbErr) {
throw new ServiceUnavailableError('Unable to save user data', { cause: dbErr });
}
} catch (finalErr) {
logErrorChain(finalErr);
}
ServiceUnavailableError: Unable to save user data
DatabaseError: Failed to connect
ConnectionTimeoutError: DB connection timed out
→ “문제가 어디서 시작되었는지”를 명확하게 보여주는 계층적 오류 기록.
| 기능 | 지원 |
|---|---|
new Error(message, { cause }) | ✔️ |
| 커스텀 클래스에서 cause 전달 | ✔️ |
| 원본 에러 스택/타입 보존 | ✔️ |
| 디버깅·로깅·테스트 강화 | ✔️ |
| TypeScript ES2022 설정 필요 | ✔️ |
| 체인 자동 출력 없음 → 직접 로깅 | ⚠️ |
→ “원인 에러 손실” 문제를 표준적으로 해결하는 모던 패턴.
원문 - Error chaining in JavaScript: cleaner debugging with Error.cause