자바스크립트 Axios interceptor 설정하기

프론트 깎는 개발자·2022년 12월 22일
5

axios

목록 보기
1/1

최근 작업중인 팀빌딩 서비스인 어울림 프로젝트 작업이 거의 마무리 되고, 마지막 QA 단계를 남겨두고 있다. QA 과정은 프로젝트에 참여한 멤버들이 직접 사이트를 이리저리 만져보며 버그를 발견해나가고 수정하는 방식으로 진행했다.

초기에 버그들을 리스트업을 하는 과정에서 모든 버그들이 발견되면 좋지만, 언제나 그렇듯,

버그를 잡으며 버그가 발생

하는 경우가 많다. 🥲

그런 경우에는 리스트업 항목에 추가하게 된다. 이 에러에는 어떤 문제점이 있었는지 보고 관찰해 보자.

사용자 인증 문제

댓글 작성, 게시글 작성 등의 컨텐츠 작성 기능은 처음부터 로그인이 되지 않은 상태면 input form 들이 노출되지 않도록 처리를 했었다. 그래서 로그인을 하지 않은 유저가 댓글을 작성하거나, 게시글을 작성할 수는 없었다. 반대로, 로그인이 되어 있다면, 여기에 이용되는 input form 들이 노출되도록 처리를 해서, 사용자가 데이터를 입력할 수 있도록 하였다.

로그인이 되어 있을 경우 아래 첫 번째 이미지 처럼 input form 이 나오고 댓글이 나오며, 그렇지 않은 경우 아래 두 번째 이미지 처럼 바로 댓글만 표출된다.

로그인 한 경우

로그인 하지 않은 경우

즉, 처음에 우리의 단순한 생각으로는 로그인이 되어 있지 않으면 input form 이 존재하지를 않으므로, 적어도 브라우저에서 정상적으로 서비스를 이용한다면 백엔드에서 401 Unauthorized 에러는 발생할 일이 없을 것이라고 생각을 한 것이다. (애초에 요청을 보낼 수가 없기에)

그런데 내가 이 버그를 발견 한 것은 의외의 시점에서 발생했다. 댓글 작성 부분에서 전날 밤까지 열심히 예외처리를 하고, 마지막 커밋을 하고 push 까지 해서 올린 후에 피곤한 나머지 바로 그 상태로 노트북을 덮고 잠에 들었다. 그리고 다음 날 아침에 일어나자마자 노트북을 다시 켜서, 그냥 똑같이 테스트 목적으로 댓글 작성 버튼을 그 어제 덮은 상태 그대로의 화면에서 눌렀는데, 등록이 되지 않는 것이었다.

콘솔을 열어보니 백엔드에서 401 Unauthorized 에러가 날아오고 있었다.

즉, 어젯밤 로그인을 한 시점에서는 받아둔 JWT 가 유효하기 때문에 (우리 JWT 의 유효기간은 30분이다) 댓글의 input form 은 생성되었지만, 하루밤이 지나면서, input form UI 가 render 된 상태는 그대로이지만, 백엔드 상에서 기존의 JWT 는 만료 처리가 된 것이었다!

JWT 의 유효기간이 30분 밖에 되지 않아, 사실 하룻밤 까지도 아니더라도, 잠시만 자리를 비우고 다시 인증이 필요한 어떤 액션을 취하면 충분히 종종 있을 수 있는 일이라고 생각했다. 그래서 UI 가 로그인 상태에 따라 다르게 rendering 이 되더라도, 반드시 모든 API 요청에 대한 예외처리를 꼼꼼히 해 두어야겠다는 것을 다시 한 번 깨닫고, 예외처리를 시작했다.

그런데, 댓글 작성에 대한 401 예외 처리를 하던 도중, 게시물 작성, 본인 프로필 변경 등 다른 인증을 요하는 화면에서도 동일한 현상이 발생할 수 있음을 깨달았고, 매번 똑같은 로직의 401 에러를 핸들링 하는 것은 코드 중복도가 올라가, 유지보수가 어려워질 것 같았다.

Axios Interceptor

그래서 찾다가 나온 해결 방법 Axios Interceptor 를 이용하는 것이었다. 우리는 기존에 통신 할 때 Axios 를 이용하고 있었고, 401 에러의 경우, 여러 군데에서 발생할 수 있는 에러였다. 그래서 공통으로 사용하는 serverAxios 객체에 프론트엔드에서 송신하는 request 와 수신하는 response 중간에 개입 (intercept) 를 해서 공통되게 어떤 로직을 처리할 수 있게 하는 것이다.

기존의 serverAxios 코드는 다음과 같았다.

// commonAxios.ts
// 프로젝트에서 공통적으로 사용하는 axios 객체
export const serverAxios = axios.create({
  baseURL: process.env.NEXT_PUBLIC_API_ENDPOINT,
});

여기서 아래와 같이 하단에 method 로 response interceptor 을 달아 주었다. (현재 상황으로는 공통적으로 어떤 request 에 로직을 추가하는 것이 아니라, (백엔드) 서버에서 오는 response 의 값을 보고 어떤 대응을 해야 하므로 .interceptors.response.use~ 를 달아주었다.)
(request interceptor 의 경우 .interceptors.request.use~ 를 이용한다.)

// commonAxios.ts
// 프로젝트에서 공통적으로 사용하는 axios 객체
export const serverAxios = axios.create({
  baseURL: process.env.NEXT_PUBLIC_API_ENDPOINT,
});

// response interceptor 추가
serverAxios.interceptors.response.use(
  function (response) {
    // 2xx 대 status code (정상 작동)시 할 일.

    return response;
  },
  function (error) {
    console.log("Axios interceptor for 4xx range status code called!");
    // 4xx 대 status code (오류 코드 발생) 시 할 일.
    if (error.response.status === 401) {
      // 로그인 한 지 너무 오래 돼서 form 들은 표시되지만 실제 백엔드에서는 401 에러 쏴줄 때는...
      // 메인페이지로 redirect
      Router.push("/");
    }

    return Promise.reject(error);
  }
);

이런 식으로 처리를 해 주면, 문제 상황과 같이 input form 은 있는데 401 에러가 오는 경우에 대해 공통적으로 메인 페이지로 redirect 시킬 수가 있다.

현재 이 프로젝트의 경우 Google 의 sign in with google 을 사용 중이라 특별히 로그인 페이지로 직접 redirect 하기 난감한 부분이 있었다. 만일 자체적인 로그인 페이지가 있다면, 로그인 페이지로 redirect 를 시키는 것이 UX 입장에서 더 매끄럽지 않을까 생각한다)

또, 현재는 단순히 redirect 처리만 하면 되긴 하지만, 에러 종류에 따라 또 다른 React 의 UI component 를 mount 하거나 unmount 시켜야 할 수도 있는데 (Modal 등), 이 부분은 또 고민해 볼 부분인 것 같다!


참고자료

profile
Comfort Zone 에서 벗어나자!

2개의 댓글

comment-user-thumbnail
2022년 12월 24일

읽기 쉽게 글을 잘 써 놓으셔서 프론트를 잘 모르는데도 편하게 읽었습니다..👍👍

1개의 답글