[API 개선] useApi 함수 리팩토링 및 API 플러그인 추가로 API 호출 방식 통일
This commit is contained in:
@@ -1,36 +1,36 @@
|
|||||||
import { useRuntimeConfig } from "#imports";
|
/**
|
||||||
|
* API 호출을 위한 편의 함수
|
||||||
export const useApi = async <T>(
|
*
|
||||||
path: string,
|
* @template T - 응답 데이터의 타입
|
||||||
options: {
|
* @param path - API 엔드포인트 경로 (예: '/users', '/users/1')
|
||||||
method?: "get" | "post" | "put" | "delete";
|
* @param opts - 요청 옵션 (method, body, headers 등)
|
||||||
body?: any;
|
* @returns Promise<T> - API 응답 데이터
|
||||||
query?: Record<string, any>;
|
*
|
||||||
headers?: HeadersInit;
|
* @example
|
||||||
credentials?: RequestCredentials;
|
* // GET 요청
|
||||||
} = {}
|
* const users = await useApi<User[]>('/users')
|
||||||
): Promise<T> => {
|
*
|
||||||
const config = useRuntimeConfig();
|
* // POST 요청
|
||||||
const method = options.method ? options.method.toUpperCase() : "GET";
|
* const newUser = await useApi<User>('/users', {
|
||||||
|
* method: 'POST',
|
||||||
try {
|
* body: { name: 'John', email: 'john@example.com' }
|
||||||
const response = await $fetch<T>(
|
* })
|
||||||
`${config.public.apiBase}${config.public.contextPath}${path}`,
|
*
|
||||||
{
|
* // PUT 요청
|
||||||
method: method as any,
|
* const updatedUser = await useApi<User>('/users/1', {
|
||||||
body: options.body,
|
* method: 'PUT',
|
||||||
query: options.query,
|
* body: { name: 'John Updated' }
|
||||||
credentials: options.credentials || "include", // 쿠키 자동 전송
|
* })
|
||||||
headers: {
|
*
|
||||||
"Content-Type": "application/json",
|
* // DELETE 요청
|
||||||
...options.headers,
|
* await useApi('/users/1', { method: 'DELETE' })
|
||||||
},
|
*
|
||||||
}
|
* // FormData 업로드
|
||||||
);
|
* const formData = new FormData()
|
||||||
|
* formData.append('file', file)
|
||||||
return response;
|
* await useApi('/upload', { method: 'POST', body: formData })
|
||||||
} catch (error) {
|
*/
|
||||||
console.error("API 호출 실패:", error);
|
export const useApi = <T>(path: string, opts?: any): Promise<T> => {
|
||||||
throw error;
|
const { $api } = useNuxtApp();
|
||||||
}
|
return ($api as any)(path, opts);
|
||||||
};
|
};
|
||||||
|
44
plugins/api.ts
Normal file
44
plugins/api.ts
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
export default defineNuxtPlugin(() => {
|
||||||
|
const config = useRuntimeConfig();
|
||||||
|
const baseURL = `${config.public.apiBase}${config.public.contextPath}`;
|
||||||
|
|
||||||
|
const api = $fetch.create({
|
||||||
|
baseURL,
|
||||||
|
credentials: "include",
|
||||||
|
onRequest({ request, options }) {
|
||||||
|
// 1) GET/HEAD가 아니면 body만 넣기 (GET에 body 금지)
|
||||||
|
const method = (options.method ?? "GET").toUpperCase();
|
||||||
|
if (method === "GET" || method === "HEAD") {
|
||||||
|
delete (options as any).body;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2) FormData면 Content-Type 자동 지정 금지
|
||||||
|
const isFormData =
|
||||||
|
typeof FormData !== "undefined" && options.body instanceof FormData;
|
||||||
|
options.headers = {
|
||||||
|
...(isFormData ? {} : { "Content-Type": "application/json" }),
|
||||||
|
...(options.headers || {}),
|
||||||
|
};
|
||||||
|
|
||||||
|
// 3) SSR 쿠키 포워딩
|
||||||
|
if (import.meta.server) {
|
||||||
|
const cookie = useRequestHeaders(["cookie"])?.cookie;
|
||||||
|
// request가 절대 URL이면 호스트 비교
|
||||||
|
const reqUrl = typeof request === "string" ? request : String(request);
|
||||||
|
const isBackendApi =
|
||||||
|
!reqUrl.startsWith("http") || // 상대경로면 내 API
|
||||||
|
reqUrl.startsWith(baseURL); // 혹은 baseURL과 동일
|
||||||
|
|
||||||
|
if (cookie && isBackendApi) {
|
||||||
|
options.headers = { ...(options.headers || {}), cookie } as any;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onResponseError({ response }) {
|
||||||
|
// 공통 로깅
|
||||||
|
console.error("[API ERROR]", response.status, response._data);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return { provide: { api } };
|
||||||
|
});
|
Reference in New Issue
Block a user