문제 상황

export const getServerSideProps = async (): Promise<
  GetServerSidePropsResult<data>
> => {
  try {
    const data = await API호출();
    return {
      props: {
        data,
      },
    };
  } catch {
    return {
    //401 에러 처리..
    //500 에러 처리..
      redirect: {
        destination: "/500",
        permanent: false,
      },
    };
  }
};
 

기존의 서버사이드렌더링의 경우, 일반적인 오류들에 대해서 처리를 하는 로직이 공통되는 부분들이 많다. 기본적으로 페이지 렌더링 과정에서 GET 요청에서도 사용자의 인가가 필요한 부분에서는 401 에러에 대한 처리를 해주고, 500에러 등 처리를 해줘야 하는 부분에 대해서 500 에러 처리를 해야 하다 보니까 결국 보일러플레이트가 많아지는 문제가 생기게 된다.

해결하기

api 호출 함수를 받아 일반적인 에러를 처리하는 로직을 처리할 수 있도록 고차함수를 만들어서 서버사이드 렌더링 과정에서 범용적으로 사용할 수 있도록 개선했다.

import { AxiosResponse, AxiosPromise, isAxiosError } from "axios";
import { GetServerSidePropsResult, GetServerSidePropsContext } from "next";
 
const LOGIN_PATH: string = "/login";
export async function withCheckInServer<T>(
  fetchCall: () => AxiosPromise<T>,
  options?: {
    // 404 대신 다른 처리를 원할 때
    onError?: (
      // eslint-disable-next-line no-unused-vars
      error: unknown,
      // eslint-disable-next-line no-unused-vars
      context?: GetServerSidePropsContext
    ) => GetServerSidePropsResult<T>;
    // 에러 로깅 비활성화
    context?: GetServerSidePropsContext;
  }
): Promise<GetServerSidePropsResult<T>> {
  const { onError, context } = options || {};
 
  try {
    // fetchCall이 함수인지 확인
    if (typeof fetchCall !== "function") {
      throw new Error(
        "fetchCall must be a function that returns an AxiosPromise"
      );
    }
 
    const response: AxiosResponse<T> = await fetchCall();
 
    // 응답 데이터 검증
    if (typeof response !== "object") {
      throw new Error("Invalid response format");
    }
 
    return {
      props: response.data, // response.data를 사용해야 함
    };
  } catch (error) {
    if (isAxiosError(error)) {
      const status = error.response?.status;
 
      switch (status) {
        case 401:
        case 403:
          return {
            redirect: {
              destination: LOGIN_PATH,
              permanent: false,
            },
          };
        case 404:
          return {
            notFound: true,
          };
 
        case 500:
          return {
            redirect: {
              destination: "/500",
              permanent: false,
            },
          };
      }
    }
 
    // 커스텀 에러 핸들러가 있는 경우
    if (onError) {
      return onError(error, context);
    }
 
    // 기본 처리: 404
    return {
      redirect: {
        destination: "/500",
        permanent: false,
      },
    };
  }
}

기존의 API 호출 로직에 대해 공통되는 부분들에 대해서 모두 재사용을 방지할 수 있는 만큼, 훨씬 편해졌다.