이번 프로젝트에서는 Next의 app router를 사용하게 됐는데, axios를 사용하지 않고 Next의 fetch를 활용하고 싶었다. 이유는
Next.js는 서버에서 각 요청이 지속적인 캐싱 및 재검증 방식을 설정할 수 있도록 Web fetch() API를 확장한다.
브라우저에서는 cache 옵션이 브라우저의 HTTP 캐시와 fetch 요청이 어떻게 상호작용할지를 결정하는 반면,Next.js에서는 이 확장을 통해 서버 측 fetch 요청이 프레임워크의 지속적인 데이터 캐시(Data Cache) 와 어떻게 상호작용할지를 cache 옵션으로 설정할 수 있다.
그리고 Server Components 안에서는 async와 await를 사용해 fetch를 직접 호출할 수 있다.
이러한 fetch 기능을 사용해보고 싶었고, axios와 혼용하여 쓰기보다는 fetch 하나만 사용하는것이 통일성이 있어 좋을 것이라고 생각되었다.
그렇게 개발을 시작하게 되었는데 아무래도 fetch는 각 api 함수마다 헤더와 바디를 모두 써야해서
// 지정가 취소
export async function cancelTrade({
token,
orderId,
}: CancelData): Promise<string> {
if (token === null) {
throw new Error();
}
try {
const res = await fetch(
`${process.env.NEXT_PUBLIC_API_URL}/api/account/order/${orderId}/cancel`,
{
method: "DELETE",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
},
},
);
if (!res.ok) {
throw new Error(`Failed to post transaction data: ${res.status}`);
}
return await res.text();
} catch {
throw new Error();
}
}
모든 api 함수들이 이런 형태를 띄게 되었다. 중복되는 코드들이 너무 많다고 느꼈고 하나의 함수를 만들어서 축약할 수 있으면 좋겠다는 생각이 들었다. 그래서 코드 리팩토링을 하며 api request를 개발하였다.
아래 코드와 같이 메소드, 엔드포인트, 옵션을 지정해 개발자가 원하는 api request에 대응할 수 있으며, 에러처리도 함께 되는 함수를 작성했다.
export default async function makeApiRequest<T, R = string>(
method: "GET" | "POST" | "PUT" | "DELETE",
endpoint: string,
options: {
token?: string | null;
data?: T;
responseType?: "json" | "text";
},
): Promise<R> {
const { token, data, responseType = "json" } = options;
const headers: HeadersInit = {
...(data && { "Content-Type": "application/json" }),
...(token && { Authorization: `Bearer ${token}` }),
};
const config: RequestInit = {
method,
headers,
...(data && { body: JSON.stringify(data) }),
};
const response = await fetch(
`${process.env.NEXT_PUBLIC_API_URL}${endpoint}`,
config,
);
if (!response.ok) {
const errorData = await response.text();
throw new Error(
errorData || `요청 실패: ${response.status} ${response.statusText}`,
);
}
return responseType === "json" ? response.json() : response.text();
}
이렇게 작성하고 나니 사용하는 측에서는
export async function cancelTrade({
token,
orderId,
}: CancelData): Promise<string> {
return makeApiRequest("DELETE", `/api/account/order/${orderId}/cancel`, {
token,
responseType: "text",
});
}
이와 같이 간단한 api 함수만 작성할 수 있게 되어 훨씬 더 간단해진 모습을 볼 수 있었다. 처음 코드의 절반 이상이 줄어들었고, 중복되는 로직을 계속 반복해서 작성하지 않아도 되어 뿌듯했다✨