🍄 throw 문 (Exception throwing)
- Javascript(TypeScript)의
throw
statement는 명시적으로 예외를 발생시킬 때 사용된다.
- throw문에 의해 exception이 발생되면 정상적인 실행 플로우를 중단하고, 제어의 흐름을 가장 가까운
try...catch
문으로 이동시켜 예외를 처리하게 된다.
- 현재 함수 내부에서 매칭되는
catch
블록이 없다면 exception은 상위 호출 스택으로 propagate되어 외부 함수에 있는try…catch
문으로 전달된다.
- TypeScript에서는 어느 타입의 값이든 예외로 던질 수 있다.
- 원시타입 : number, string, boolean, symbol..
- objects, arrays, functions, classes, Error 또는 TypeError 같은 custom type
🍄 Error type annotations
try { ... throw new Error("Failed!"); } catch (error: 항상 any 또는 unknown) { ... }
- TypeScript는 대부분의 코드에서 정적 타입 체크를 제공하지만, exception은 런타임 오류로 간주된다.
- throw된 예외값의 타입이 런타임에 결정되므로, TypeScript 컴파일러는 throw된 값에 대해 특정 타입을 강제하지 않는다.
- 즉, catch 문에 전달되는 error 매개변수에 대한 타입 설정은
any
또는unknown
으로만 제한된다.
그렇다면, catch문 내부의 error에 대해서 어떻게 타입 정의를 해야 바람직할까?
[방법2] 👍
unknown
- error 매개변수의 타입으로
: unknown
을 지정 - error 객체 타입이 unknown으로 지정된 경우, catch문 내부에서 error 객체에 접근하기 전에 반드시 명시적인 type checking과 type narrowing이 요구된다.⭐️
- 즉, 먼저 type narrowing으로 Error의 타입을 구체화한 후 해당 에러 객체의 프로퍼티에 접근하게되므로, 런타임 에러를 방지하는 보다 더 안전한 방식이라고 할 수 있다.
- 예시 코드
try { ... throw new Error("Failed!"); } catch (error: unknown) { if (typeof error === "string") { // error 객체 타입이 문자열인 경우 ... } else if (error instanceof Error) { // error 객체가 Error의 인스턴스인 경우 ... } else { // 그 외의 경우... } }
: unknown
처럼 직접적인 명시 없이도 에러의 타입을 Unknown으로 지정할 수 있다.[방법3]
- Type Assertion(타입 단언)
try { if (state === "fail") { throw new Error("Failure!"); } } catch (error) { return (error as Error).message; }
- 예) 위 예시 코드의 try문 내부 어딘가에서 string 타입의 에러를 throw하는 경우, catch문에서
undefined
를 리턴하게 된다!
🍄 built-in Error class (내장 Error 클래스)
- 내장 Error 클래스
- 오류 객체를 표상하는 JavaScript의 표준 클래스이다.
- Property
name
: 에러의 유형을 나타내는 프로퍼티message
: 사람이 이해 가능한 언어로 서술된 에러의 내용. 에러 메시지stack
: 에러가 발생한 지점을 추적할 수 있는 스택을 담고 있는 프로퍼티
try { // some code that may throw an error throw new Error("Random error message"); } catch (err) { // handle the error if (err instanceof Error) { console.log(err.name); // the type of error console.log(err.message); // the description of the error console.log(err.stack); // the stack trace of the error } else { // handle other errors } }
🍄 Standard error classes (표준 Error 클래스)
- built-in(내장) Error 클래스로부터 파생된 JavaScript의 표준 Error 클래스
- 종류
SyntaxError
: 자바스크립트 코드 구문 분석시 발생하는 구문(syntax) 오류TypeError
: 호환되지 않는 type에서 작업 또는 함수가 수행되었을 때의 오류ReferenceError
: 정의되지 않은 변수나 함수에 대한 잘못된 참조 오류RangeError
: 숫자 값이 허용 범위를 벗어난 경우의 오류URIError
: encodeURIComponent 혹은 decodeURIComponent 등의 인코딩, 디코딩 기능에서 잘못된 URI 문자열이 발견될 경우의 오류
🍄 Custom Error types
- 내장 Error 클래스를 확장하여 커스텀 에러를 생성할 수 있다.
message
프로퍼티를 지정하기 위해 자식 생성자에서super()
를 호출해야 하며,name
프로퍼티 또한 명시적으로 세팅해주어야 한다.
- 예시
/********** 예시 1 ************/ class CustomError extends Error { constructor(message: string) { super(message); // call the parent constructor this.name = "CustomError"; // set the name property } } try { throw new CustomError("Something went wrong"); } catch (err) { if (err instanceof CustomError) { console.log(err.name); // CustomError console.log(err.message); // Something went wrong } else { // handle other errors } } /********** 예시 2 ************/ // Custom Error Class에 추가 프로퍼티를 설정할 수 있다. class HttpError extends Error { statusCode: number; constructor(statusCode: number, message: string) { super(message); this.name = "HttpError"; this.statusCode = statusCode; } log() { console.log(`Http error ${this.statusCode}: ${this.message}`); } } try { throw new HttpError(404, "Not found"); } catch (err) { if (err instanceof HttpError) { err.log(); // Http error 404: Not found } else { // handle other errors } }
- 사용자 정의 에러 타입 ⇒ 에러의 구체적인 특성을 전달하는 의미있는 에러명 정의가 가능하다.
- 즉, 코드 명확성 및 가독성이 향상되어 코드를 이해하고 유지보수하기에 더 수월해진다.
🍄 Best practices for error handling
마지막으로, 타입스크립트에서의 바람직한 오류 처리 방법론에 대해 정리해보면 다음과 같다.
- Error의 성격을 정확히 나타내는 Error type을 선택할 것. (TypeError, SyntaxError, 사용자 정의 Error 등..)
- Error에 대한 유용한 정보를 제공하는 설명적인 오류 메시지를 포함할 것.
- 처리되지 않은 예외로 인한 프로그램 충돌을 피할 것.
- 에러가 조용히 무시되거나 숨겨지는 상황을 피할 것.
- 좋은 사용자 경험을 보장하는 방식으로 예외를 처리할 것. Error 발생 이후에도 프로그램이 계속 실행되거나 오류로부터 복구될 수 있도록 대체 메커니즘/경로를 고려해두자.
- 중앙 집중식 오류 처리 메커니즘을 만들거나, 오류 처리 기능을 제공하는 프레임워크 혹은 라이브러리를 활용할 것. Error를 포착 및 추적하기 위한 견고한 로깅 및 모니터링 메커니즘을 구현하라.
- 예상되는 Error 조건과 이러한 Error가 발생했을 때 코드의 동작을 모두 테스트할 것.
- TypeScript의 정적 타입 검사를 활용해 컴파일 시점에 오류를 포착할 것.