TypeScript try-catch 에서의 Error 처리하기

user profile img

신현호

TypeScript
Post Thumbnail

목차

    서론

    여느때와 같이 빈둥거리다가 코딩을 하고있었는데, try-catch문에서 계속 빨간줄을 그어서 뭔가 하고 봤더니 TS Error가 발생하더라구요
    catch에서 사용하는 Error에 대한 타입 에러였는데, 해당 문제에 대해서 학습한 내용을 몇 자 적어보려고 합니다.

    try-catch에서 Error는 어떤 타입인가?

    TS 4.4버전 이전까지 TS에서는 try/catch 에서의 Error를 any타입으로 지정하고 있었습니다만, 4.4버전 이후부터는 unknown타입으로 변경되었습니다.
    따라서 제가 다음과 같이 하려고 했을때 지속적으로 에러를 발생시킨 것 이었습니다.

    ts

    export const registerUser = createAsyncThunk(
      'user/registerUser',
      async (body: RequestBody, thunkAPI) => {
        try {
          const response = await axiosInstance.post(`/users/register`, body)
    
          return response.data
        } catch (error) {
          console.log(error)
    
          return thunkAPI.rejectWithValue(error.response.data)
        }
      }
    )
    

    타입이 unknown이기때문에, error.response에 접근하려고 하면 타입에러가 발생하는것입니다.

    여기까지 분석이 완료되었다면, 우리는 문제의 본질을 파악할 수 있습니다. 바로, unknown타입을 핸들링하기 라는걸요.
    따라서, 이 unknown 타입을 핸들링하는 방법에 대해서 알아보겠습니다 :)

    any타입 사용하기

    ts

    export const registerUser = createAsyncThunk(
      'user/registerUser',
      async (body: RequestBody, thunkAPI) => {
        try {
          const response = await axiosInstance.post(`/users/register`, body)
    
          return response.data
        } catch (error: any) {
          console.log(error)
    
          return thunkAPI.rejectWithValue(error.response.data)
        }
      }
    )
    

    가장 간단한 방법입니다, any 타입을 사용함으로써 해당 에러를 방지할 수 있습니다.. 만
    타입스크립트를 사용하면서 any 타입을 도배한다는 건, 타입스크립트를 사용하는분들이라면 반드시 지양해야할 행동임을 알고계실거라고 생각합니다.

    또한 any타입으로 도배한다면 Eslint에서도 지속적으로 경고창을 띄우고, 이를 피하기위해 해당 경고까지 무시해버린다면 사실상 TSEslint를 사용하지 않는것과 마찬가지겠죠?

    그러니 다음 방법으로 넘어가봅시다.

    타입을 단언 하기

    ts

    export const registerUser = createAsyncThunk(
      'user/registerUser',
      async (body: RequestBody, thunkAPI) => {
        try {
          const response = await axiosInstance.post(`/users/register`, body)
    
          return response.data
        } catch (error) {
          const customError = error as CustomError
          console.log(error)
    
          return thunkAPI.rejectWithValue(error.response.data)
        }
      }
    )
    

    CustomError라는 커스텀 타입(인터페이스)를 생성하여 타입 단언을 하는 방법이 있을 수 있겠습니다.
    저같은경우는 error.response만 사용할 것이기 때문에 타입을 작성한다면 다음과 같이 작성할 것 같네요

    ts

    export default class CustomError extends Error {
      response?: {
        data: unknown
        status: number
        headers: string
      }
    }
    

    하지만, 이 선택지 역시나 any보단 낫겠지만 타입을 단언한다는 점에서 다른 좋은 대안이 있다면 지양하는것이 좋습니다.

    이번에도 다음 방법으로 넘어가봅시다.

    타입 가드를 사용 하기

    추천되는 방법 중 하나로는 typeofinstanceof를 사용하여 타입가드 하는 방법이 있습니다.

    ts

    export const registerUser = createAsyncThunk(
      'user/registerUser',
      async (body: RequestBody, thunkAPI) => {
        try {
          const response = await axiosInstance.post(`/users/register`, body)
    
          return response.data
        } catch (error) {
          console.log(error)
    
          if (error instanceof CustomError) {
            return thunkAPI.rejectWithValue(error.response?.data)
          }
        }
      }
    )
    

    ts

    export default class CustomError extends Error {
      response?: {
        data: unknown
        status: number
        headers: string
      }
    }
    

    커스텀 에러 클래스를 정의한 뒤, instanceof를 사용하여 타입가드를 했습니다.
    이렇게 타입가드를 사용하면 any도, 타입 단언도 모두 사용하지 않고 unknown타입을 핸들링할 수 있습니다.

    다만, axios의 경우 커스텀 클래스를 사용하지 않고도 편하게 해당 문제를 해결할 수 있습니다.

    axios.isAxiosError()

    ts

    export const registerUser = createAsyncThunk(
      'user/registerUser',
      async (body: RequestBody, thunkAPI) => {
        try {
          const response = await axiosInstance.post(`/users/register`, body)
    
          return response.data
        } catch (error) {
          console.log(error)
    
          if (isAxiosError(error)) {
            return thunkAPI.rejectWithValue(error.response?.data)
          }
        }
      }
    )
    

    Axios 공식문서에서는 axios error의 타입 가드를 위해 axios.isAxiosError() 를 제공합니다.
    해당 함수 사용시 axios에서는 간단하게 타입 가드를 할 수 있습니다!

    참고

    Profile Image

    신현호

    Frontend Developer

    프론트엔드 개발자를 꿈꾸고 있는 대학생입니다. 끊임없이 배우고 성장하는 개발자가 되기 위해 노력하고 있습니다.