본문 바로가기

programming/Javascript

Fetch try-catch : 400 은 response, 401은 catch error 로 빠진다면?

 

[현상황]

jwt accessToken / refreshToken 을 사용하여 로그인 검증을 진행하고 있음

API 요청시 400 Bad Request 와 같은 응답은 response 로 잘 받아지고 있으나, 

accessToken 이 만료된 후, API 요청을 했을 때 401 Unauthorized 응답, 근데 response 에 접근하지 못하고 try-catch 에서 catch 에러로 빠지며, 스크립트로, status code, error message 에 접근하지 못함.

그냥 "failed to Fetch" 메시지로 찍힘

 

 

[기대상황]

API 요청시 400 / 401 모두 백엔드 서버에서 응답하는 response 객체로, HTTP status code 와 에러메시지로 접근하여 에러를 핸들링하고,

네트워크 레벨 에러, CORS 에러만 catch 에러로 핸들링하고자 함

 

 

Axios Instance 처럼 사용하고 있는 custom fetch Instance 

[코드]

import { getCookie } from "cookies-next";
import { signIn, getSession } from "next-auth/react";
import { CustomError } from "./customError";

interface FetchOptions extends RequestInit {
  headers?: HeadersInit;
}

let lastSession: any = null;

const fetchWithToken = async (
  url: string,
  method: string = "GET",
  body: any = null,
  options: FetchOptions = {},
): Promise<Response | undefined> => {
  // 현재 들고 있는 최근 세션 없는 경우만 session 요청
  if (lastSession == null) {
    const session = await getSession();
    if (session) {
      lastSession = session;
    } else {
      // 세션이 없는 경우 - 로그인 안한 상태
      // throw new CustomError("Unauthorized", 401);
    }
  }

  // 세션 정보 있는 경우 api 콜
  const headers = {
    ...options.headers,
    "Content-Type": "application/json",
    Country: getCookie("country") || "us",
    Language: getCookie("language") || "en",
    Authorization: `Bearer ${(lastSession?.user as any).accessToken}`,
  };

  const fetchOptions: FetchOptions = {
    ...options,
    method,
    headers,
    credentials: "include",
  };

  if (body) {
    fetchOptions.body = JSON.stringify(body);
  }

  try {
    const response = await fetch(`${process.env.NEXT_PUBLIC_CHAT_SERVER_API_URL}${url}`, fetchOptions);
    console.log("fetch response : ", response);
    return response;
  } catch (error) {
    // Promise 자체가 rejected (network error, CORS 등)
    console.log("fetch catch error : ", error);
    throw error;
  }
};

export { fetchWithToken };

 

 

백엔드로부터 정상 응답 (에러 응답도 포함 400, 401, 404, 등) 을 받는다면, fetch 의 response 에 접근할 수 있으나,

 

 

백엔드에서 정상 에러 응답 처리한 400 에러,

응답헤더에 CORS 처리가 잘 되어있다

 

 

프론트에서 fetch 의 response 로 받아서 console.log 에 response 가 status 코드와 함께 잘 찍힘

 

 

백엔드에 요청조차 하지 못하거나 (network level error),  CORS 처리가 되지 않은 응답을 받는다면, 바로 catch 에러 구문으로 떨어지게 된다.

 

크롬 개발자 툴에서 네트워크 탭을 확인해 보면, 401 Unauthorized 라고 백엔드로부터 정상적인 에러응답을 받은것 처럼 보인다.

여기서 헷갈리기 쉬웠던 것이 CORS 에러도 401 에러로 찍힌다.

응답헤더도 확인해보면 CORS 처리가 안되어 있는 것을 확인할 수 있다.

 

실제로 catch 구문으로 떨어져서, 프론트의 스크립트 상으로 status code와 error 메시지에 접근할 수 없고, 그저 fetch 자체에서 실패 처리한 "Failed to fetch" 메시지만 확인할 수 있다.

 

 

[해결책]

백엔드에서 실제 데이터 관련한 응답에서 에러응답을 리턴할때는 CORS 처리가 잘되어있었는데,

JWT 만료관련된 에러 응답을 리턴할 때는 CORS 처리가 안되어있는 것을 확인했다.

백엔드에서 응답헤더에 CORS 처리를 해주면 해결!

 

 

[포인트]

CORS 에러랑, 백엔드와 약속한 토큰만료시 리턴할 에러코드가 동일하게 401 이다.

이 때문에 CORS 에러 때문에 catch 에러로 떨어진 다는 것을 빠르게 캐치하지 못했다.