TIL_2023_08_18

이종현·2023년 8월 19일
0

Today_I_Learned

목록 보기
84/145
post-thumbnail

Today 요약

  1. 피드백 해결하기
  2. 자바스크립트 강의

1. What I did?

1.1 피드백 해결하기

api에서 어떤 에러를 던지느냐에 따라 catch하는 곳에서 제대로 받을 수 있다.

  • ApiClient에서 아무 생각없이 throw new Error 시전.. 하지만 axios 통신을 하고 있는 것이기 때문에 이 상황에서는 throw new AxiosError를 해줘야한다.
  • 그동안 에러가 정상적으로 동작하는지 제대로 체크도 안하고 있었다..(반성하라!!!!)
const getErrorMessage = (error: Error | AxiosError | null): string => {
  let errorMessage = UNKNOWN_ERROR

  if (error instanceof AxiosError) {
    console.log(error.response)
    if (error.response?.status === 404) {
      console.log(1)
      errorMessage = CHECK_MESSAGE_EMAIL
    }
    errorMessage = error.response?.data.message || UNKNOWN_ERROR
  } else {
    errorMessage = error?.message
  }

  return errorMessage
}

콘솔을 계속 찍어보면서 확인해보았다. thorw new AxiosError를 하고 난 뒤부터는 if문 안으로 들어오기는 했으나, error의 response 객체가 undefined로 할당된다. 네트워크 탭에서는 정상적으로 응답이 들어오는데, 왜 정보를 가지고 오지 못할까?

  • 일단 찾아낸 건.. interceptors를 사용하면서 문제가 발생한 것 같다. interceptors를 사용하면 응답 객체 일부 정보가 제대로 전달이 안될 수 도 있는 것 같다.
    this.api.interceptors.response.use(
      (response) => response,
      (error) => {
        throw new AxiosError(error)
      }
    )
    기존 코드를 아래와 같이 변경..
    this.api.interceptors.response.use(
      (response) => response,
      (error) => {
        if (error.response) {
          throw error
        } else {
          throw new AxiosError(error)
        }
      }
    );
    기존코드는 error가 존재해야지만 error를 던졌다.하지만 새로운 코드에서 error.response 객체가 없다면 새로운 걸 만들어서 error를 던짐으로써 해결했다. interceptors 때문에 정확하게 왜 에러의 response 객체를 못 받아오는지는 모르겠지만, 뭔가 우회하는 방식으로 찜찜하게 해결했다. (어차피 이후에 멘토님이 피드백 주셨던 request로 api를 전체적으로 변경해볼 것이기 때문에 거기서는 interceptors를 사용하지 않을 것이기에 이 부분은 이렇게 해결하는 것으로 마무리했다.)

