이 포스팅은 개인 프로젝트를 진행하면서 공부하게 된 내용 임에도 불구하고
해당 상황에 대한 문제 해결 방식이 아닌 , 기본 개념을 설명하는 방식으로 작성합니다
JS에서는 catch문에서 인자로 전달받는 error에 대해 아무런 타입이 필요가 없었지만
TS에서는 타입 지정이 필요합니다
[typescript] Object is of type 'unknown'.ts(2571) (error object)
기존에는 catch로 넘어온 에러 객체의 타입이 any 타입 이었기 때문에, JS 처럼 사용해도 에러가 발생하지 않았습니다.
하지만 타입스크립트 v4.4 부터는 에러 객체의 타입이 unknown 타입으로 정의 되어서 JS 처럼 바로 에러 객체에 접근하게 되면 에러가 발생하게 됩니다
throw 문을 이용해 에러 발생을 시킬 때 에러에는 아래와 같이 그 어떤 타입도 들어갈 수 있기 때문에
throw "What the!?";
throw 7;
throw { wut: "is this" };
throw null;
throw new Promise(() => {});
throw undefined;
아래처럼 catch에 넘어오는 에러 객체의 타입을 지정할 수도 없습니다
catch (error: {
status: string;
data: string;
})
이에 대한 간편한 해결책으로 type assertion이 있습니다
반환된 에러 객체의 타입을 지정하고 catch문 안에서 사용할 때 해당 타입으로 단언하고
사용하는 것입니다
그렇지만 알다시피 TS에서 type assertion은 그리 좋은 선택지가 아닙니다
unknown 타입일 때 가끔 as 연산자를 사용하긴 하지만, 사실 TS에서 as 도 any 만큼 지양하는것이 옳습니다
그렇다면 어떻게 해결해야 할까요?
이럴때 가장 좋은 방법은 instanceof 를 이용하여 각 에러 객체 타입에 따른
타입 가드
를 하는 것입니다
try 부분에서 발생한 에러가 발생하게 되면 아래와 같이 작성할 수 있습니다.
catch로 넘어온 값이 표준 Error 객체인지 확인을 하고 , 기본 프로퍼티인 .message를 출력하는 것입니다
여기서 , 보통 에러 객체라고 하면 아래의 종류들이 있는데
new Error() 와도 같은 JS 표준 에러 객체 종류 (ReferenceError , SyntaxError , TypeError 등..)
각 라이브러리 마다 사용되는 에러 객체 (Axios 사용시 , HTTP 요청과 관련된 에러 정보를 담고 있는 AxiosError 객체)
커스텀 에러 클래스로 만든 객체
기본적으로 JS 표준 에러객체인 new Error()는 모든 에러의 프로토타입입니다.
즉 , 모든 에러 객체들은 Error 객체를 상속해서 사용하게 되므로
error instanceof Error
를 사용하면 모든 에러 객체가 true값이 되므로 모든 에러 종류에 대해 일괄적으로 처리할 수 있습니다
예를들어 각 에러 객체마다 구조가 다르겠지만
message프로퍼티의 경우 모든 에러 객체들이 상속받아 사용하므로 일괄적으로 사용할 수 있습니다
위의 로직을 모든 catch 블록에서 사용할 수 있는 유틸 함수로도 만들어줄 수 있습니다
💡 모든 에러 객체는 (커스템 에러 , HTTP 통신 라이브러리 에러 객체 등…) 전부 Error객체를 상속받는 것이므로 위의 error instanceof Error 에 들어오게 됩니다.
그렇지만 위처럼 하는 것은 그렇게 좋은 방법은 아닙니다
에러 객채의 구조가 전부 다 다르고 message프로퍼티만 사용하는것이 아니기 때문에
각 에러객체의 구조에 맞춰서 사용하는게 좋습니다
error instanceof Error 가 아닌 , 각 에러 객체에 따라 다르게 적용합니다
ex ) error instanceof NicknameDuplicationError(커스텀에러)
만약 Axios를 사용한다고 가정 해 봅시다
Axios를 사용하게 되면 반환 되는 에러 객체는 JS 표준 에러 객체가 아닌
HTTP 클라이언트 라이브러리의 에러 객체인 AxiosError가 들어오게 됩니다
(자세한 설명은 catch 인자로 넘어온 에러 객체는 뭘까? )
객체의 프로퍼티는 다음과 같습니다.
💡 위의 속성을 가진 에러 타입을 사용해야 하지만, 모든 속성을 처음부터 전부 기재하는 것보다 필요할 때마다 추가하는 것이 좋습니다.
현재 제가 저기서 사용하는 프로퍼티는 response , response.data , response.status 입니다.
그러므로 아래와 같이 진행해 보았습니다.
그렇지만 이 방법은 이상하게도 에러를 발생시킵니다.
왜냐하면 타입스크립트에서 인터페이스는 순전히 타입스크립트 문법이므로
컴파일 되면 코드가 사라지게 된다. 그러므로 instanceof 할 개체가 없어지기 때문입니다.
그렇지만 , 클래스는 자바스크립트에서도 있는 문법이라, 컴퍼일 돼도 코드가 남게 됩니다!
그래서 커스텀 에러 객체를 사용하려면 커스텀 에러 클래스
를 사용해야 합니다
생성자에 사용된 부분을 보면
이제 위의 커스텀 에러 객체를 바탕으로 타입가드를 사용해서
에러 정보를 사용할 수 있습니다.
사실, Axios는 전용 에러 객체인 AxiosError 객체가 따로 존재합니다
그러므로 위처럼 직접 Axios에 대한 에러 객체를 만드는 것보다
아래처럼 AxiosError 객체를 직접 사용하는 것이 더 간편합니다.
catch (error) {
if (error instanceof AxiosError) {
console.log(error.response?.status);
console.log(error.response?.data);
...
}
}
그렇지만 Axios와 달리 ,
만약 해당 상황에 따른 에러 객체가 존재하지 않는다면,
이땐 커스텀 에러 클래스를 직접 만들어서 적용할 수 있습니다.
예를 들어,
아래는 사용 예시 입니다
자연스레 발생하는 에러( 변수 참조 에러 , HTTP통신 에러 등 ) 에서는
이미 이에 따른 내장 에러 객체가 catch의 error 객체에 전달됩니다.
그러므로 우리가 만든 커스텀 에러 객체를 사용하기 위해서는
조건에 따라 해당 에러를throw를 통해 명시적으로 에러를 발생시켜야 합니다.
예를 들어 input창에 상대방 닉네임을 입력하고 API 호출을 해야 하는데
현재 로그인된 내 닉네임을 입력했다면
throw 를 명시해서 NicknameDuplicationError 커스텀 에러를 직접 던질 수 있습니다.
출처 :
좋은글 감사합니다.