Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
…al-project/WEB1_1_DDang_FE into 150-feature/enhance-web-socket
  • Loading branch information
shlee9999 committed Dec 9, 2024
2 parents d25ff2d + 1b211f8 commit 6b5818b
Show file tree
Hide file tree
Showing 67 changed files with 3,121 additions and 1,401 deletions.
1,474 changes: 737 additions & 737 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'
import { Suspense, useState } from 'react'
import { Helmet, HelmetProvider } from 'react-helmet-async'
import { RouterProvider } from 'react-router-dom'
Expand All @@ -7,8 +9,6 @@ import { router } from '~/router'
import GlobalStyle from '~/styles/globalStyle'
import { darkTheme, lightTheme } from '~/styles/theme'
import PageLoader from '~components/PageLoader'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'
import PushNotification from '~components/PushNotification'

const queryClient = new QueryClient()
Expand Down
7 changes: 6 additions & 1 deletion src/WebSocketContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,12 @@ export const WebSocketProvider = ({ children }: { children: React.ReactNode }) =
)
}

export const useWebSocket = () => {
export const useWebSocket = (): {

Check warning on line 80 in src/WebSocketContext.tsx

View workflow job for this annotation

GitHub Actions / lighthouse

Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components
client: Client | null
isConnected: boolean
subscribe: (destination: string, callback: (message: any) => void) => void
publish: (destination: string, body: any) => void
} => {
const context = useContext(WebSocketContext)
if (!context) {
throw new Error('useWebSocket must be used within a WebSocketProvider')
Expand Down
46 changes: 25 additions & 21 deletions src/apis/dog/createDogProfile.ts
Original file line number Diff line number Diff line change
@@ -1,41 +1,45 @@
import { AxiosError } from 'axios'
import { APIResponse, ErrorResponse } from '~types/api'
import { axiosInstance } from '~apis/axiosInstance'
import { DogProfileType } from '~types/dogProfile'

interface CreateDogProfileResponse {
dogId: number
name: string
breed: string
birthDate: string
weight: number
gender: 'MALE' | 'FEMALE'
profileImg: File
isNeutered: 'TRUE' | 'FALSE'
familyId: number
comment: string
}
export type CreateDogProfileRequest = FormData

export type CreateDogProfileResponse = DogProfileType

export const createDogProfile = async (formData: FormData): Promise<APIResponse<CreateDogProfileResponse>> => {
/**
* 새로운 반려견 프로필을 생성합니다.
*/
export const createDogProfile = async (
req: CreateDogProfileRequest
): Promise<APIResponse<CreateDogProfileResponse>> => {
try {
const { data } = await axiosInstance.post<APIResponse<CreateDogProfileResponse>>('/dogs/create', formData, {
const { data } = await axiosInstance.post<APIResponse<CreateDogProfileResponse>>('/dogs/create', req, {
headers: {
'Content-Type': 'multipart/form-data',
},
})
return data
} catch (error) {
if (error instanceof AxiosError) {
const { response, request } = error as AxiosError<ErrorResponse>
const { response } = error as AxiosError<ErrorResponse>

if (response) {
console.error('반려견 등록 오류:', response.data)
throw new Error(response.data.message ?? '요청 실패')
const { code, message } = response.data
switch (code) {
case 400:
throw new Error(message || '잘못된 요청입니다.')
case 401:
throw new Error(message || '인증에 실패했습니다.')
case 500:
throw new Error(message || '서버 오류가 발생했습니다.')
default:
throw new Error(message || '알 수 없는 오류가 발생했습니다.')
}
}

if (request) {
console.error('요청 에러:', request)
throw new Error('네트워크 연결을 확인해주세요')
}
// 요청 자체가 실패한 경우
throw new Error('네트워크 연결을 확인해주세요')
}

console.error('예상치 못한 에러:', error)
Expand Down
37 changes: 26 additions & 11 deletions src/apis/dog/deleteDogProfile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,42 @@ import { AxiosError } from 'axios'
import { APIResponse, ErrorResponse } from '~types/api'
import { axiosInstance } from '~apis/axiosInstance'

interface DeleteDogProfileResponse {
export type DeleteDogProfileRequest = {
id: number
}

export type DeleteDogProfileResponse = {
data: Record<string, never>
}

export const deleteDogProfile = async (id: number): Promise<APIResponse<DeleteDogProfileResponse>> => {
/**
* 반려견 프로필을 삭제합니다.
*/
export const deleteDogProfile = async (
req: DeleteDogProfileRequest
): Promise<APIResponse<DeleteDogProfileResponse>> => {
try {
const { data } = await axiosInstance.delete<APIResponse<DeleteDogProfileResponse>>(`/dogs/${id}`)
const { data } = await axiosInstance.delete<APIResponse<DeleteDogProfileResponse>>(`/dogs/${req.id}`)
return data
} catch (error) {
if (error instanceof AxiosError) {
const { response, request } = error as AxiosError<ErrorResponse>
const { response } = error as AxiosError<ErrorResponse>

if (response) {
console.error('반려견 삭제 오류:', response.data)
throw new Error(response.data.message ?? '요청 실패')
}

if (request) {
console.error('요청 에러:', request)
throw new Error('네트워크 연결을 확인해주세요')
const { code, message } = response.data
switch (code) {
case 400:
throw new Error(message || '잘못된 요청입니다.')
case 401:
throw new Error(message || '인증에 실패했습니다.')
case 500:
throw new Error(message || '서버 오류가 발생했습니다.')
default:
throw new Error(message || '알 수 없는 오류가 발생했습니다.')
}
}
// 요청 자체가 실패한 경우
throw new Error('네트워크 연결을 확인해주세요')
}

console.error('예상치 못한 에러:', error)
Expand Down
52 changes: 28 additions & 24 deletions src/apis/dog/fetchDogProfile.ts
Original file line number Diff line number Diff line change
@@ -1,41 +1,45 @@
import { AxiosError } from 'axios'
import { APIResponse, ErrorResponse } from '~types/api'
import { APIResponse, CommonAPIResponse, ErrorResponse } from '~types/api'
import { axiosInstance } from '~apis/axiosInstance'

interface DogProfileDetail {
dogId: number
name: string
breed: string
birthDate: string
weight: number
gender: 'MALE' | 'FEMALE'
profileImg: string
isNeutered: 'TRUE' | 'FALSE'
familyId: number
comment: string
type DogProfileDetail = Pick<
CommonAPIResponse,
'dogId' | 'name' | 'breed' | 'birthDate' | 'weight' | 'gender' | 'profileImg' | 'isNeutered' | 'familyId' | 'comment'
>

export type FetchDogProfileRequest = {
id: number
}

export interface FetchDogProfileResponse {
export type FetchDogProfileResponse = {
data: DogProfileDetail
}

export const fetchDogProfile = async (id: number): Promise<APIResponse<FetchDogProfileResponse>> => {
/**
* 반려견의 상세 프로필 정보를 조회합니다.
*/
export const fetchDogProfile = async (req: FetchDogProfileRequest): Promise<APIResponse<FetchDogProfileResponse>> => {
try {
const { data } = await axiosInstance.get<APIResponse<FetchDogProfileResponse>>(`/dogs/${id}`)
const { data } = await axiosInstance.get<APIResponse<FetchDogProfileResponse>>(`/dogs/${req.id}`)
return data
} catch (error) {
if (error instanceof AxiosError) {
const { response, request } = error as AxiosError<ErrorResponse>
const { response } = error as AxiosError<ErrorResponse>

if (response) {
console.error('반려견 조회 오류:', response.data)
throw new Error(response.data.message ?? '요청 실패')
}

if (request) {
console.error('요청 에러:', request)
throw new Error('네트워크 연결을 확인해주세요')
const { code, message } = response.data
switch (code) {
case 400:
throw new Error(message || '잘못된 요청입니다.')
case 401:
throw new Error(message || '인증에 실패했습니다.')
case 500:
throw new Error(message || '서버 오류가 발생했습니다.')
default:
throw new Error(message || '알 수 없는 오류가 발생했습니다.')
}
}
// 요청 자체가 실패한 경우
throw new Error('네트워크 연결을 확인해주세요')
}

console.error('예상치 못한 에러:', error)
Expand Down
48 changes: 48 additions & 0 deletions src/apis/dog/patchDogProfile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { AxiosError } from 'axios'
import { APIResponse, ErrorResponse } from '~types/api'
import { axiosInstance } from '~apis/axiosInstance'
import { DogProfileType } from '~types/dogProfile'

export type PatchDogProfileRequest = FormData

export type PatchDogProfileResponse = DogProfileType

/**
* 반려견 프로필 정보를 수정합니다.
*/
export const patchDogProfile = async (
id: number,
req: PatchDogProfileRequest
): Promise<APIResponse<PatchDogProfileResponse>> => {
try {
const { data } = await axiosInstance.patch<APIResponse<PatchDogProfileResponse>>(`/dogs/${id}`, req, {
headers: {
'Content-Type': 'multipart/form-data',
},
})
return data
} catch (error) {
if (error instanceof AxiosError) {
const { response } = error as AxiosError<ErrorResponse>

if (response) {
const { code, message } = response.data
switch (code) {
case 400:
throw new Error(message || '잘못된 요청입니다.')
case 401:
throw new Error(message || '인증에 실패했습니다.')
case 500:
throw new Error(message || '서버 오류가 발생했습니다.')
default:
throw new Error(message || '알 수 없는 오류가 발생했습니다.')
}
}

throw new Error('네트워크 연결을 확인해주세요')
}

console.error('예상치 못한 에러:', error)
throw new Error('다시 시도해주세요')
}
}
62 changes: 62 additions & 0 deletions src/apis/dog/useDogProfile.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { useMutation, useQueryClient } from '@tanstack/react-query'
import { createDogProfile } from '~apis/dog/createDogProfile'
import { useNavigate } from 'react-router-dom'
import { useModalStore } from '~stores/modalStore'
import { useToastStore } from '~stores/toastStore'
import { useDogProfileStore } from '~stores/dogProfileStore'
import { useSuspenseQuery } from '@tanstack/react-query'
import { fetchDogProfile } from '~apis/dog/fetchDogProfile'
import { queryKey } from '~constants/queryKey'
import { patchDogProfile, PatchDogProfileRequest } from '~apis/dog/patchDogProfile'
import ConfirmModal from '~modals/ConfirmModal'

export function useCreateDogProfile() {
const { setDogProfile } = useDogProfileStore()
const { pushModal, clearModal } = useModalStore()
const { showToast } = useToastStore()
const navigate = useNavigate()

const completeRegistration = () => {
navigate('/')
clearModal()
}

return useMutation({
mutationFn: (formData: FormData) => createDogProfile(formData),
onSuccess: response => {
setDogProfile({ ...response.data })
pushModal(<ConfirmModal content='반려견 등록이 완료되었습니다' onClick={completeRegistration} />)
},
onError: (error: Error) => {
showToast(error.message)
},
})
}

export function useFetchDogProfile(id: number) {
return useSuspenseQuery({
queryKey: queryKey.dog.profile(id),
queryFn: () => fetchDogProfile({ id }).then(res => res.data),
})
}

export function usePatchDogProfile(id: number) {
const queryClient = useQueryClient()
const { clearModal, pushModal } = useModalStore()
const { showToast } = useToastStore()

const completeRegistration = () => {
queryClient.invalidateQueries({ queryKey: queryKey.dog.profile(id) })
clearModal()
}

return useMutation({
mutationFn: (data: PatchDogProfileRequest) => patchDogProfile(id, data),
onSuccess: () => {
pushModal(<ConfirmModal content='반려견 정보가 수정되었습니다' onClick={completeRegistration} />)
},
onError: (error: Error) => {
showToast(error.message)
},
})
}
49 changes: 49 additions & 0 deletions src/apis/family/fetchFamilyDDang.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { AxiosError } from 'axios'
import { APIResponse, CommonAPIResponse, ErrorResponse } from '~types/api'
import { axiosInstance } from '~apis/axiosInstance'
import { DayOfWeek } from '~types/common'

export type FetchFamilyDDangResponse = Pick<
CommonAPIResponse,
'familyId' | 'dogs' | 'totalWalkCount' | 'totalDistanceInKilometers' | 'totalCalorie'
> & {
members: (CommonAPIResponse['members'][number] & {
walkScheduleInfoList: {
walkScheduleId: number
dayOfWeek: DayOfWeek
walkTime: string
}[]
totalWalkCount: number // 추가된 속성
})[]
}

export const fetchFamilyDDang = async (): Promise<APIResponse<FetchFamilyDDangResponse>> => {
try {
const { data } = await axiosInstance.get<APIResponse<FetchFamilyDDangResponse>>(`/family`)
console.log('패밀리댕 정보 바인딩 : ', data)
return data
} catch (error) {
if (error instanceof AxiosError) {
const { response } = error as AxiosError<ErrorResponse>

if (response) {
const { code, message } = response.data
switch (code) {
case 400:
throw new Error(message || '잘못된 요청입니다.')
case 401:
throw new Error(message || '인증에 실패했습니다.')
case 500:
throw new Error(message || '서버 오류가 발생했습니다.')
default:
throw new Error(message || '알 수 없는 오류가 발생했습니다.')
}
}
// 요청 자체가 실패한 경우
throw new Error('네트워크 연결을 확인해주세요')
}

console.error('예상치 못한 에러:', error)
throw new Error('다시 시도해주세요')
}
}
Loading

0 comments on commit 6b5818b

Please sign in to comment.