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로 할당된다. 네트워크 탭에서는 정상적으로 응답이 들어오는데, 왜 정보를 가지고 오지 못할까?
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를 사용하지 않을 것이기에 이 부분은 이렇게 해결하는 것으로 마무리했다.)그래서 아주 간단하게 해결하자면 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)
}
이 부분까지가 멘토님의 첫 피드백이다.
기존에 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에 토큰이 제대로 전달되지 않았다. 하지만 지금은 아니다. 흐름을 살펴보자.
로그인 버튼을 클릭하면 로그인 API가 호출된다. 이 때 post
메서드를 사용하여 로그인 정보를 서버에 전달하면, 해당 요청의 헤더에 토큰이 담겨져 있다.
로그인 성공 후, 투두 페이지로 리다이렉션된다. 이 때 투두 페이지의 컴포넌트가 렌더링되는 시점에서 getTodos
가 호출된다.
getTodos
메서드는 get
메서드를 사용하여 투두 목록을 가져온다. 이때도 토큰이 Authorization
헤더에 담겨져 있어 API 요청이 토큰과 함께 이루어진다.
이 과정에서 리다이렉션 되기 전에 투두 컴포넌트가 렌더링되고 그때 이미 get 메서드를 사용해서 토큰을 가지고 오기 때문에 문제가 없이 동작할 수 있다. 하지만 기존에는 로그인이 성공하고 발행된 토큰을 todoApi에 다시 setToken해주는 부분이 없었기 때문에 문제가 발생했던 것이다.
setToken과 request 두 가지를 비교해보면 아무래도 request쪽이 더욱 좋아보인다. 지금은 간단한 토이프로젝트지만, 앞으로 조금 더 규모가 있는 프로젝트를 진행하게 된다면 아무래도 request 처럼 관리를 하는 것이 효율적이라 생각한다.
점점 복잡한 애플리케이션이 개발됨에 따라 제이쿼리는 직접적인 DOM 조작과 이벤트 처리에 한계를 경험하게 되었습니다. 이로 인해 제이쿼리로 개발한 코드는 상태 관리와 컴포넌트 분리의 어려움으로 인해 애플리케이션 유지보수에 어려움을 겪게 되었습니다. 반면 리액트는 컴포넌트 기반의 UI 구성과 상태 관리를 중심으로 하여 각 컴포넌트를 독립적으로 다루며, 상태 변화에 따라 업데이트됩니다. 또한 가상 DOM을 활용하여 변경 사항을 효율적으로 추적하고 반영하여 UI 업데이트를 처리합니다. 이러한 장점들로 리액트는 보다 복잡한 애플리케이션을 효율적으로 관리할 수 있습니다.