refactor: Enhance survey hook and integrate spinner functionality

- Refactored useSurvey hook to utilize useAxios for API calls.
- Added spinner visibility management in EdgeProvider to improve loading feedback.
- Cleaned up imports and organized code structure for better readability.
This commit is contained in:
yoosangwook 2025-05-22 14:08:23 +09:00
parent 0e7de68f29
commit 2c5ddad29b
5 changed files with 130 additions and 7 deletions

93
src/hooks/useAxios.ts Normal file
View File

@ -0,0 +1,93 @@
import axios from 'axios'
import { useSpinnerStore } from '@/store/spinnerStore'
export const useAxios = () => {
const { setIsShow } = useSpinnerStore()
const axiosInstance = (url: string | null | undefined) => {
const baseURL = url || process.env.NEXT_PUBLIC_API_URL
const instance = axios.create({
baseURL,
headers: {
Accept: 'application/json',
},
})
instance.interceptors.request.use(
(config) => {
// console.log('🚀 ~ config:', config)
setIsShow(true)
return config
},
(error) => {
return Promise.reject(error)
},
)
instance.interceptors.response.use(
(response) => {
response.data = transferResponse(response)
setIsShow(false)
return response
},
(error) => {
// 에러 처리 로직
return Promise.reject(error)
},
)
return instance
}
// response데이터가 array, object에 따라 분기하여 키 변환
const transferResponse = (response: any) => {
if (!response.data) return response.data
// 배열인 경우 각 객체의 키를 변환
if (Array.isArray(response.data)) {
return response.data.map((item: any) => transformObjectKeys(item))
}
// 단일 객체인 경우
return transformObjectKeys(response.data)
}
// camel case object 반환
const transformObjectKeys = (obj: any): any => {
if (Array.isArray(obj)) {
return obj.map(transformObjectKeys)
}
if (obj !== null && typeof obj === 'object') {
return Object.keys(obj).reduce((acc: any, key: string) => {
let transformedKey = key
// Handle uppercase snake_case (e.g., USER_NAME -> userName)
// Handle lowercase snake_case (e.g., user_name -> userName)
if (/^[A-Z_]+$/.test(key) || /^[a-z_]+$/.test(key)) {
transformedKey = snakeToCamel(key)
}
// Handle single uppercase word (e.g., ROLE -> role)
else if (/^[A-Z]+$/.test(key)) {
transformedKey = key.toLowerCase()
}
// Preserve existing camelCase
acc[transformedKey] = transformObjectKeys(obj[key])
return acc
}, {})
}
return obj
}
const snakeToCamel = (str: string): string => {
return str.toLowerCase().replace(/([-_][a-z])/g, (group) => group.toUpperCase().replace('-', '').replace('_', ''))
}
return {
axiosInstance,
transferResponse,
transformObjectKeys,
}
}

View File

@ -1,11 +1,10 @@
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import type { SurveyBasicInfo, SurveyDetailInfo, SurveyDetailRequest, SurveyDetailCoverRequest, SurveyRegistRequest } from '@/types/Survey'
import { axiosInstance } from '@/libs/axios'
import { useSurveyFilterStore } from '@/store/surveyFilterStore'
import { queryStringFormatter } from '@/utils/common-utils'
import { useSessionStore } from '@/store/session'
import { useMemo } from 'react'
import { AxiosResponse } from 'axios'
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import { useSurveyFilterStore } from '@/store/surveyFilterStore'
import { useSessionStore } from '@/store/session'
import { useAxios } from './useAxios'
import { queryStringFormatter } from '@/utils/common-utils'
export const requiredFields = [
{
@ -75,6 +74,7 @@ export function useServey(id?: number): {
const queryClient = useQueryClient()
const { keyword, searchOption, isMySurvey, sort, offset } = useSurveyFilterStore()
const { session } = useSessionStore()
const { axiosInstance } = useAxios()
const {
data: surveyListData,

View File

@ -1,3 +1,4 @@
import { useSpinnerStore } from '@/store/spinnerStore'
import axios from 'axios'
export const axiosInstance = (url: string | null | undefined) => {

View File

@ -8,6 +8,8 @@ import { usePopupController } from '@/store/popupController'
import { useSideNavState } from '@/store/sideNavState'
import { useSessionStore } from '@/store/session'
import { tracking } from '@/libs/tracking'
import Spinner from '@/components/ui/common/Spinner'
import { useSpinnerStore } from '@/store/spinnerStore'
declare global {
interface Window {
@ -28,6 +30,7 @@ export default function EdgeProvider({ children, sessionData }: EdgeProviderProp
const { reset } = useSideNavState()
const { setAlertMsg, setAlertBtn, setAlert, setAlert2, setAlert2BtnYes, setAlert2BtnNo } = usePopupController()
const { session, setSession } = useSessionStore()
const { isShow, setIsShow } = useSpinnerStore()
/**
*
@ -110,5 +113,10 @@ export default function EdgeProvider({ children, sessionData }: EdgeProviderProp
handlePageEvent(pathname)
}, [pathname])
return <>{children}</>
return (
<>
{children}
{isShow && <Spinner />}
</>
)
}

21
src/store/spinnerStore.ts Normal file
View File

@ -0,0 +1,21 @@
import { create } from 'zustand'
type SpinnerState = {
isShow: boolean
setIsShow: (isShow: boolean) => void
resetCount: () => void
}
type InitialState = {
isShow: boolean
}
const initialState: InitialState = {
isShow: false,
}
export const useSpinnerStore = create<SpinnerState>((set) => ({
...initialState,
setIsShow: (isShow: boolean) => set({ isShow }),
resetCount: () => set(initialState),
}))