formdata 업로드 시 에러

이경택·2025년 2월 5일
3

파일 업로드 함수 리팩토링 과정에서 form data fetch 함수 오류 트러블 슈팅

  • 기존에 서버 액션 보내던 serverFetcher 함수에서 clientFetcher로 변경하는 과정에서 오류

변경 이유

브라우저에서 받아오는 쿠키를 서버 통신에서는 사용할 수가 없기 때문에 serverFetcher에서는 토큰을 직접 넣어줘야 하는 상황.

넣어주기 위해서는 page.tsx 에서 cookie를 가져와 props로 내려줘 전달하는 로직.

→ props 드릴링과 새로운 페이지에서 파일 업로드를 위해 매번 토큰을 넘겨줘야 하는 귀찮은 상황

export const fetcher = async <T>(
  url: string,
  options?: RequestInit
): Promise<T> => {
  const response = await fetch(url, {
    headers: {
      "Content-Type": "application/json",
    },
    ...options,
  });

  if (!response.ok) {
    const errorDetails = await response.json().catch(() => null);
    throw {
      status: response.status,
      message: errorDetails?.message || "요청 실패",
      statusCode: errorDetails?.statusCode || response.status,
    };
  }
  return response.json();
};

export const serverFetcher = async <T>(
  url: string,
  options?: RequestInit
): Promise<T> => {
  const baseUrl = process.env.NEXT_PUBLIC_NEXT_LOCAL_API_URL;

  const response = await fetch(`${baseUrl}${url}`, {
    headers: {
      "Content-Type": "application/json",
    },
    ...options,
  });

  if (!response.ok) {
    throw new Error(`API Error: ${response.status}`);
  }

  return response.json();
};

변경 중 만난 에러

rewrites 문제

next.config.js 파일에서 rewrites 함수를 통해 외부 통신시 필요한 API KEY를 숨길 수도 있고 사용자로부터 들어오는 요청 경로를 다른 경로로 매핑할 수도 있다.

프로젝트에서는 /api/:path* 로 들어오는 모든 경로를 ENV 설정 해둔 url로 매핑하는 방식으로 사용했다.

async rewrites() {
    return [
      {
        source: "/api/:path*",
        destination: `${getApiUrl(ENV.IS_LOCAL)}/api/:path*`,
      },
    ];
  },

여기서 file 업로드하는 함수는 /file/upload 라는 경로로 되어있어서 요청을 보내면 계속 404 에러가 떴다.

/api/file/upload 경로로 수정하니 잘 동작했다.

headers 문제

기존에 api 통신을 하기 위해 세팅해 두었던 fetcher라는 함수가 있는데, 그 함수는 기본적으로 헤더
content-typeapplication/json 으로 설정되어 있었고 나는 파일을 보내야 하기 때문에 multipart/form-data 로 보내야 했던 상황이다.

그래서 오버라이딩 해서 보냈을 때 아래 사진과 같은 에러가 났다.

export const uploadFile = async (
  data: FormData
): Promise<{ message: string; url: string }> => {
  const url = API_CONFIG.endpoints.file.upload;
  return fetcher(url, {
    method: "POST",
    headers: {
      "Content-Type": "multipart/form-data",
    },
    body: data,
  });
};

서버에서도 로그가 찍히지 않고, 네트워크 탭에서만 에러가 찍히면서 메시지를 보내줘서 디버깅 하기가 힘들었다.

찾아 본 결과, nestjs 서버에서 파일을 받을 때 multer 인터셉터를 사용해 파일을 받는데 이 때 헤더에 boundary 값이 없으면 인터셉터에서 임의로 에러를 리턴해주는 것이었다.

boundary값은 content-type을 따로 명시하지 않고 보내면 브라우저가 알아서 헤더를 설정하게 되고 아래 사진과 같이 boundary 값이 같이 가게 된다.

두 가지 문제가 겹쳐있어 디버깅과 문제 해결이 힘들었다ㅜ

profile
한 줄로 소개 할 수 없는 개발자

0개의 댓글

관련 채용 정보