June.

11월 안에는 꼭...

Portfolio

About

Get a catch block error message with TypeScript (TypeScript로 catch 블록 오류 메시지 받기)

2023/02/02
번역

3 min read

Get a catch block error message with TypeScript (TypeScript로 catch 블록 오류 메시지 받기)

이 글은 Kent C. Dodds가 작성한 Get a catch block error message with TypeScript의 원본 게시물을 번역한 것입니다.

좋아요. 이것에 대해 이야기해볼까요?

1const reportError = ({ message }) => { 2 // 로깅 서비스에 오류를 보냅니다... 3}; 4 5try { 6 throw new Error("Oh no!"); 7} catch (error) { 8 // 계속 진행하지만 report합니다. 9 reportError({ message: error.message }); 10}

아직까지 좋아보이나요? 아마 JavaScript라서 잘되는 것 같네요. TypeScript를 써 봅시다.

1const reportError = ({ message }: { message: string }) => { 2 // 로깅 서비스에 오류를 보냅니다... 3}; 4 5try { 6 throw new Error("Oh no!"); 7} catch (error) { 8 // 계속 진행하지만 report합니다. 9 reportError({ message: error.message }); 10}

catch문 안에 있는 reportError 함수는 행복하지 않아보이네요. 특히 error.message 부분이 안타깝게 보입니다. 그 이유는 (최근에) TypeScript에서 오류 타입을 기본적으로 unknown으로 설정하기 때문이에요. 이게 도대체 뭘까요! 오류의 세계에서는 발생하는 오류 유형에 대해 제공할 수 있는 보장이 많지 않습니다. 사실, 이것이 promise 거부에서의 catch(error => {})의 오류 Generic을 제공할 수 없는 이유입니다(Promise<ResolvedValue, NopeYouCantProvideARejectedValueType>). 실제로, throw된 것이 오류일 것이라고 가정할 수도 없습니다. 그 에러는 무엇이든 될 수 있습니다. 니다.

1throw "뭐야!?"; 2throw 7; 3throw { what: "뭐야" }; 4throw null; 5throw new Promise(() => {}); 6throw undefined;

진짜로 어떤 타입의 무엇이든지 던져도 됩니다. 그래서 쉽죠? 오류에 대한 타입 주석(annotation)을 추가해서 이 코드가 오류만 던지는 것이라고 표시할 수 있지 않을까요?

1try { 2 throw new Error("이런!"); 3} catch (error: Error) { 4 // 계속 진행하지만 report합니다. 5 reportError({ message: error.message }); 6}

그렇게 생각하긴 아직 이릅니다! 그러면 다음과 같은 TypeScript 컴파일 오류가 발생합니다.

1Catch clause variable type annotation must be 'any' or 'unknown' if specified. ts(1196)

이 에러가 나온 이유는 코드에서 다른 것이 던져질 가능성이 없다고 보이지만, JavaScript는 좀 웃기기 때문에 외부 라이브러리에서 에러 생성자를 몽키 패치(monkey-patching)하여 다른 것을 던질 수 있습니다.

1// 이렇게 말이죠. 2Error = function () { 3 throw "Flowers"; 4} as any;

그렇다면 개발자는 무엇을 해야 할까요? 우리가 할 수 있는 최선은 아래와 같습니다.

1try { 2 throw new Error('Oh no!') 3} catch (error) { 4 let message = 'Unknown Error' 5 if (error instanceof Error) message = error.message; 6 // 계속 진행하지만 report합니다. 7 reportError({ message }); 8}

좋네요! 이제 TypeScript는 우리에게 소리를 지르지 않고, 더 중요한 건 완전히 예상치 못한 일이 발생할 수 있는 경우를 처리하고 있네요. 아마도 우리는 다음과 같이 더 좋게 할 수 있을 것 같네요.

1try { 2 throw new Error("Oh no!"); 3} catch (error) { 4 let message; 5 if (error instanceof Error) message = error.message; 6 else message = String(error); 7 // 계속 진행하지만 report합니다. 8 reportError({ message }); 9}

따라서 여기에서 오류가 실제 Error 개체가 아닌 경우 오류를 문자열화하면 유용하게 사용할 수 있습니다.

그런 다음 이걸 모든 catch 블록에서 사용할 유틸리티로 바꿀 수 있습니다.

1function getErrorMessage(error: unknown) { 2 if (error instanceof Error) return error.message; 3 return String(error); 4} 5 6const reportError = ({ message }: { message: string }) => { 7 // 로깅 서비스에 오류를 보냅니다... 8}; 9 10try { 11 throw new Error("Oh no!"); 12} catch (error) { 13 // 계속 진행하지만 report합니다. 14 reportError({ message: getErrorMessage(error) }); 15}

위와 같은 내용들은 제 프로젝트에서 저에게 도움이 되었습니다. 바라건대 그것은 이 글을 보는 당신에게도 도움이 될 것입니다.

업데이트: Nicolas는 처리 중인 오류 개체가 실제 오류가 아닌 상황을 처리하기 위한 좋은 제안을 했습니다. 그리고 나서 Jesse는 가능한 경우 오류 개체를 문자열로 만들 것을 제안했습니다. 따라서 결합된 제안은 모두 다음과 같습니다.

1type ErrorWithMessage = { 2 message: string; 3}; 4 5// 요기서 "is" 키워드는 Type Predicate, 직역하자면 ‘타입 단정’이라 할 수 있어요. 6function isErrorWithMessage(error: unknown): error is ErrorWithMessage { 7 return ( 8 typeof error === "object" && 9 error !== null && 10 "message" in error && 11 typeof (error as Record<string, unknown>).message === "string" 12 ); 13} 14 15function toErrorWithMessage(maybeError: unknown): ErrorWithMessage { 16 if (isErrorWithMessage(maybeError)) return maybeError; 17 18 try { 19 return new Error(JSON.stringify(maybeError)); 20 } catch { 21 // MaybeError를 문자열화(stringifying)하는 오류가 있는 경우 대체(fallback)코드에요. 22 // 예를 들면 순환 참조같은게 있을 수 있겠네요. 23 return new Error(String(maybeError)); 24 } 25} 26 27function getErrorMessage(error: unknown) { 28 return toErrorWithMessage(error).message; 29}

편리하네요!

결론

TypeScript에 재미있는 부분이 있지만 불가능하다고 생각하고 TypeScript에서 컴파일 오류나 경고를 무시하지 않는다는 점을 기억하세요. 그게 가장 중요한 것 같아요. 대부분의 경우 예상치 못한 일이 발생할 수 있으며 TypeScript는 이러한 드문 경우를 처리하도록 하는 꽤 좋은 작업을 수행하죠. 그리고 아마 우리가 생각하는 것 만큼 가능성이 낮지 않다는 것을 알게 될 거에요.

관련 포스트가 1개 있어요.

각각을 사용할 때의 규칙

번역
translation/useeffect-vs-uselayouteffect cover image
2023/01/28

useEffect vs useLayoutEffect

profile

정현수.

Currently Managed

Currently not managed

© 2024. junghyeonsu all rights reserved.