토이프로젝트를 진행하던 와중 공통 에러는 어떻게 처리할 것인가에 곰곰이 생각해보았다.
유튜브에서 해외 개발자 영상을 찾아보던 와중에 좋은 방법이 있어 기록하고자 한다.
현재 토이프로젝트는 React, refine을 사용중이다.
api를 호출할 때 fetch를 사용중이다.
만약 api를 호출할 때마다 fetch를 사용한다면 공통에러를 잡기가 쉽지않다.
그래서 fetch 요청을 한 군데에서만 처리하고 그곳에서 에러 처리를 하는것이 좋다.
공통에러처리를 위해 fetchWrapper로 만들어주었다.
import { GraphQLFormattedError } from "graphql";
type Error = {
message: string;
statusCode: string;
};
const customFetch = async (url: string, options: RequestInit) => {
const accessToken = localStorage.getItem("access-token");
const headers = options.headers as Record<string, string>;
return await fetch(url, {
...options,
headers: {
...headers,
Authorization: headers?.Authorization || `Bearer ${accessToken}`,
"Content-Type": "application/json",
"Apollo-Require-Preflight": "true",
},
});
};
const getGraphQLErrors = (
body: Record<"errors", GraphQLFormattedError[] | undefined>
): Error | null => {
if (!body) {
return {
message: "Unknown error",
statusCode: "INTERNAL_SERVER_ERROR",
};
}
if ("errors" in body) {
const errors = body?.errors;
const messages = errors?.map((error) => error?.message)?.join("");
const code = errors?.[0].extensions?.code;
return {
message: messages || JSON.stringify(errors),
statusCode: code || 500,
};
}
return null;
};
export const fetchWrapper = async (url: string, options: RequestInit) => {
const response = await customFetch(url, options);
const responseClone = response.clone();
const body = await responseClone.json();
const error = getGraphQLErrors(body);
if (error) {
throw error;
}
return response;
};
fetch
를 한번에 처리하기위해 customFetch
함수를 만들어 안에서 accessToken
을 실어 fetch
를 진행한다.
현재 graphQL
을 사용중이므로 에러를 잡아내기위해 getGraphQLErrors
함수를 만들어주었다.
body
가 없는 경우와 errors
가 body
에 있는경우를 분기처리해준다. 만약 에러가 없다면 null
을 return
해준다.
fetchWrapper
안에서 작성한 customFetch
를 사용해서 fetch
요청을 보내고 에러가 있을경우 throw
해준다.
만약 graphQL을 사용중이지 않고 REST API를 사용중이라면 아래와 같이 바꿀 수 있다.
const getRestErrors = (response: Response): Error | null => {
if(!response.ok) {
return {
message: `HTTP Error ${response.status}`,
statusCode: response.status.toString()
};
}
return null;
}
이제 fetchWrapper를 사용해보자!
import { GraphQLClient } from "@refinedev/nestjs-query";
import { fetchWrapper } from "./fetch-wrapper";
export const API_URL = "https://api.crm.refine.dev";
export const client = new GraphQLClient(API_URL, {
fetch: (url: string, options: RequestInit) => {
try {
return fetchWrapper(url, options);
} catch (error) {
return Promise.reject(error as Error);
}
},
});