From ae653566c5473f58cfa8d9093e35569f7e6df2d9 Mon Sep 17 00:00:00 2001 From: dooohun <74540646+dooohun@users.noreply.github.com> Date: Wed, 13 Nov 2024 15:57:48 +0900 Subject: [PATCH] =?UTF-8?q?[FE]=20-=20api=20=ED=86=B5=EC=8B=A0=EC=9D=84=20?= =?UTF-8?q?=EC=9C=84=ED=95=9C=20apiClient=20=EA=B5=AC=ED=98=84=20(#55)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: API 기본 세팅 추가 - api를 endPoint만 작성하면 쉽게 호출 가능한 형태의 함수인 apiClient 추가 - AbortController를 통해 요청 제한 시간 추가 * feat: apiClient를 axios와 비슷한 형태로 사용가능하도록 수정 - body의 경우 객체 상태로 넘길 수 있도록 타입 설정 * feat: react-query QueryClientProvider 설정 추가 --- packages/FE/src/main.tsx | 7 +++- packages/FE/src/shared/api/index.ts | 57 +++++++++++++++++++++++++++++ packages/FE/tsconfig.json | 2 +- 3 files changed, 64 insertions(+), 2 deletions(-) create mode 100644 packages/FE/src/shared/api/index.ts diff --git a/packages/FE/src/main.tsx b/packages/FE/src/main.tsx index f2f91d7c..6b8a8349 100644 --- a/packages/FE/src/main.tsx +++ b/packages/FE/src/main.tsx @@ -4,11 +4,16 @@ import { BrowserRouter } from 'react-router-dom'; import './index.css'; import App from '@/app'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; + +const queryClient = new QueryClient(); createRoot(document.getElementById('root')!).render( - + + + , ); diff --git a/packages/FE/src/shared/api/index.ts b/packages/FE/src/shared/api/index.ts new file mode 100644 index 00000000..e1189941 --- /dev/null +++ b/packages/FE/src/shared/api/index.ts @@ -0,0 +1,57 @@ +// TODO: env 파일로 관리하기 +const BASE_URL = 'http://localhost:3000/'; + +interface FetchOptions extends Omit { + headers?: Record; + body?: unknown; +} + +export const apiClient = { + get: (endPoint: string, options: FetchOptions = {}) => + sendRequest(endPoint, { ...options, method: 'GET' }), + post: (endPoint: string, options: FetchOptions = {}) => + sendRequest(endPoint, { ...options, method: 'POST' }), + put: (endPoint: string, options: FetchOptions = {}) => + sendRequest(endPoint, { ...options, method: 'PUT' }), + delete: (endPoint: string, options: FetchOptions = {}) => + sendRequest(endPoint, { ...options, method: 'DELETE' }), +}; + +async function sendRequest(endPoint: string, options: FetchOptions = {}, timeout: number = 10000) { + const { headers, body, ...restOptions } = options; + + const abortController = new AbortController(); + const timeoutId = setTimeout(() => { + abortController.abort(); + }, timeout); + + try { + const response = await fetch(`${BASE_URL}${endPoint}`, { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${localStorage.getItem('token')}`, + ...headers, + }, + // axios처럼 객체를 body로 받을 수 있도록 설정 + ...(body !== undefined && + body !== null && { + body: typeof body === 'object' ? JSON.stringify(body) : (body as BodyInit), + }), + signal: abortController.signal, + ...restOptions, + }); + + if (!response.ok) { + const errorMessage = await response.text(); + throw new Error(errorMessage || 'API 요청 실패'); + } + return response.json(); + } catch (error) { + if (error instanceof Error && error.name === 'AbortError') { + throw new Error('요청 시간이 초과되었습니다.'); + } + throw error; + } finally { + clearTimeout(timeoutId); + } +} diff --git a/packages/FE/tsconfig.json b/packages/FE/tsconfig.json index e21e04e1..fa9f0d6e 100644 --- a/packages/FE/tsconfig.json +++ b/packages/FE/tsconfig.json @@ -5,7 +5,7 @@ "lib": ["ES2020", "DOM", "DOM.Iterable"], "module": "ESNext", "skipLibCheck": true, - "types": ["vite-plugin-svgr/client"], + "types": ["vite-plugin-svgr/client", "node"], /* Bundler mode */ "moduleResolution": "Bundler",