React+Axios에 익숙해져 있는 상태에서 attraction 팀프로젝트에서 Next.js App router 사용하다보니 몇가지 불편함을 접했습니다.
- 클라이언트 컴포넌트에서 interceptor 기능의 부재
- res.json(), JSON.stringify을 매번 사용하는 것
- 400번 이상의 에러는 res.ok로 분기 처리
- fetch에서는 사용할 수 없는
axios.post
와 같은 API 메서드
위와 같은 불편한 사항들을 해결하기 위해 Easyfetch는 Next App router에서 사용하는 확장된 fetch를 axios 사용자들이 좀 더 편리하게 사용하기 위해 만든 라이브러리 입니다.
pnpm add @woogie0303/easyfetch
yarn add @woogie0303/easyfetch
npm install @woogie0303/easyfetch
interface NextFetchRequestConfig {
revalidate?: number | false;
tags?: string[];
}
interface RequestInitWithNextConfig extends globalThis.RequestInit {
next?: NextFetchRequestConfig | undefined;
}
type EasyFetchResponse<T> = Omit<
Awaited<ReturnType<typeof fetch>>,
keyof Body | 'clone' | 'url'
> & {
body: T;
config: [string | URL, RequestInit | RequestInitWithNextConfig | undefined];
};
type EasyFetchRequestType = [
string | URL,
RequestInitWithNextConfig | undefined
];
const easy = easyFetch(defaultConfig);
defaultConfig?: {
baseUrl?: string | URL;
headers?: HeadersInit;
}
const easy = easyFetch({
baseUrl: 'https://attraction/',
headers: {
'Content-Type': 'multipart/form-data',
},
});
const request = new Request('https://hi');
easy.request(request);
easy.get('getUser');
easy.delete('deleteUser');
easy.patch('postUser', { userData: 'kang' });
easy.post('postUser', { userData: 'kang' });
easy.put('postUser', { userData: 'kang' });
<T>(url: string | URL, reqConfig?: Omit<RequestInitWithNextConfig, 'method'>) =>
Promise<EasyFetchResponse<T>>;
<T>(
url: string | URL,
reqBody?: object, // Request Body
reqConfig?: Omit<RequestInitWithNextConfig, 'method' | 'body'>
) => Promise<EasyFetchResponse<T>>;
<T>(request: RequestInfo | URL, requestInit?: RequestInitWithNextConfig) =>
Promise<EasyFetchResponse<T>>;
요청을 보내기 전에 Fetch 인자와 관련해서 설정할 것이 있으면 추가할 수 있는 기능입니다.
const easy = easyFetch();
easy.interceptor.request(onFulfilled, onReject);
(
onFulfilled?: (arg: EasyFetchRequestType)
=> | Promise<EasyFetchRequestType>
| EasyFetchRequestType,
onRejected?: (err: any) => any
): void
응답을 받기전에 처리해야할 로직을 설정할 수 있습니다. 400번대 에러를 처리할때는 EasyFetchResponse 타입 단언을 사용해서 서버 에러를 핸들링 할 수 있습니다.
const easy = easyFetch();
easy.interceptor.response(onFulfilled, onReject);
(
onFulfilled?: (arg: EasyFetchResponse<any>)
=> | Promise<EasyFetchResponse<any>>
| EasyFetchResponse<any>,
onRejected?: (err: any) => any
): void
const easy = easyFetch();
easy.interceptor.response(
(res) => res,
async (err) => {
const error = err as EasyFetchResponse<ErrorType>;
if (error.status === 401) {
const { body } = await easy.get<Token>('https://google.co/getToken', {
...error.config[1],
});
const headers = new Headers(error.config[1]?.headers);
headers.set('Authorization', `Bearer ${body.accessToken}`);
return easy.request(error.config[0], {
...error.config[1],
headers,
});
}
throw err;
}
);
에러를 핸들링할 때 Interceptor Response기능은 response의 형태를 바꿔주는 transform response와 다른 기능입니다. error config url을 인자로 해서 반환해주는 것이 타입의 일관성을 줄 수 있습니다.
// 잘못된 사용 예시
const easy = easyFetch();
easy.interceptor.response(
(res) => res,
(err) => {
const serverError = err as EasyFetchResponse<ResponseType>;
return { data: 'Hi' };
}
);
const data = await easy.get<ResponseType>('https://sdf'); // return {data: 'Hi'}
// 올바른 사용 예시
const easy = easyFetch();
easy.interceptor.response(
(res) => res,
(err) => {
const serverError = err as EasyFetchResponse<ResponseType>;
const [url, requestConfig] = serverError.config;
return easy.get(url, requestConfig);
}
);
const data = easy.get<ResponseType>('https://sdf'); // return ResponseType Data
MIT © DongWook Kang