TypeScript에서 catch 블록의 error 변수는
기본적으로 unknown 타입으로 처리되기 때문에 발생한 오류.
TypeScript의 타입 안전성을 위해 error.message에 직접 접근할 수 없다.
🖥️ MeetupForm.tsx
try {
await createMutation.mutateAsync(meetupFormData);
queryClient.invalidateQueries({ queryKey: ["meetups"] });
queryClient.invalidateQueries({ queryKey: ["headhuntings"] });
alert("모임 생성에 성공했습니다!");
router.push("/");
} catch (error: any) {
console.error("모임 생성 오류 발생:", error?.message || "알 수 없는 오류");
alert(`모임 생성 중 오류가 발생했습니다: ${error?.message || "알 수 없는 오류"}`);
} finally {
setIsSubmitting(false);
}
};
➡️ 1. error 타입 지정
catch (error: any)로 타입을 명시적으로 지정한다. TypeScript에서는 catch된 오류의 타입이 기본적으로 unknown이기 때문에, any로 지정하여 타입 체크를 우회.
➡️ 2. 안전한 접근
error?.message || "알 수 없는 오류" 구문을 사용
그런데 영 아리까리해서 더 알아보는 게 좋겠다.
➡️ YES. TypeScript 2.0 이후부터 catch 블록의 error 변수가 unknown 타입으로 처리된다.
타입 안정성을 높이기 위해서.
JavaScript에서는 어떤 타입의 에러든 throw될 수 있으므로, 실제로는 예상치 못한 타입도 들어올 수 있다.
JS 내장 객체로 실행 시간에 발생하는 오류를 나타낸다.
TS에서는 이를 인터페이스로 정의하고 있다.
🖥️ typescript
interface Error {
name: string;
message: string;
stack?: string;
}
🖥️ catch (error: unknown) {
const err = error as Error;
console.log(err.message);
}
(<타입>변수)(변수 as 타입) : JSX와 호환되어 더 많이 사용됨🖥️ typescript
try {
// 오류 발생 가능한 코드
} catch (error: unknown) {
// 방법 1: as 키워드 사용
const err = error as Error;
console.log(err.message); // 이제 Error 타입의 속성에 안전하게 접근 가능
// 방법 2: 직접 타입 단언
console.log((error as Error).message);
}
🖥️ typescript
try {
throw "문자열 에러"; // 문자열을 throw할 수도 있음
} catch (error: unknown) {
// 이 단언은 런타임 오류를 발생시킬 수 있음
console.log((error as Error).message); // 런타임에 undefined 출력될 수 있음
}
🖥️ typescript
try {
// 오류 발생 가능한 코드
} catch (error: unknown) {
// 먼저 타입 체크 후 단언
if (typeof error === 'object' && error !== null && 'message' in error) {
const err = error as Error;
console.log(err.message);
} else {
console.log('알 수 없는 오류 발생');
}
}
🖥️ typescript
class ApiError extends Error {
statusCode: number;
constructor(message: string, statusCode: number) {
super(message);
this.name = 'ApiError';
this.statusCode = statusCode;
}
}
try {
throw new ApiError('API 요청 실패', 404);
} catch (error: unknown) {
// ApiError로 타입 단언
const apiError = error as ApiError;
console.log(apiError.statusCode); // 404
}
🖥️ catch (error: unknown) {
if (error instanceof Error) {
console.log(error.message);
}
}
타입 가드는 특정 스코프 내에서 변수의 타입을 보장하는 런타임 검사다.
타입 가드를 사용시 TS는 해당 스코프 내에서 변수를 더 구체적인 타입으로 좁힐 수 있다.
instancof는 JS연산자로, 객체가 특정 클래스의 인스턴스인지 확인한다.
이는 클래스 계층 구조를 확인할 수 있어 유용하다.
(객체 instanceof 생성자)
🖥️ typescript
try {
// 오류 발생 가능한 코드
} catch (error: unknown) {
if (error instanceof Error) {
// 이 블록 내에서 TypeScript는 error를 Error 타입으로 인식
console.log(error.message); // 안전하게 접근 가능
console.log(error.stack); // 안전하게 접근 가능
} else {
// error가 Error 인스턴스가 아닌 경우
console.log('알 수 없는 오류:', error);
}
}
🖥️ typescript
try {
// 오류 발생 가능한 코드
} catch (error: unknown) {
if (error instanceof TypeError) {
console.log('타입 오류:', error.message);
} else if (error instanceof SyntaxError) {
console.log('구문 오류:', error.message);
} else if (error instanceof ReferenceError) {
console.log('참조 오류:', error.message);
} else if (error instanceof Error) {
console.log('일반 오류:', error.message);
} else {
console.log('알 수 없는 오류:', error);
}
}
🖥️ typescript
class NetworkError extends Error {
constructor(message: string) {
super(message);
this.name = 'NetworkError';
}
}
class ValidationError extends Error {
fields: string[];
constructor(message: string, fields: string[]) {
super(message);
this.name = 'ValidationError';
this.fields = fields;
}
}
try {
const random = Math.random();
if (random < 0.33) {
throw new NetworkError('네트워크 연결 실패');
} else if (random < 0.66) {
throw new ValidationError('유효성 검사 실패', ['email', 'password']);
} else {
throw new Error('일반 오류');
}
} catch (error: unknown) {
if (error instanceof NetworkError) {
console.log('네트워크 오류:', error.message);
// 네트워크 재연결 로직
} else if (error instanceof ValidationError) {
console.log('유효성 검사 오류:', error.message);
console.log('문제 필드:', error.fields);
// 유효성 오류 처리 로직
} else if (error instanceof Error) {
console.log('일반 오류:', error.message);
} else {
console.log('알 수 없는 오류:', error);
}
}
🖥️ typescript
// 타입 서술 함수
function isNetworkError(error: unknown): error is NetworkError {
return error instanceof NetworkError;
}
function isValidationError(error: unknown): error is ValidationError {
return error instanceof ValidationError;
}
try {
// 오류 발생 가능한 코드
} catch (error: unknown) {
if (isNetworkError(error)) {
// 여기서 error는 NetworkError 타입
console.log(error.message);
} else if (isValidationError(error)) {
// 여기서 error는 ValidationError 타입
console.log(error.fields);
}
}
🖥️ typescript
async function fetchData() {
try {
const response = await fetch('/api/data');
if (!response.ok) {
throw new ApiError(`API 오류: ${response.status}`, response.status);
}
return await response.json();
} catch (error: unknown) {
if (error instanceof ApiError) {
if (error.statusCode === 401) {
// 인증 오류 처리
redirectToLogin();
} else if (error.statusCode === 404) {
// 리소스 없음 처리
showNotFoundMessage();
} else {
// 기타 API 오류
showErrorMessage(error.message);
}
} else if (error instanceof Error) {
// 네트워크 오류 등 일반 오류
showErrorMessage(`요청 실패: ${error.message}`);
} else {
// 알 수 없는 오류
showErrorMessage('알 수 없는 오류가 발생했습니다');
}
// 오류를 다시 throw하여 호출자에게 전파할 수 있음
throw error;
}
}
🖥️ catch (error: any) {
console.log(error.message);
}
🖥️ catch (error: unknown) {
console.log((error as Error)?.message || "알 수 없는 오류");
}