[Javascript] Error / try-catch
JS Error / try-catch, JS Error, JS try catch, JavaScript 에러, custom error
정의
JS 의 에러 처리. throw 로 던지고 try/catch/finally 로 잡는다. 내장 Error 타입과 사용자 정의 Error.
throw / try / catch
try {
throw new Error('Something bad');
} catch (e) {
console.error(e.message);
} finally {
cleanup();
}
finally 는 에러 유무와 무관하게 실행.
내장 Error 타입
| 타입 | 의미 |
|---|---|
Error | 기본 |
TypeError | 타입 불일치 (null.foo) |
ReferenceError | 선언 안 된 변수 |
SyntaxError | 구문 오류 |
RangeError | 범위 (new Array(-1)) |
URIError | URI 함수 |
EvalError | eval 관련 (사실상 deprecated) |
AggregateError | 여러 에러 묶음 (Promise.any) |
Error 객체의 속성
const e = new Error('oops');
e.message // 'oops'
e.name // 'Error'
e.stack // 스택 트레이스
사용자 정의 Error
class ValidationError extends Error {
constructor(field, message) {
super(message);
this.name = 'ValidationError';
this.field = field;
}
}
try {
throw new ValidationError('email', 'invalid format');
} catch (e) {
if (e instanceof ValidationError) {
console.log(e.field, e.message);
}
}
super(message) 로 부모 (Error) 의 message 설정. name 명시 권장.
에러 체인 (cause, ES2022)
try {
riskyOperation();
} catch (originalError) {
throw new Error('High-level failure', { cause: originalError });
}
Error.cause 로 원인 에러 보존. 디버깅에 매우 유용.
try { ... } catch (e) {
console.error(e.message);
console.error(e.cause); // 원인 에러
}
catch 의 인자 옵션 (ES2019+)
try { ... } catch (e) { ... }
try { ... } catch { ... } // 인자 생략 가능 (ES2019)
에러 객체를 안 쓸 때 더 깔끔.
try-catch 의 한계
비동기 에러
try {
setTimeout(() => { throw new Error('lost'); }, 0);
} catch (e) {
// 잡히지 않음!
}
setTimeout 콜백은 다른 스택. try-catch 범위 밖.
Promise / async
// promise
fetch('/api').then(...).catch(e => handle(e));
// async/await (가능!)
try {
const r = await fetch('/api');
return await r.json();
} catch (e) {
handle(e);
}
async/await 안에서는 비동기도 try-catch 로 잡힌다.
global handler
// 브라우저
window.addEventListener('error', e => { ... });
window.addEventListener('unhandledrejection', e => { ... });
// Node.js
process.on('uncaughtException', e => { ... });
process.on('unhandledRejection', e => { ... });
처리 안 된 에러를 마지막에 잡는 안전망.
자주 쓰는 패턴
에러 타입별 분기
try {
await api();
} catch (e) {
if (e instanceof ValidationError) showFormError(e.field);
else if (e instanceof NetworkError) showRetry();
else throw e; // 알 수 없는 에러는 위로 전달
}
finally 로 리소스 정리
const conn = await openConnection();
try {
await doWork(conn);
} finally {
await conn.close(); // 에러 유무 무관 정리
}
sentinel pattern
let result;
try {
result = computeRisky();
} catch {
result = DEFAULT;
}
Result<T> 패턴 (함수형)
function safeDivide(a, b) {
if (b === 0) return { ok: false, error: 'div by zero' };
return { ok: true, value: a / b };
}
const r = safeDivide(10, 0);
if (!r.ok) handle(r.error);
else use(r.value);
throw 보다 명시적.
함정
1. catch 의 e 가 any
try { ... } catch (e) {
// e 는 unknown (TypeScript 4.4+)
if (e instanceof Error) console.log(e.message);
}
throw 가 무엇이든 던질 수 있어서 (throw 'string', throw 42) 검사 필요.
2. throw new Error vs throw ’…’
throw 'oops'; // 문자열 (스택 없음)
throw new Error('oops'); // ✓ 스택 트레이스 포함
항상 new Error 권장.
3. async 함수 안의 throw
async function foo() {
throw new Error('x');
}
// foo() 가 rejected Promise 반환
foo().catch(handle);
async 함수의 throw 는 자동으로 rejection.
4. finally 의 return
function foo() {
try { return 1; }
finally { return 2; }
}
foo(); // 2 (finally 의 return 이 우선)
finally 에서 return / throw 하면 try 의 결과 덮어씀. 일반적으로 피한다.
참고
이 글의 용어 (3개)
- [Javascript] async/awaitjavascript
- 정의 / 는 기반 비동기 코드를 마치 동기 코드처럼 쓸 수 있게 해주는 ES2017 의 문법 설탕. 본질은 Promise 그 자체, 문법만 다르다. 전체 동작 메커니즘은 글 참조…
- [Javascript] Classjavascript
- 정의 ES6 는 prototype chain 위의 문법 설탕. C++/Java 스타일 OOP 와 닮았지만 내부는 . 구조 상속 super 의 의미 - : 부모 constructo…
- [Javascript] Promisejavascript
- 정의 Promise 는 미래의 어떤 시점에 결정될 값을 나타내는 객체다. ECMAScript 2015 (ES6) 에 도입되어 기반 비동기의 가독성과 에러 처리 문제를 해결했다. …
💬 댓글