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",