diff --git a/src/apis/avatars/avatarApi.ts b/src/apis/avatars/avatarApi.ts index 86d5240..109e2ea 100644 --- a/src/apis/avatars/avatarApi.ts +++ b/src/apis/avatars/avatarApi.ts @@ -5,42 +5,37 @@ import { FinalChoiceAvatarRequest, FinalChoiceAvatarResponse, } from "@/types/avatars/masters"; +import { AvatarType } from "@/types/avatars/masters"; +import { ApiResponse, GlobalResponse } from "@/types/common/apiResponse.type"; import axios from "@/apis/instance"; -export const getSelectionAvatarApi = async () => { - try { - const response = await axios.get("/api/v1/avatars/masters"); - return response.data; - } catch (error) { - alert("회원가입에 실패했습니다."); - console.log(error); - } +export const getSelectionAvatarApi = async (): ApiResponse => { + return axios.get("/api/v1/avatars/masters").then(res => res.data); }; export const postCrationAvatarApi = async ( formData: FormData -): Promise => { +): ApiResponse => { try { - const response = await Axios.post( + const response = await Axios.post>( "http://43.200.84.255:8000/process-image", formData ); return response.data; } catch (error) { - console.error("error :", error); + console.error("이미지 생성 실패:", error); throw error; } }; export const finalChoiceAvatarApi = async ( data: FinalChoiceAvatarRequest -): Promise => { +): ApiResponse => { try { - const response = await axios.post( - "/api/v1/avatars", - data - ); + const response = await axios.post< + GlobalResponse + >("/api/v1/avatars", data); return response.data; } catch (error) { console.error("아바타 선택 실패:", error); diff --git a/src/apis/missions/QuizApi.ts b/src/apis/missions/QuizApi.ts index df1f0dd..52facd2 100644 --- a/src/apis/missions/QuizApi.ts +++ b/src/apis/missions/QuizApi.ts @@ -1,36 +1,32 @@ -import { AnswerQuizResponse } from "@/types/realQuiz/answerQuiz"; +import { ApiResponse, GlobalResponse } from "@/types/common/apiResponse.type"; +import { + AnswerQuizRequest, + AnswerQuizResponse, +} from "@/types/realQuiz/answerQuiz"; import { getQuizRequest, getQuizResponse } from "@/types/realQuiz/getQuiz"; import axios from "@/apis/instance"; export const getQuizApi = async ( params: getQuizRequest -): Promise => { - try { - const response = await axios.get("/api/v1/realQuiz", { params }); - - return response.data; - } catch (error) { - console.error("Error get MissionPanel:", error); - throw error; - } +): ApiResponse => { + return axios.get("/api/v1/realQuiz", { params }).then(res => res.data); }; export const postAnswerQuizApi = async ({ selectedOptionOrder, quizId, -}: { - selectedOptionOrder: number; - quizId: number; -}): Promise => { +}: AnswerQuizRequest & { quizId: number }): Promise< + GlobalResponse +> => { try { - const response = await axios.post( + const response = await axios.post>( `/api/v1/realQuiz/${quizId}/answer`, { selectedOptionOrder } ); return response.data; } catch (error) { - console.error("Error post Answer Quiz:", error); + console.error("퀴즈 답안 제출 실패:", error); throw error; } }; diff --git a/src/apis/missions/writeDiaryApi.ts b/src/apis/missions/writeDiaryApi.ts index bfab156..15bdd18 100644 --- a/src/apis/missions/writeDiaryApi.ts +++ b/src/apis/missions/writeDiaryApi.ts @@ -1,3 +1,4 @@ +import { ApiResponse } from "@/types/common/apiResponse.type"; import { writeDiaryImageUploadResponse, writeDiarySubmitRequest, @@ -7,26 +8,15 @@ import { import axios from "@/apis/instance"; // 이미지 전송 API -export const takePhotoUploadApi = async ( + +export const takePhotoUploadApi = ( formData: FormData -): Promise => { - try { - const response = await axios.post("/api/v1/diaries/images", formData); - return response.data; - } catch (error) { - console.error("Error post TakePhotoUpload:", error); - throw error; - } +): ApiResponse => { + return axios.post("/api/v1/diaries/images", formData).then(res => res.data); }; export const writeDiarySubmitApi = async ( params: writeDiarySubmitRequest -): Promise => { - try { - const response = await axios.post("/api/v1/diaries", params); - return response.data; - } catch (error) { - console.error("Error post WriteDiarySubmit:", error); - throw error; - } +): ApiResponse => { + return axios.post("api/v1/diaries", params).then(res => res.data); }; diff --git a/src/hooks/avatars/useFinalChoiceAvatarApi.ts b/src/hooks/avatars/useFinalChoiceAvatarApi.ts index 6210f63..4329e38 100644 --- a/src/hooks/avatars/useFinalChoiceAvatarApi.ts +++ b/src/hooks/avatars/useFinalChoiceAvatarApi.ts @@ -1,25 +1,28 @@ import { useMutation } from "@tanstack/react-query"; +import { AxiosError } from "axios"; import { FinalChoiceAvatarRequest, FinalChoiceAvatarResponse, } from "@/types/avatars/masters"; +import { ErrorResponse, GlobalResponse } from "@/types/common/apiResponse.type"; import { finalChoiceAvatarApi } from "@/apis/avatars/avatarApi"; export const useFinalChoiceAvatar = () => { return useMutation< - FinalChoiceAvatarResponse, - Error, + GlobalResponse, + AxiosError, FinalChoiceAvatarRequest >({ mutationFn: data => finalChoiceAvatarApi(data), onSuccess: data => { - console.log("아바타 최종 선택 성공:", data); + console.log("아바타 최종 선택 성공:", data.result); }, onError: error => { alert("아바타 최종 선택에 실패했습니다."); - console.error(error); + console.error(error.response?.data || error.message); }, + retry: 1, }); }; diff --git a/src/hooks/avatars/useGetSelectAvatarApi.ts b/src/hooks/avatars/useGetSelectAvatarApi.ts index 6cda42d..ccef53a 100644 --- a/src/hooks/avatars/useGetSelectAvatarApi.ts +++ b/src/hooks/avatars/useGetSelectAvatarApi.ts @@ -1,12 +1,23 @@ import { useQuery } from "@tanstack/react-query"; +import { AxiosError } from "axios"; -import { SelectAvatarResponse } from "@/types/avatars/masters"; +import { AvatarType } from "@/types/avatars/masters"; +import { ErrorResponse, GlobalResponse } from "@/types/common/apiResponse.type"; import { getSelectionAvatarApi } from "@/apis/avatars/avatarApi"; export const useGetSelectAvatar = () => { - return useQuery({ + return useQuery< + GlobalResponse, + AxiosError, + AvatarType[] + >({ queryKey: ["selectionAvatar"], queryFn: getSelectionAvatarApi, + select: data => data.result, + staleTime: 1000 * 60 * 120, + gcTime: 1000 * 60 * 5, + retry: 1, + refetchOnWindowFocus: true, }); }; diff --git a/src/hooks/mission/useGetQuizApi.ts b/src/hooks/mission/useGetQuizApi.ts index 7b93eca..d0b8980 100644 --- a/src/hooks/mission/useGetQuizApi.ts +++ b/src/hooks/mission/useGetQuizApi.ts @@ -1,13 +1,21 @@ import { useQuery } from "@tanstack/react-query"; +import { AxiosError } from "axios"; +import { ApiResponse, ErrorResponse } from "@/types/common/apiResponse.type"; import { getQuizRequest, getQuizResponse } from "@/types/realQuiz/getQuiz"; import { getQuizApi } from "@/apis/missions/QuizApi"; export const useGetQuiz = (params: getQuizRequest) => { - return useQuery({ + return useQuery< + ApiResponse, // 서버 전체 응답 타입 + AxiosError, // Axios 확장 에러 타입 + getQuizResponse // select로 실제 사용할 데이터 타입 + >({ queryKey: ["quiz", params], - queryFn: () => getQuizApi(params), + staleTime: 1000 * 60 * 5, // 5분 동안 stale 처리 안함 + retry: 1, // 실패 시 1번 재시도 + refetchOnWindowFocus: false, // 창 포커스 시 재요청 X }); }; diff --git a/src/hooks/mission/usePostAnswerQuiz.ts b/src/hooks/mission/usePostAnswerQuiz.ts index 690da63..f83bc01 100644 --- a/src/hooks/mission/usePostAnswerQuiz.ts +++ b/src/hooks/mission/usePostAnswerQuiz.ts @@ -1,5 +1,7 @@ import { useMutation } from "@tanstack/react-query"; +import { AxiosError } from "axios"; +import { ErrorResponse, GlobalResponse } from "@/types/common/apiResponse.type"; import { AnswerQuizResponse } from "@/types/realQuiz/answerQuiz"; import { postAnswerQuizApi } from "@/apis/missions/QuizApi"; @@ -10,8 +12,21 @@ interface AnswerQuizParams { } export const useAnswerQuiz = () => { - return useMutation({ - mutationFn: ({ quizId, selectedOptionOrder }) => + return useMutation< + GlobalResponse, // 성공 응답 타입 + AxiosError, // 에러 타입 + AnswerQuizParams // 요청 파라미터 타입 + >({ + mutationFn: ({ quizId, selectedOptionOrder }: AnswerQuizParams) => postAnswerQuizApi({ quizId, selectedOptionOrder }), + onSuccess: data => { + console.log("퀴즈 답안 제출 성공:", data.result); + }, + onError: error => { + console.error( + "퀴즈 답안 제출 실패:", + error.response?.data || error.message + ); + }, }); }; diff --git a/src/hooks/mission/useWriteDiaryApi.ts b/src/hooks/mission/useWriteDiaryApi.ts index 1c7de9f..c37af05 100644 --- a/src/hooks/mission/useWriteDiaryApi.ts +++ b/src/hooks/mission/useWriteDiaryApi.ts @@ -1,7 +1,10 @@ // src/hooks/mission/useWriteDiaryApi.ts import { useMutation, useQueryClient } from "@tanstack/react-query"; +import { AxiosError } from "axios"; +import { ErrorResponse, GlobalResponse } from "@/types/common/apiResponse.type"; import { + WriteDiaryImageUploadRequest, writeDiaryImageUploadResponse, writeDiarySubmitRequest, writeDiarySubmitResponse, @@ -16,20 +19,21 @@ export const useWriteDiaryImageUploadApi = () => { const queryClient = useQueryClient(); return useMutation< - writeDiaryImageUploadResponse, - Error, - { formData: FormData } + GlobalResponse, + AxiosError, + WriteDiaryImageUploadRequest >({ mutationFn: ({ formData }) => takePhotoUploadApi(formData), - onSuccess: data => { - console.log("일기 사진 업로드 성공:", data); - + onSettled: () => { queryClient.invalidateQueries({ queryKey: ["diaries"] }); }, - onError: error => { - console.error("일기 사진 업로드 실패:", error); + console.error( + "이미지 업로드 실패:", + error.response?.data || error.message + ); }, + retry: 1, }); }; @@ -37,9 +41,9 @@ export const useWriteDiarySubmitApi = () => { const queryClient = useQueryClient(); return useMutation< - writeDiarySubmitResponse, // 성공 응답 타입 - Error, // 에러 타입 - writeDiarySubmitRequest // 요청 타입 + GlobalResponse, + AxiosError, + writeDiarySubmitRequest >({ mutationFn: body => writeDiarySubmitApi(body), onSuccess: data => { @@ -48,7 +52,7 @@ export const useWriteDiarySubmitApi = () => { queryClient.invalidateQueries({ queryKey: ["diaries"] }); }, onError: error => { - console.error("일기 작성 실패:", error); + console.error("일기 작성 실패:", error.response?.data || error.message); }, }); }; diff --git a/src/pages/registration/SelectionDetailPage.tsx b/src/pages/registration/SelectionDetailPage.tsx index e606641..30bc3de 100644 --- a/src/pages/registration/SelectionDetailPage.tsx +++ b/src/pages/registration/SelectionDetailPage.tsx @@ -17,15 +17,15 @@ const SelectionDetailPage = () => { const [selectedId, setSelectedId] = useState(null); useEffect(() => { - if (data?.result && data.result.length > 0 && selectedId === null) { - setSelectedId(data.result[0].id); + if (data && data.length > 0 && selectedId === null) { + setSelectedId(data[0].id); } }, [data, selectedId]); const handleNextClick = () => { - if (selectedId === null || !data?.result) return; + if (selectedId === null || !data) return; - const selectedAvatar = data.result.find( + const selectedAvatar = data.find( (avatar: AvatarType) => avatar.id === selectedId ); @@ -61,10 +61,10 @@ const SelectionDetailPage = () => {
원하는 아바타를 선택해주세요.
-
- {data?.result && selectedId !== null && ( +
+ {data && selectedId !== null && ( diff --git a/src/types/avatars/masters.ts b/src/types/avatars/masters.ts index abd2aa6..119c9fe 100644 --- a/src/types/avatars/masters.ts +++ b/src/types/avatars/masters.ts @@ -1,17 +1,10 @@ +// 아바타 선택하기 조회 API 응답 타입 export interface AvatarType { id: number; defaultImageUrl: string; description: string; } -// 아바타 선택하기 조회 API 응답 타입 -export interface SelectAvatarResponse { - isSuccess: boolean; - code: string; - message: string; - result: AvatarType[]; -} - // 아바타 생성하기 응답 타입 export interface CreateAvatarResponse { imageUrl: string; @@ -24,9 +17,4 @@ export interface FinalChoiceAvatarRequest { masterId: number; } -export interface FinalChoiceAvatarResponse { - isSuccess: boolean; - code: string; - message: string; - result: string; -} +export type FinalChoiceAvatarResponse = string; diff --git a/src/types/mission/writeDiary.ts b/src/types/mission/writeDiary.ts index 9507e19..3cf87da 100644 --- a/src/types/mission/writeDiary.ts +++ b/src/types/mission/writeDiary.ts @@ -1,13 +1,12 @@ -export interface writeDiaryImageUploadResponse { - isSuccess: boolean; - code: string; - message: string; - result: { - imageId: number; - imageUrl: string; - }; +export interface WriteDiaryImageUploadRequest { + formData: FormData; } +export type writeDiaryImageUploadResponse = { + imageId: number; + imageUrl: string; +}; + export interface writeDiarySubmitRequest { title: string; content: string; @@ -17,17 +16,12 @@ export interface writeDiarySubmitRequest { } export interface writeDiarySubmitResponse { - isSuccess: boolean; - code: string; - message: string; - result: { - diaryId: number; - title: string; - content: string; - imageUrl: string; - likeCount: number; - createdAt: string; - updatedAt: string; - public: boolean; - }; + diaryId: number; + title: string; + content: string; + imageUrl: string; + likeCount: number; + createdAt: string; + updatedAt: string; + public: boolean; } diff --git a/src/types/realQuiz/answerQuiz.ts b/src/types/realQuiz/answerQuiz.ts index c7b6f54..3bdb666 100644 --- a/src/types/realQuiz/answerQuiz.ts +++ b/src/types/realQuiz/answerQuiz.ts @@ -3,16 +3,11 @@ export interface AnswerQuizRequest { } export interface AnswerQuizResponse { - isSuccess: boolean; - code: string; - message: string; - result: { - quizQuestion: string; - quizType: "OX" | "MULTI_CHOICE"; - selectedOptionNumber: number; - answerNumber: number; - answerDescription: string; - isCorrect: boolean; - isCompleted: boolean; - }; + quizQuestion: string; + quizType: "OX" | "MULTI_CHOICE"; + selectedOptionNumber: number; + answerNumber: number; + answerDescription: string; + isCorrect: boolean; + isCompleted: boolean; } diff --git a/src/types/realQuiz/getQuiz.ts b/src/types/realQuiz/getQuiz.ts index 41cca90..4883eb2 100644 --- a/src/types/realQuiz/getQuiz.ts +++ b/src/types/realQuiz/getQuiz.ts @@ -3,23 +3,18 @@ export interface getQuizRequest { } export interface getQuizResponse { - isSuccess: boolean; - code: string; - message: string; - result: { - quizId: number; - quizQuestion: string; - quizType: "OX" | "MULTI_CHOICE" | "CHOICE_WITH_PICTURE"; - answerNumber: number | null; - answerDescription: string | null; - isCompleted: boolean; - quizOptions: - | [ - { - optionOrder: number; - optionText: string; - }, - ] - | null; - }; + quizId: number; + quizQuestion: string; + quizType: "OX" | "MULTI_CHOICE" | "CHOICE_WITH_PICTURE"; + answerNumber: number | null; + answerDescription: string | null; + isCompleted: boolean; + quizOptions: + | [ + { + optionOrder: number; + optionText: string; + }, + ] + | null; }