accessToken을 제대로 넘겨주지 못함

  • 기존에 로그인을 할 때 AuthApi를 호출하는데, 그때 api를 클래스로 생성한다. 그때 토큰을 전달하고 있었는데 (결과적으로 그 부분도 토큰을 전달할 필요가 없었음..) 잘 생각해보면 로그인이 성공하고 나서 토큰이 발행되는데, 로그인을 요청하는 api는 그 이후에 다시 new를 통해서 인스턴스를 생성하지 않는다. 로그인을 클릭했을때만, api가 호출되는데, 클릭하는 시점에서는 인스턴스에 토큰이 없는 상태다. 이때 새로고침했을때 다시 토큰이 생성되는 이유는 컴포넌트가 다시 호출되면서 api를 다시 생성해서 저장된 토큰을 다시 가져오기 때문이였다.
  • saveToken
    • 그래서 아주 간단하게 해결하자면 setToken 함수를 이용해서 토큰을 set해주는 걸로 간단하게 해결가능

      setToken(localToken: string) {
      	this.api.defaults.headers.common = {
      	  ...this.api.defaults.headers.common,
      	  Authorization: `Bearer ${localToken}`
      	}
      }

      클래스에 setToken 메서드 정의하고 signin api와 통신해서 받아온 토큰을 저장하고 저장한 다음에 setToken 메서드를 불러온다. 이때 authApi에 전달하는 건 소용이 없다. 이 부분에 대해서 잘 생각해보면 로그인이 완료된 뒤에 토큰을 가지고 검증하는 것이기 때문에 authApi에서는 토큰을 굳이 가지고 잇을 필요가 없다. 그래서 todoApi에 토큰을 set 해주어야 한다. 그래야 그 토큰을 가지고 todoApp을 정상적으로 이용이 가능한 것이다.

      const saveToken = (token: string) => {
        localToken.save(token)
        todoApi.setToken(token)
      }

      이 부분까지가 멘토님의 첫 피드백이다.

  • request
    • 기존에 post, get 메서드 등에서 중복적으로 가지고 있던 then, catch 구문을 해결하기 위해서 멘토님 피드백이 두 가지가 있었는데, 하나는 interceptors였고, 하나는 request 였다. 그런데 interceptors는 상대적으로 해결하기가 쉬웠던 반면에 request는 내가 구글링을 잘못한건지 찾기가 어려웠다. 그런데 이번에 멘토링 진행하면서 멘토님이 request로 중복적인 코드도 해결하고 토큰도 전달되지 않았던 부분도 해결하는 부분을 직접 보여줬다. 그래서 그 부분을 복기하면서 정리해보고 다시 내 언어로 표현해보자.

      private request(
          method: Method,
          url: string,
          data: Record<string, string | boolean> = {},
          config?: AxiosRequestConfig
        ) {
          return this.api
            .request({
              method,
              url,
              data: method === 'post' || method === 'put' ? data : undefined,
              headers: {
                ...this.headers,
                Authorization: `Bearer ${localToken.get()}`,
                'Content-Type': 'application/json'
              },
              ...config
            })
            .then((res) => res)
            .catch((error) => {
              if (error.response) {
                throw error
              } else {
                throw new AxiosError(error)
              }
            })
        }

      request 메서드를 보자. 기존에 interceptors에서 처리했던 then, catch에 있던 로직도 가지고 왔고, 각 메서드 함수에서 사용했던 method와 url로 endpoint를 넘겨받을 수 있고, data를 통해 body를 받을 수 있게 해주었다. 멘토님 피드백은 params를 이용해 쿼리도 사용할 수 있게 하는 등의 활용법도 알려주셨는데, 현재 App에서는 불필요하다고 판단해서 제거했다. 그리고 headers에는 공통적으로 전달하고 싶은 token과 content-type을 전달했다. 이렇게 정의하고 난 뒤에사용하는 곳에서는 정말 기존과는 엄청나게 차이가 날 정도로 간략하게 사용할 수 있다.

      get(endpoint: string) {
        return this.request('get', endpoint)
      }
      
      post(endpoint: string, body: Record<string, string>) {
        return this.request('post', endpoint, body)
      }
      
      ...

      이렇게 사용함으로써 api를 호출하는 곳마다 토큰을 전달할 수 있다. 기존에는 todoApi에 토큰이 제대로 전달되지 않았다. 하지만 지금은 아니다. 흐름을 살펴보자.

    1. 로그인 버튼을 클릭하면 로그인 API가 호출된다. 이 때 post 메서드를 사용하여 로그인 정보를 서버에 전달하면, 해당 요청의 헤더에 토큰이 담겨져 있다.

    2. 로그인 성공 후, 투두 페이지로 리다이렉션된다. 이 때 투두 페이지의 컴포넌트가 렌더링되는 시점에서 getTodos가 호출된다.

    3. getTodos 메서드는 get 메서드를 사용하여 투두 목록을 가져온다. 이때도 토큰이 Authorization 헤더에 담겨져 있어 API 요청이 토큰과 함께 이루어진다.

      이 과정에서 리다이렉션 되기 전에 투두 컴포넌트가 렌더링되고 그때 이미 get 메서드를 사용해서 토큰을 가지고 오기 때문에 문제가 없이 동작할 수 있다. 하지만 기존에는 로그인이 성공하고 발행된 토큰을 todoApi에 다시 setToken해주는 부분이 없었기 때문에 문제가 발생했던 것이다.

      setToken과 request 두 가지를 비교해보면 아무래도 request쪽이 더욱 좋아보인다. 지금은 간단한 토이프로젝트지만, 앞으로 조금 더 규모가 있는 프로젝트를 진행하게 된다면 아무래도 request 처럼 관리를 하는 것이 효율적이라 생각한다.

return 타입 명시하기

  • return 타입이 추론될 때는 명시를 안하는 경우가 대부분이었다. 하지만 실제로 return을 해야하는 곳에서 하지 않은 코드가 있을때 return 타입을 명시하지 않으면 오류가 발생하지 않는데, return 타입을 명시해놓으면 함수에서 return을 사용하지 않으면 오류로 알려준다.
  • 실제로 프로젝트 코드에서 request의 return 타입을 명시하지 않았고, return없이 메서드를 작성했을 때는 오류가 발생하지 않았다. 대신 그 코드를 사용하는 곳에서 오류가 발생했다. 하지만 return 타입을 명시해놓으면 메서드 자체에서 오류가 발생한다. 그렇게 되면 오류를 좀 더 빨리 알아차릴 수 있게 된다.

2. What I Learned?

2.1 리액트 vs 제이쿼리

점점 복잡한 애플리케이션이 개발됨에 따라 제이쿼리는 직접적인 DOM 조작과 이벤트 처리에 한계를 경험하게 되었습니다. 이로 인해 제이쿼리로 개발한 코드는 상태 관리와 컴포넌트 분리의 어려움으로 인해 애플리케이션 유지보수에 어려움을 겪게 되었습니다. 반면 리액트는 컴포넌트 기반의 UI 구성과 상태 관리를 중심으로 하여 각 컴포넌트를 독립적으로 다루며, 상태 변화에 따라 업데이트됩니다. 또한 가상 DOM을 활용하여 변경 사항을 효율적으로 추적하고 반영하여 UI 업데이트를 처리합니다. 이러한 장점들로 리액트는 보다 복잡한 애플리케이션을 효율적으로 관리할 수 있습니다.


profile
데이터리터러시를 중요하게 생각하는 프론트엔드 개발자

0개의 댓글