현재 프로젝트에서 api를 모두 Axios와 Tanstack Query를 활용해서 호출하고 있다.
예를 들어 목표를 호출하는 함수를 만드려면 새로운 파일을 만들고 axiosInstance
를 활용해 api를 호출하는 로직을 작성하고 있다.
apis
폴더 안에 API 호출 로직을 작성한다.
/apis/Goals/getGoals.tsx
export const getGoals = async () => {
try {
const response = await axiosInstance.get(API_ENDPOINTS.GOAL.GOALS);
return response.data;
} catch (error) {
console.error("목표 불러오기 에러",error)
throw error
}
}
이후 /hooks/apis/goals/useGoalsQuery.tsx
파일에서 useQuery
를 사용해 API 호출을 처리하는 커스텀 훅을 만든다.
const goalsOptions: UseQueryOptions<GoalsResponse, AxiosError> = {
queryKey: [QUERY_KEYS.GOALS],
queryFn: () => getGoals(),
};
export const useGoalsQuery = () => {
const { data, ...etc } = useQuery(goalsOptions);
const goals = data?.data ?? [];
return { goals, ...etc };
};
현재 방식은 API 호출과 관련된 파일 구조가 복잡해지고 하나의 파일안에서 작성하기엔 가독성이 좋지 않아서 방법을 고민해보았다.
API 통신에서 사용되는 GET
, POST
, PUT
, DELETE
를 각각 함수로 만들어 재사용 가능하도록 만들었다.
각각의 응답 및 반환 타입을 제네릭 타입으로 설정하였고 공통된 에러처리 로직을 추가해 코드 중복을 줄이고 가독성을 높였다.
/apis/service/httpMethid.ts
import axiosInstance from '@/lib/axiosInstance';
export async function GET<T>(url: string): Promise<T> {
try {
const response = await axiosInstance.get(url);
return response.data;
} catch (error) {
throw new Error(error instanceof Error ? error.message : String(error));
}
}
export async function POST<T, U>(url: string, data?: U): Promise<T> {
try {
const response = await axiosInstance.post(url, data);
return response.data;
} catch (error) {
throw new Error(error instanceof Error ? error.message : String(error));
}
}
export async function PUT<T, U>(url: string, data: U): Promise<T> {
try {
const response = await axiosInstance.put(url, data);
return response.data;
} catch (error) {
throw new Error(error instanceof Error ? error.message : String(error));
}
}
export async function DELETE<T>(url: string): Promise<T> {
try {
const response = await axiosInstance.delete(url);
return response.data;
} catch (error) {
throw new Error(error instanceof Error ? error.message : String(error));
}
}
기존에는 동일한 로직을 여러 파일에서 반복적으로 작성해야 했던 문제를 개선하고 공통 함수로 중복 코드를 줄일 수 있었다.
/hooks/apis/goals/useGoalsQuery.tsx
const goalsOptions: UseQueryOptions<GoalsResponse, AxiosError> = {
queryKey: [QUERY_KEYS.GOALS],
queryFn: () => GET<GoalsResponse>(API_ENDPOINTS.GOAL.GOALS),
};
export const useGoalsQuery = () => {
const { data, ...etc } = useQuery(goalsOptions);
const goals = data?.data ?? [];
return { goals, ...etc };
};
서버 사이드 렌더링(SSR)에서 API 호출이 불가능하다.
현재 구조에서는 커스텀된 axiosInstance
를 사용해 요청을 보낼 때 브라우저 환경에서만 접근할 수 있는 쿠키를 활용해 토큰을 주입하고 있다.
SEO가 중요한 페이지일 경우엔 서버 사이드로 데이터 패칭을 통해서 불러와야할 경우엔 새로운 인스턴스를 만들어서 사용해야하는 한계점이 있다.
기존 방식은 API 호출 로직과 Tanstack Query hook을 각각 작성해야 했기 때문에 불필요한 중복 코드가 발생했다.
공통 HTTP 메서드 함수를 사용하게 되면서 API 호출 로직의 중복을 제거하고 각 도메인별로 간단하게 hook을 작성할 수 있게되었다.
결과적으로 코드의 양을 줄이고 새로운 API를 추가하는 데 필요한 시간을 단축시킬 수 있었다.