From cb127ac84d115982a28a93dfcb0d5dae38474b04 Mon Sep 17 00:00:00 2001 From: wynter24 Date: Sat, 4 Jan 2025 23:34:00 +0900 Subject: [PATCH 01/18] =?UTF-8?q?=E2=9C=A8[Feat]=20=EB=AA=A8=EC=9E=84=20?= =?UTF-8?q?=EB=AA=A9=EB=A1=9D=20=EC=BF=BC=EB=A6=AC=EA=B0=92=20=EC=A7=81?= =?UTF-8?q?=EC=A0=91=20=EC=82=AC=EC=9A=A9=20=EB=B0=8F=20=EC=8B=A0=EC=B2=AD?= =?UTF-8?q?=20=EA=B0=80=EB=8A=A5=20=ED=95=84=ED=84=B0=EB=A7=81=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20#290?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/common-layout/FilterBar.tsx | 21 ++----------- .../common-layout/FilterSection.tsx | 30 +++---------------- .../bookclub/components/BookClubMainPage.tsx | 17 ++--------- .../bookclub/hooks/useFetchBookClubList.ts | 19 +++--------- src/types/bookclubs.ts | 1 + 5 files changed, 14 insertions(+), 74 deletions(-) diff --git a/src/components/common-layout/FilterBar.tsx b/src/components/common-layout/FilterBar.tsx index 3f650976..e992ebc1 100644 --- a/src/components/common-layout/FilterBar.tsx +++ b/src/components/common-layout/FilterBar.tsx @@ -3,24 +3,14 @@ import { SearchSection, FilterSection, } from '@/components/common-layout'; -import { BookClub, BookClubParams } from '@/types/bookclubs'; -import { Dispatch, SetStateAction } from 'react'; +import { BookClubParams } from '@/types/bookclubs'; interface FilterBarProps { filters: BookClubParams; handleFilterChange: (newFilter: Partial) => void; - bookClubs: BookClub[]; - initialBookClubs: BookClub[]; - setBookClubs: Dispatch>; } -function FilterBar({ - filters, - handleFilterChange, - bookClubs, - initialBookClubs, - setBookClubs, -}: FilterBarProps) { +function FilterBar({ filters, handleFilterChange }: FilterBarProps) { return (
@@ -30,12 +20,7 @@ function FilterBar({ handleFilterChange({ searchKeyword: value }) } /> - +
); } diff --git a/src/components/common-layout/FilterSection.tsx b/src/components/common-layout/FilterSection.tsx index 2dfa8c8c..28fc3851 100644 --- a/src/components/common-layout/FilterSection.tsx +++ b/src/components/common-layout/FilterSection.tsx @@ -2,45 +2,23 @@ import DropDown from '@/components/drop-down/DropDown'; import FilterCheckbox from '@/components/filter-checkbox/FilterCheckbox'; -import { ChangeEvent, Dispatch, SetStateAction, useState } from 'react'; +import { ChangeEvent, useState } from 'react'; import SortingButton from '@/components/sorting-button/SortingButton'; -import { BookClub, BookClubParams } from '../../types/bookclubs'; +import { BookClubParams } from '../../types/bookclubs'; import { getMeetingType, getMemberLimit } from '@/lib/utils/filterUtils'; -import { clubStatus } from '@/lib/utils/clubUtils'; interface CategoryTabsProps { - bookClubs: BookClub[]; - initialBookClubs: BookClub[]; - setBookClubs: Dispatch>; onFilterChange: (newFilters: Partial) => void; } -function FilterSection({ - bookClubs, - initialBookClubs, - setBookClubs, - onFilterChange, -}: CategoryTabsProps) { +function FilterSection({ onFilterChange }: CategoryTabsProps) { const [showAvailableOnly, setShowAvailableOnly] = useState(false); // 신청가능 const toggleAvailableOnly = (e: ChangeEvent) => { const isChecked = e.target.checked; setShowAvailableOnly(isChecked); - const filteredBookClubs = isChecked - ? bookClubs.filter( - (club) => - club.memberCount < club.memberLimit && - clubStatus( - club.memberCount, - club.memberLimit, - club.endDate, - new Date(), - ) !== 'closed', - ) - : initialBookClubs; - - setBookClubs(filteredBookClubs); + onFilterChange({ isAvailable: isChecked }); }; const updateMemberLimitFilter = (selectedValue: string | undefined) => { diff --git a/src/features/bookclub/components/BookClubMainPage.tsx b/src/features/bookclub/components/BookClubMainPage.tsx index 8d027d28..4cdc7632 100644 --- a/src/features/bookclub/components/BookClubMainPage.tsx +++ b/src/features/bookclub/components/BookClubMainPage.tsx @@ -9,14 +9,7 @@ import { useRouter } from 'next/navigation'; import Loading from '@/components/loading/Loading'; function BookClubMainPage() { - const { - clubList, - initialBookClubs, - setClubList, - isLoading, - filters, - updateFilters, - } = useBookClubList(); + const { clubList, isLoading, filters, updateFilters } = useBookClubList(); const router = useRouter(); @@ -48,13 +41,7 @@ function BookClubMainPage() { /> } /> - + {isLoading ? (
diff --git a/src/features/bookclub/hooks/useFetchBookClubList.ts b/src/features/bookclub/hooks/useFetchBookClubList.ts index 974374af..870584b9 100644 --- a/src/features/bookclub/hooks/useFetchBookClubList.ts +++ b/src/features/bookclub/hooks/useFetchBookClubList.ts @@ -1,12 +1,9 @@ -import { useState, useEffect } from 'react'; -import { BookClub, BookClubParams } from '@/types/bookclubs'; +import { useState } from 'react'; +import { BookClubParams } from '@/types/bookclubs'; import { useQuery } from '@tanstack/react-query'; import { bookClubs } from '@/api/book-club/react-query'; const useBookClubList = () => { - // TODO: 신청 가능 필터 param 추가시 clubList, initialBookClubs 상태 관리x - const [clubList, setClubList] = useState([]); - const [initialBookClubs, setInitialBookClubs] = useState([]); const [filters, setFilters] = useState({ bookClubType: 'ALL', meetingType: 'ALL', @@ -20,15 +17,9 @@ const useBookClubList = () => { ...bookClubs.list(filters), }); - const clubInfo = data?.bookClubs; + const clubList = data?.bookClubs; - // TODO: param 추가시, useEffect 대신 clubInfo 직접 사용 - useEffect(() => { - if (clubInfo) { - setClubList(clubInfo); - setInitialBookClubs(clubInfo); // 초기 데이터 설정 - } - }, [clubInfo]); + console.log(clubList); const updateFilters = (newFilters: Partial) => { setFilters((prevFilters) => ({ ...prevFilters, ...newFilters })); @@ -36,8 +27,6 @@ const useBookClubList = () => { return { clubList, - initialBookClubs, - setClubList, isLoading, error, filters, diff --git a/src/types/bookclubs.ts b/src/types/bookclubs.ts index 45d1426f..81b9c090 100644 --- a/src/types/bookclubs.ts +++ b/src/types/bookclubs.ts @@ -10,6 +10,7 @@ export interface BookClubParams { searchKeyword?: string; memberLimitMin?: number; memberLimitMax?: number; + isAvailable?: boolean; } export interface MyProfileParams { From 23590af5e5531023f7e30dfcfc6dbb0c62adb586 Mon Sep 17 00:00:00 2001 From: wynter24 Date: Sun, 5 Jan 2025 10:13:27 +0900 Subject: [PATCH 02/18] =?UTF-8?q?=F0=9F=90=9B[Fix]=20=EC=B0=9C=ED=95=98?= =?UTF-8?q?=EA=B8=B0=20=EB=82=99=EA=B4=80=EC=A0=81=20=EC=97=85=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=8A=B8=ED=95=98=EA=B8=B0=20#290?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/book-club/react-query/customHooks.ts | 57 ++++++++++++++++--- .../bookclub/hooks/useFetchBookClubList.ts | 7 ++- 2 files changed, 55 insertions(+), 9 deletions(-) diff --git a/src/api/book-club/react-query/customHooks.ts b/src/api/book-club/react-query/customHooks.ts index defec8ef..f8523b95 100644 --- a/src/api/book-club/react-query/customHooks.ts +++ b/src/api/book-club/react-query/customHooks.ts @@ -9,6 +9,7 @@ import { } from '../index'; import { WriteReviewParams } from '../types'; import { AxiosError } from 'axios'; +import { BookClub, BookClubParams } from '@/types/bookclubs'; export function useBookClubCreateMutation() { const queryClient = useQueryClient(); @@ -102,15 +103,55 @@ export function useCancelBookClub() { export function useLikeBookClub() { const queryClient = useQueryClient(); - return useMutation, number>({ + return useMutation< + void, + AxiosError<{ message: string }>, + number, + { previousBookClubs?: any } + >({ mutationFn: (id: number) => bookClubLikeAPI.like(id), - onSuccess: (_, id) => { - queryClient.invalidateQueries({ - queryKey: bookClubs.list().queryKey, - }); - queryClient.invalidateQueries({ - queryKey: bookClubs.detail(id).queryKey, - }); + + onMutate: async (id) => { + const filters: BookClubParams = { + bookClubType: 'ALL', + meetingType: 'ALL', + order: 'DESC', + page: 1, + size: 10, + searchKeyword: '', + }; + + const queryKey = bookClubs.list(filters).queryKey; + + // console.log('onMutate 쿼리 키:', queryKey); + + // 기존 캐시 데이터 확인 + const previousBookClubs = queryClient.getQueryData<{ + bookClubs: BookClub[]; + }>(queryKey); + + // console.log('onMutate 이전 캐시 데이터:', previousBookClubs); + + if (previousBookClubs) { + queryClient.setQueryData(queryKey, { + ...previousBookClubs, + bookClubs: previousBookClubs.bookClubs.map((club: any) => + club.id === id ? { ...club, isLiked: true } : club, + ), + }); + } + + return { previousBookClubs }; + }, + + onError: (_error, _id, context) => { + // 요청 실패 시 이전 상태 복구 + if (context?.previousBookClubs) { + queryClient.setQueryData( + bookClubs.list().queryKey, + context.previousBookClubs, + ); + } }, }); } diff --git a/src/features/bookclub/hooks/useFetchBookClubList.ts b/src/features/bookclub/hooks/useFetchBookClubList.ts index 870584b9..1a45ce33 100644 --- a/src/features/bookclub/hooks/useFetchBookClubList.ts +++ b/src/features/bookclub/hooks/useFetchBookClubList.ts @@ -2,6 +2,7 @@ import { useState } from 'react'; import { BookClubParams } from '@/types/bookclubs'; import { useQuery } from '@tanstack/react-query'; import { bookClubs } from '@/api/book-club/react-query'; +// import { queryClient } from '@/lib/utils/reactQueryProvider'; const useBookClubList = () => { const [filters, setFilters] = useState({ @@ -19,7 +20,11 @@ const useBookClubList = () => { const clubList = data?.bookClubs; - console.log(clubList); + // console.log('쿼리 키:', bookClubs.list(filters).queryKey); + console.log('useQuery 데이터:', clubList); + + // const queryKey = bookClubs.list(filters).queryKey; + // console.log('캐시 데이터:', queryClient.getQueryData(queryKey)); const updateFilters = (newFilters: Partial) => { setFilters((prevFilters) => ({ ...prevFilters, ...newFilters })); From 26ff9124e170d042f276edd3625b1e7a4e66b216 Mon Sep 17 00:00:00 2001 From: wynter24 Date: Sun, 5 Jan 2025 11:17:10 +0900 Subject: [PATCH 03/18] =?UTF-8?q?=E2=99=BB=EF=B8=8F[Refactor]=20=EA=B8=B0?= =?UTF-8?q?=EB=B3=B8=20=ED=95=84=ED=84=B0=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20?= =?UTF-8?q?=EA=B3=B5=ED=86=B5=EC=9C=BC=EB=A1=9C=20=EB=B6=84=EB=A6=AC=20#29?= =?UTF-8?q?0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/bookclub/hooks/useFetchBookClubList.ts | 10 ++-------- src/lib/constants/filters.ts | 10 ++++++++++ 2 files changed, 12 insertions(+), 8 deletions(-) create mode 100644 src/lib/constants/filters.ts diff --git a/src/features/bookclub/hooks/useFetchBookClubList.ts b/src/features/bookclub/hooks/useFetchBookClubList.ts index 1a45ce33..63545187 100644 --- a/src/features/bookclub/hooks/useFetchBookClubList.ts +++ b/src/features/bookclub/hooks/useFetchBookClubList.ts @@ -2,17 +2,11 @@ import { useState } from 'react'; import { BookClubParams } from '@/types/bookclubs'; import { useQuery } from '@tanstack/react-query'; import { bookClubs } from '@/api/book-club/react-query'; +import { DEFAULT_FILTERS } from '@/lib/constants/filters'; // import { queryClient } from '@/lib/utils/reactQueryProvider'; const useBookClubList = () => { - const [filters, setFilters] = useState({ - bookClubType: 'ALL', - meetingType: 'ALL', - order: 'DESC', - page: 1, - size: 10, - searchKeyword: '', - }); + const [filters, setFilters] = useState(DEFAULT_FILTERS); const { data, isLoading, error } = useQuery({ ...bookClubs.list(filters), diff --git a/src/lib/constants/filters.ts b/src/lib/constants/filters.ts new file mode 100644 index 00000000..0ee948a3 --- /dev/null +++ b/src/lib/constants/filters.ts @@ -0,0 +1,10 @@ +import { BookClubParams } from '@/types/bookclubs'; + +export const DEFAULT_FILTERS: BookClubParams = { + bookClubType: 'ALL', + meetingType: 'ALL', + order: 'DESC', + page: 1, + size: 10, + searchKeyword: '', +}; From a7c2ec76a1a7de8b22d87dea525889bb00139d00 Mon Sep 17 00:00:00 2001 From: wynter24 Date: Sun, 5 Jan 2025 11:18:16 +0900 Subject: [PATCH 04/18] =?UTF-8?q?=E2=99=BB=EF=B8=8F[Refactor]=20=EC=B0=9C?= =?UTF-8?q?=20=EA=B4=80=EB=A0=A8=20=EB=82=99=EA=B4=80=EC=A0=84=20=EC=97=85?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=8A=B8=20=EB=A1=9C=EC=A7=81=20=EA=B3=B5?= =?UTF-8?q?=ED=86=B5=EC=9C=BC=EB=A1=9C=20=EB=B6=84=EB=A6=AC=20#290?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../react-query/likeOptimisticUpdate.ts | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 src/api/book-club/react-query/likeOptimisticUpdate.ts diff --git a/src/api/book-club/react-query/likeOptimisticUpdate.ts b/src/api/book-club/react-query/likeOptimisticUpdate.ts new file mode 100644 index 00000000..f509ca45 --- /dev/null +++ b/src/api/book-club/react-query/likeOptimisticUpdate.ts @@ -0,0 +1,60 @@ +import { QueryClient } from '@tanstack/react-query'; +import { BookClub } from '@/types/bookclubs'; +import { bookClubs } from './queries'; +import { DEFAULT_FILTERS } from '@/lib/constants/filters'; + +export const likeOnMutate = async ( + queryClient: QueryClient, + id: number, + isLiked: boolean, +) => { + const listQueryKey = bookClubs.list(DEFAULT_FILTERS).queryKey; + const detailQueryKey = bookClubs.detail(id).queryKey; + + const previousBookClubs = queryClient.getQueryData<{ + bookClubs: BookClub[]; + }>(listQueryKey); + + const previousDetail = queryClient.getQueryData(detailQueryKey); + + // 목록 캐시 업데이트 + if (previousBookClubs) { + queryClient.setQueryData(listQueryKey, { + ...previousBookClubs, + bookClubs: previousBookClubs.bookClubs.map((club) => + club.id === id ? { ...club, isLiked } : club, + ), + }); + } + + // 상세 캐시 업데이트 + if (previousDetail) { + queryClient.setQueryData(detailQueryKey, { + ...previousDetail, + isLiked, + }); + } + + return { previousBookClubs, previousDetail }; +}; + +export const likeOnError = ( + queryClient: QueryClient, + id: number, + context: { + previousBookClubs?: { bookClubs: BookClub[] }; + previousDetail?: BookClub; + }, +) => { + const listQueryKey = bookClubs.list(DEFAULT_FILTERS).queryKey; + const detailQueryKey = bookClubs.detail(id).queryKey; + + // 이전 상태 복구 + if (context.previousBookClubs) { + queryClient.setQueryData(listQueryKey, context.previousBookClubs); + } + + if (context.previousDetail) { + queryClient.setQueryData(detailQueryKey, context.previousDetail); + } +}; From 74998dbf8cb7a577154f151bf502104d403e0d54 Mon Sep 17 00:00:00 2001 From: wynter24 Date: Sun, 5 Jan 2025 11:19:12 +0900 Subject: [PATCH 05/18] =?UTF-8?q?=E2=9C=A8[Feat]=20=EC=B0=9C=ED=95=98?= =?UTF-8?q?=EA=B8=B0,=20=EC=82=AD=EC=A0=9C=ED=95=98=EA=B8=B0=EC=97=90=20?= =?UTF-8?q?=EB=82=99=EA=B4=80=EC=A0=81=20=EC=97=85=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=8A=B8=20=EC=A0=81=EC=9A=A9=20#290?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/book-club/react-query/customHooks.ts | 66 +++++--------------- 1 file changed, 15 insertions(+), 51 deletions(-) diff --git a/src/api/book-club/react-query/customHooks.ts b/src/api/book-club/react-query/customHooks.ts index f8523b95..1251357e 100644 --- a/src/api/book-club/react-query/customHooks.ts +++ b/src/api/book-club/react-query/customHooks.ts @@ -9,7 +9,7 @@ import { } from '../index'; import { WriteReviewParams } from '../types'; import { AxiosError } from 'axios'; -import { BookClub, BookClubParams } from '@/types/bookclubs'; +import { likeOnError, likeOnMutate } from './likeOptimisticUpdate'; export function useBookClubCreateMutation() { const queryClient = useQueryClient(); @@ -103,54 +103,16 @@ export function useCancelBookClub() { export function useLikeBookClub() { const queryClient = useQueryClient(); - return useMutation< - void, - AxiosError<{ message: string }>, - number, - { previousBookClubs?: any } - >({ + return useMutation, number>({ mutationFn: (id: number) => bookClubLikeAPI.like(id), onMutate: async (id) => { - const filters: BookClubParams = { - bookClubType: 'ALL', - meetingType: 'ALL', - order: 'DESC', - page: 1, - size: 10, - searchKeyword: '', - }; - - const queryKey = bookClubs.list(filters).queryKey; - - // console.log('onMutate 쿼리 키:', queryKey); - - // 기존 캐시 데이터 확인 - const previousBookClubs = queryClient.getQueryData<{ - bookClubs: BookClub[]; - }>(queryKey); - - // console.log('onMutate 이전 캐시 데이터:', previousBookClubs); - - if (previousBookClubs) { - queryClient.setQueryData(queryKey, { - ...previousBookClubs, - bookClubs: previousBookClubs.bookClubs.map((club: any) => - club.id === id ? { ...club, isLiked: true } : club, - ), - }); - } - - return { previousBookClubs }; + return likeOnMutate(queryClient, id, true); }, - onError: (_error, _id, context) => { - // 요청 실패 시 이전 상태 복구 - if (context?.previousBookClubs) { - queryClient.setQueryData( - bookClubs.list().queryKey, - context.previousBookClubs, - ); + onError: (_error, id, context) => { + if (context) { + likeOnError(queryClient, id, context); } }, }); @@ -161,13 +123,15 @@ export function useUnLikeBookClub() { return useMutation, number>({ mutationFn: (id: number) => bookClubLikeAPI.unlike(id), - onSuccess: (_, id) => { - queryClient.invalidateQueries({ - queryKey: bookClubs.list().queryKey, - }); - queryClient.invalidateQueries({ - queryKey: bookClubs.detail(id).queryKey, - }); + + onMutate: async (id) => { + return likeOnMutate(queryClient, id, false); + }, + + onError: (_error, id, context) => { + if (context) { + likeOnError(queryClient, id, context); + } }, }); } From fe86d10385628f339a33031bfb347c46e6ba37a0 Mon Sep 17 00:00:00 2001 From: wynter24 Date: Sun, 5 Jan 2025 11:57:32 +0900 Subject: [PATCH 06/18] =?UTF-8?q?=F0=9F=92=AC[Comment]=20=EB=B6=88?= =?UTF-8?q?=ED=95=84=EC=9A=94=ED=95=9C=20=EC=A3=BC=EC=84=9D=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0=20#290?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/bookclub/hooks/useFetchBookClubList.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/features/bookclub/hooks/useFetchBookClubList.ts b/src/features/bookclub/hooks/useFetchBookClubList.ts index 63545187..8d72f6a1 100644 --- a/src/features/bookclub/hooks/useFetchBookClubList.ts +++ b/src/features/bookclub/hooks/useFetchBookClubList.ts @@ -3,7 +3,6 @@ import { BookClubParams } from '@/types/bookclubs'; import { useQuery } from '@tanstack/react-query'; import { bookClubs } from '@/api/book-club/react-query'; import { DEFAULT_FILTERS } from '@/lib/constants/filters'; -// import { queryClient } from '@/lib/utils/reactQueryProvider'; const useBookClubList = () => { const [filters, setFilters] = useState(DEFAULT_FILTERS); @@ -14,11 +13,7 @@ const useBookClubList = () => { const clubList = data?.bookClubs; - // console.log('쿼리 키:', bookClubs.list(filters).queryKey); - console.log('useQuery 데이터:', clubList); - - // const queryKey = bookClubs.list(filters).queryKey; - // console.log('캐시 데이터:', queryClient.getQueryData(queryKey)); + // console.log('useQuery 데이터:', clubList); const updateFilters = (newFilters: Partial) => { setFilters((prevFilters) => ({ ...prevFilters, ...newFilters })); From 9909924b239da535555c6463c7b225dd45398a3c Mon Sep 17 00:00:00 2001 From: wynter24 Date: Sun, 5 Jan 2025 11:59:54 +0900 Subject: [PATCH 07/18] =?UTF-8?q?=F0=9F=92=84[Design]=20=EB=A6=AC=EB=B7=B0?= =?UTF-8?q?=20=ED=8F=89=EC=A0=90=EC=9D=B4=20NaN=EC=9D=BC=20=EA=B2=BD?= =?UTF-8?q?=EC=9A=B0=200=EC=9C=BC=EB=A1=9C=20=ED=91=9C=EC=8B=9C=20#290?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/club-details/components/ReviewSummary.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/features/club-details/components/ReviewSummary.tsx b/src/features/club-details/components/ReviewSummary.tsx index 3abb9e6c..3c61c75e 100644 --- a/src/features/club-details/components/ReviewSummary.tsx +++ b/src/features/club-details/components/ReviewSummary.tsx @@ -7,6 +7,10 @@ import { } from '@/features/club-details/utils/rating'; function ReviewSummary({ reviewInfo }: { reviewInfo: ClubReviewResponse }) { + const validAverageScore = isNaN(reviewInfo.averageScore) + ? 0 + : reviewInfo.averageScore; + return (

@@ -20,10 +24,10 @@ function ReviewSummary({ reviewInfo }: { reviewInfo: ClubReviewResponse }) { >
- {reviewInfo.averageScore} + {validAverageScore} / 5
- +
{[5, 4, 3, 2, 1].map((score) => ( From 41261c75de8eb887432481d2c3cec2285281bfed Mon Sep 17 00:00:00 2001 From: wynter24 Date: Sun, 5 Jan 2025 12:16:18 +0900 Subject: [PATCH 08/18] =?UTF-8?q?=E2=9C=A8[Feat]=20=EC=A3=BC=EC=B5=9C?= =?UTF-8?q?=EC=9E=90=20=EB=AA=A8=EC=9E=84=20=EC=B7=A8=EC=86=8C=ED=95=98?= =?UTF-8?q?=EA=B8=B0=20=ED=9B=84=20=EB=AA=A8=EC=9E=84=20=EB=AA=A9=EB=A1=9D?= =?UTF-8?q?=20=EC=BA=90=EC=8B=9C=20=EB=AC=B4=ED=9A=A8=ED=99=94=20#290?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/book-club/react-query/customHooks.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/api/book-club/react-query/customHooks.ts b/src/api/book-club/react-query/customHooks.ts index 1251357e..76b5250f 100644 --- a/src/api/book-club/react-query/customHooks.ts +++ b/src/api/book-club/react-query/customHooks.ts @@ -90,6 +90,9 @@ export function useCancelBookClub() { return useMutation({ mutationFn: (id: number) => bookClubMainAPI.cancel(id), onSuccess: () => { + queryClient.invalidateQueries({ + queryKey: bookClubs.list().queryKey, + }); queryClient.invalidateQueries({ queryKey: bookClubs.my()._ctx.created().queryKey, }); From 6320b55e561eeb00c30646bc519aae240b061be9 Mon Sep 17 00:00:00 2001 From: wynter24 Date: Sun, 5 Jan 2025 13:16:05 +0900 Subject: [PATCH 09/18] =?UTF-8?q?=F0=9F=90=9B[Fix]=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=EB=90=98=EC=A7=80=20=EC=95=8A=EB=8A=94=20=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=84=B0=20=EC=A0=9C=EA=B1=B0=20#290?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/club-wish/components/WishPage.tsx | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/features/club-wish/components/WishPage.tsx b/src/features/club-wish/components/WishPage.tsx index 272aa81b..d9a165fe 100644 --- a/src/features/club-wish/components/WishPage.tsx +++ b/src/features/club-wish/components/WishPage.tsx @@ -18,13 +18,7 @@ function WishPage() { } /> - {}} - bookClubs={mockBookClubs} - setBookClubs={() => {}} - initialBookClubs={[]} - /> + {}} /> ); From 30226b335105a04975442cdbf488f98090bad1fd Mon Sep 17 00:00:00 2001 From: cloud0406 <32586926+cloud0406@users.noreply.github.com> Date: Sun, 5 Jan 2025 15:47:29 +0900 Subject: [PATCH 10/18] =?UTF-8?q?=F0=9F=90=9B=20[Bug]=20=EC=B0=9C=ED=95=98?= =?UTF-8?q?=EA=B8=B0=20=EB=8F=99=EC=9E=91=20=EC=97=90=EB=9F=AC=20(#294)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ♻️[Refactor] 유저 북클럽 조회 api 경로 변경 * ♻️[Refactor] 찜하기시 쿼리 무효화 * 🐛[Fix] 찜하기 버그 임시 수정 --- src/api/book-club/bookClubMainAPI.ts | 11 +++++++---- src/api/book-club/react-query/customHooks.ts | 12 ++++++++++++ .../book-club/react-query/likeOptimisticUpdate.ts | 5 +++++ 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/api/book-club/bookClubMainAPI.ts b/src/api/book-club/bookClubMainAPI.ts index 12ad39cc..a37a09e5 100644 --- a/src/api/book-club/bookClubMainAPI.ts +++ b/src/api/book-club/bookClubMainAPI.ts @@ -22,7 +22,7 @@ export const bookClubMainAPI = { //유저가 참가한 북클럽 조회 userJoined: async (userId: number, params?: MyProfileParams) => { - const response = await apiClient.get(`/book-clubs/user/${userId}/joined`, { + const response = await apiClient.get(`/book-clubs/users/${userId}/joined`, { params, }); return response.data; @@ -30,9 +30,12 @@ export const bookClubMainAPI = { //유저가 만든 북클럽 조회 userCreated: async (userId: number, params?: MyProfileParams) => { - const response = await apiClient.get(`/book-clubs/user/${userId}/created`, { - params, - }); + const response = await apiClient.get( + `/book-clubs/users/${userId}/created`, + { + params, + }, + ); return response.data; }, diff --git a/src/api/book-club/react-query/customHooks.ts b/src/api/book-club/react-query/customHooks.ts index 76b5250f..dcfdc23e 100644 --- a/src/api/book-club/react-query/customHooks.ts +++ b/src/api/book-club/react-query/customHooks.ts @@ -112,6 +112,12 @@ export function useLikeBookClub() { onMutate: async (id) => { return likeOnMutate(queryClient, id, true); }, + //TODO: 로직 확인 후 변경 필요 + onSuccess: () => { + queryClient.invalidateQueries({ + queryKey: bookClubs._def, + }); + }, onError: (_error, id, context) => { if (context) { @@ -130,6 +136,12 @@ export function useUnLikeBookClub() { onMutate: async (id) => { return likeOnMutate(queryClient, id, false); }, + //TODO: 로직 확인 후 변경 필요 + onSuccess: () => { + queryClient.invalidateQueries({ + queryKey: bookClubs._def, + }); + }, onError: (_error, id, context) => { if (context) { diff --git a/src/api/book-club/react-query/likeOptimisticUpdate.ts b/src/api/book-club/react-query/likeOptimisticUpdate.ts index f509ca45..86a0a536 100644 --- a/src/api/book-club/react-query/likeOptimisticUpdate.ts +++ b/src/api/book-club/react-query/likeOptimisticUpdate.ts @@ -35,6 +35,11 @@ export const likeOnMutate = async ( }); } + //TODO: 로직 확인 후 변경 필요 + queryClient.invalidateQueries({ + queryKey: bookClubs._def, + }); + return { previousBookClubs, previousDetail }; }; From f0635463d9d21187dd86538e319905823f47ddb3 Mon Sep 17 00:00:00 2001 From: sun <104830526+sunnwave@users.noreply.github.com> Date: Sun, 5 Jan 2025 15:49:11 +0900 Subject: [PATCH 11/18] =?UTF-8?q?=E2=9C=A8[Feat]=20=ED=94=84=EB=A1=9C?= =?UTF-8?q?=ED=95=84=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EC=88=98=EC=A0=95?= =?UTF-8?q?=ED=95=98=EA=B8=B0=20#287=20(#295)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 🔥[Remove] 불필요 코드 삭제 #287 * ✨[Feat] 작성한 리뷰 사용자 프로필 사진 표시 #287 * 🐛[Fix] DetailReview 타입 변경 * 🐛[Fix] mockData Review 타입 수정 * ✨[Feat] 채팅 페이지 프로필 이미지 클릭 시 유저 프로필 페이지로 이동 구현 --- src/api/auth/authClientAPI.ts | 12 ++--- src/api/auth/react-query/customHooks.ts | 5 +- src/components/header/HeaderBar.tsx | 2 + src/constants/avatar.ts | 1 + .../components/ChatMessage.tsx | 50 +++++++++++++------ .../club-details/components/ReviewList.tsx | 2 +- .../club-details/mocks/DetailReviewDatas.ts | 20 ++++---- .../components/clubs/ProfileWrittenReview.tsx | 4 +- src/features/profile/components/info/Info.tsx | 21 ++++---- .../profile/components/info/InfoEditModal.tsx | 25 ++++++---- .../profile/container/MyJoinedClubList.tsx | 3 +- src/features/profile/hooks/useEditInfo.tsx | 13 +++++ src/features/profile/types/index.ts | 8 +-- .../profile/utils/infoEditFormUtils.tsx | 23 +++++++++ src/mocks/mockDatas.ts | 48 +++++++++--------- src/types/review.ts | 14 +++--- 16 files changed, 159 insertions(+), 92 deletions(-) create mode 100644 src/features/profile/hooks/useEditInfo.tsx create mode 100644 src/features/profile/utils/infoEditFormUtils.tsx diff --git a/src/api/auth/authClientAPI.ts b/src/api/auth/authClientAPI.ts index dc17ff46..bbd87291 100644 --- a/src/api/auth/authClientAPI.ts +++ b/src/api/auth/authClientAPI.ts @@ -1,16 +1,16 @@ -import { EditInfoParams } from '@/features/profile/types'; import apiClient from '@/lib/utils/apiClient'; export const authClientAPI = { //회원정보 확인 //회원정보 수정 - editInfo: async ({ nickname, image, description }: EditInfoParams) => { - await apiClient.post('auths/user', { - nickname, - image, - description, + editInfo: async (formData: FormData) => { + const response = await apiClient.post('auths/user', formData, { + headers: { + 'Content-Type': 'multipart/form-data', + }, }); + return response.data; }, //회원가입 diff --git a/src/api/auth/react-query/customHooks.ts b/src/api/auth/react-query/customHooks.ts index 74958a5a..58834975 100644 --- a/src/api/auth/react-query/customHooks.ts +++ b/src/api/auth/react-query/customHooks.ts @@ -2,12 +2,11 @@ import { useMutation } from '@tanstack/react-query'; import { showToast } from '@/components/toast/toast'; import { authClientAPI } from '../authClientAPI'; import { getUserInfo } from '@/features/auth/api/auth'; -import { EditInfoParams } from '@/features/profile/types'; //프로필 수정하기 -export function useEditInfo() { +export function useEditInfoMutation() { return useMutation({ - mutationFn: (data: EditInfoParams) => authClientAPI.editInfo(data), + mutationFn: (formData: FormData) => authClientAPI.editInfo(formData), onSuccess: () => { getUserInfo(); showToast({ message: '프로필 수정이 완료되었습니다.', type: 'success' }); diff --git a/src/components/header/HeaderBar.tsx b/src/components/header/HeaderBar.tsx index 3cfba709..34a905db 100644 --- a/src/components/header/HeaderBar.tsx +++ b/src/components/header/HeaderBar.tsx @@ -7,6 +7,7 @@ import { usePathname, useRouter } from 'next/navigation'; import { useAuthStore } from '@/store/authStore'; import DropDown from '../drop-down/DropDown'; import { logout } from '@/features/auth/api/auth'; +import { showToast } from '../toast/toast'; function HeaderBar() { const pathname = usePathname(); @@ -17,6 +18,7 @@ function HeaderBar() { if (value === 'LOGOUT') { try { await logout(); + showToast({ message: '로그아웃 되었습니다 ', type: 'success' }); router.replace('/bookclub'); } catch (error) { console.error('로그아웃 실패:', error); diff --git a/src/constants/avatar.ts b/src/constants/avatar.ts index 509cb089..3105d6d9 100644 --- a/src/constants/avatar.ts +++ b/src/constants/avatar.ts @@ -5,6 +5,7 @@ export const AVATAR_SIZE = { lg: 'h-[56px] w-[56px]', xl: 'h-[74px] w-[71px]', max: 'h-[74px] w-[74px]', + profile: 'h-[80px] w-[80px]', } as const; export type AvatarSize = keyof typeof AVATAR_SIZE; diff --git a/src/features/chat-room/container/chat-bubble-list/components/ChatMessage.tsx b/src/features/chat-room/container/chat-bubble-list/components/ChatMessage.tsx index ebb1d0b5..21a491a0 100644 --- a/src/features/chat-room/container/chat-bubble-list/components/ChatMessage.tsx +++ b/src/features/chat-room/container/chat-bubble-list/components/ChatMessage.tsx @@ -1,5 +1,8 @@ import ChatBubble from '@/features/chat-room/components/chat-bubble/ChatBubble'; import { ChatMessageType } from '../../../types/chatBubbleList'; +import PopUp from '@/components/pop-up/PopUp'; +import { useState } from 'react'; +import { useRouter } from 'next/navigation'; interface ChatMessageProps { message: ChatMessageType; @@ -16,24 +19,41 @@ function ChatMessage({ isConsecutive, hostId, time, - onProfileClick, + // onProfileClick, }: ChatMessageProps) { const { userId, content, userNickname } = message; + const [isPopUpOpen, setIsPopUpOpen] = useState(false); + const router = useRouter(); - return isMyMessage ? ( - - ) : ( - onProfileClick?.(userId), - isConsecutive, - }} - /> + return ( + <> + {isMyMessage ? ( + + ) : ( + setIsPopUpOpen(true), + isConsecutive, + }} + /> + )} + setIsPopUpOpen(false)} + handlePopUpConfirm={() => { + setIsPopUpOpen(false); + router.push(`/profile/${userId}`); + }} + /> + ); } diff --git a/src/features/club-details/components/ReviewList.tsx b/src/features/club-details/components/ReviewList.tsx index 98b4b411..1d190c21 100644 --- a/src/features/club-details/components/ReviewList.tsx +++ b/src/features/club-details/components/ReviewList.tsx @@ -45,7 +45,7 @@ function ReviewList({ ratingCount={review.rating} comment={review.content} userProfile={{ - profileImage: review.image || '/images/profile.png', + profileImage: review.userImage || '/images/profile.png', userName: review.nickname, createdAt: formatDateForUI(review.createdAt, 'DATE_ONLY'), }} diff --git a/src/features/club-details/mocks/DetailReviewDatas.ts b/src/features/club-details/mocks/DetailReviewDatas.ts index f9a202fb..bb8d8443 100644 --- a/src/features/club-details/mocks/DetailReviewDatas.ts +++ b/src/features/club-details/mocks/DetailReviewDatas.ts @@ -8,7 +8,7 @@ export const mockReviews: DetailReview[] = [ rating: 5, content: '정말 멋진 독서 모임이었어요! 많은 것을 배웠습니다.', nickname: '책사랑꾼', - image: undefined, + userImage: undefined, createdAt: '2025-01-01T10:00:00Z', }, { @@ -18,7 +18,7 @@ export const mockReviews: DetailReview[] = [ rating: 3, content: '그럭저럭 괜찮았어요.', nickname: '평범한독자', - image: 'https://example.com/images/user2.jpg', + userImage: 'https://example.com/images/user2.jpg', createdAt: '2025-01-02T15:30:00Z', }, { @@ -28,7 +28,7 @@ export const mockReviews: DetailReview[] = [ rating: 1, content: '시간 낭비였던 것 같아요.', nickname: '비판적인독자', - image: undefined, + userImage: undefined, createdAt: '2025-01-01T09:00:00Z', }, { @@ -38,7 +38,7 @@ export const mockReviews: DetailReview[] = [ rating: 4, content: '좋은 경험이었지만 약간 서둘러서 진행된 느낌이었습니다.', nickname: '빠른독자', - image: 'https://example.com/images/user4.jpg', + userImage: 'https://example.com/images/user4.jpg', createdAt: '2025-01-03T12:00:00Z', }, { @@ -48,7 +48,7 @@ export const mockReviews: DetailReview[] = [ rating: 5, content: '지금까지 참가한 독서 모임 중 최고였습니다!', nickname: '열정독자', - image: undefined, + userImage: undefined, createdAt: '2025-01-02T08:00:00Z', }, { @@ -58,7 +58,7 @@ export const mockReviews: DetailReview[] = [ rating: 2, content: '조직적으로 더 나아질 필요가 있습니다.', nickname: '정리된혼돈', - image: 'https://example.com/images/user6.jpg', + userImage: 'https://example.com/images/user6.jpg', createdAt: '2025-01-04T14:45:00Z', }, { @@ -68,7 +68,7 @@ export const mockReviews: DetailReview[] = [ rating: 3, content: '평범했어요.', nickname: '평범한조', - image: undefined, + userImage: undefined, createdAt: '2025-01-03T18:20:00Z', }, { @@ -78,7 +78,7 @@ export const mockReviews: DetailReview[] = [ rating: 4, content: '재미있고 흥미로운 시간이었습니다!', nickname: '행복한독자', - image: 'https://example.com/images/user8.jpg', + userImage: 'https://example.com/images/user8.jpg', createdAt: '2025-01-05T09:15:00Z', }, { @@ -88,7 +88,7 @@ export const mockReviews: DetailReview[] = [ rating: 1, content: '추천하고 싶지 않아요.', nickname: '실망한독자', - image: undefined, + userImage: undefined, createdAt: '2025-01-04T11:00:00Z', }, { @@ -98,7 +98,7 @@ export const mockReviews: DetailReview[] = [ rating: 5, content: '정말 즐거운 시간이었습니다!', nickname: '기쁜독자', - image: 'https://example.com/images/user10.jpg', + userImage: 'https://example.com/images/user10.jpg', createdAt: '2025-01-06T10:10:00Z', }, ]; diff --git a/src/features/profile/components/clubs/ProfileWrittenReview.tsx b/src/features/profile/components/clubs/ProfileWrittenReview.tsx index 828f0907..5a10d3b8 100644 --- a/src/features/profile/components/clubs/ProfileWrittenReview.tsx +++ b/src/features/profile/components/clubs/ProfileWrittenReview.tsx @@ -18,14 +18,14 @@ export default function ProfileWrittenReview({ >
diff --git a/src/features/profile/components/info/Info.tsx b/src/features/profile/components/info/Info.tsx index 9681ca11..de4a2677 100644 --- a/src/features/profile/components/info/Info.tsx +++ b/src/features/profile/components/info/Info.tsx @@ -2,19 +2,18 @@ import { useState } from 'react'; import Avatar from '@/components/avatar/Avatar'; -import { useEditInfo } from '@/api/auth/react-query'; import { InfoEditModal } from './index'; import { EditInfoParams, ProfilePageProps } from '../../types'; import IconButton from '@/components/icon-button/IconButton'; import { IcEdit } from '../../../../../public/icons'; +import { useEditInfo } from '../../hooks/useEditInfo'; export default function Info({ user, isMyPage }: ProfilePageProps) { const [isModalOpen, setIsModalOpen] = useState(false); + const { onSubmit } = useEditInfo(); - const { mutate: editInfo } = useEditInfo(); - - const onSubmitEditInfo = (formData: EditInfoParams) => { - editInfo(formData); + const onSubmitEditInfo = async (data: EditInfoParams) => { + await onSubmit(data); setIsModalOpen(false); }; @@ -42,11 +41,11 @@ export default function Info({ user, isMyPage }: ProfilePageProps) { role="content" > {/* 프로필 이미지 */} -
+
{/* 프로필 정보 */} @@ -80,9 +79,11 @@ export default function Info({ user, isMyPage }: ProfilePageProps) { onClose={() => setIsModalOpen(false)} onConfirm={(formData) => onSubmitEditInfo(formData)} infoData={{ - nickname: user?.nickname || '', - description: user?.description || '', - image: user?.image, + image: user?.image || '', + user: { + nickname: user?.nickname || '', + description: user?.description || '', + }, }} /> )} diff --git a/src/features/profile/components/info/InfoEditModal.tsx b/src/features/profile/components/info/InfoEditModal.tsx index a37b06cd..6b69aaf1 100644 --- a/src/features/profile/components/info/InfoEditModal.tsx +++ b/src/features/profile/components/info/InfoEditModal.tsx @@ -10,7 +10,7 @@ import { EditInfoParams } from '../../types'; interface InfoEditModalProps { isOpen: boolean; onClose: () => void; - onConfirm: (updatedData: EditInfoParams) => void; + onConfirm: (formData: EditInfoParams) => void; infoData: EditInfoParams; } @@ -31,7 +31,9 @@ function InfoEditContent({
@@ -63,7 +65,7 @@ function InfoEditContent({ type="text" name="nickname" aria-label="nickname" - value={formData.nickname} + value={formData.user?.nickname} onChange={handleChange} className="w-full rounded-lg bg-gray-light-02 p-2 font-medium" /> @@ -74,7 +76,7 @@ function InfoEditContent({ type="text" name="description" aria-label="description" - value={formData.description} + value={formData.user?.description} onChange={handleChange} className="w-full rounded-lg bg-gray-light-02 p-2 font-medium" /> @@ -93,21 +95,23 @@ export default function InfoEditModal({ }: InfoEditModalProps) { const { user } = useAuthStore(); const [formData, setFormData] = useState({ - nickname: infoData.nickname || user?.name || '', - description: infoData.description || user?.description || '', image: infoData.image || user?.image || '/images/profile.png', + user: { + nickname: infoData.user?.nickname || user?.name || '', + description: infoData.user?.description || user?.description || '', + }, }); const [preview, setPreview] = useState(null); const handleFileChange = (e: React.ChangeEvent) => { - const file = e.target.files ? e.target.files[0] : null; + const file = e.target.files?.[0]; if (file) { const reader = new FileReader(); reader.onloadend = () => { const fileResult = reader.result as string; setPreview(fileResult); - setFormData((prev) => ({ ...prev, image: fileResult })); + setFormData((prev) => ({ ...prev, image: file })); }; reader.readAsDataURL(file); } @@ -115,7 +119,10 @@ export default function InfoEditModal({ const handleChange = (e: React.ChangeEvent) => { const { name, value } = e.target; - setFormData((prev) => ({ ...prev, [name]: value })); + setFormData((prev) => ({ + ...prev, + user: { ...prev.user, [name]: value }, + })); }; const handleConfirm = () => { diff --git a/src/features/profile/container/MyJoinedClubList.tsx b/src/features/profile/container/MyJoinedClubList.tsx index a906eded..6651271e 100644 --- a/src/features/profile/container/MyJoinedClubList.tsx +++ b/src/features/profile/container/MyJoinedClubList.tsx @@ -56,7 +56,6 @@ export default function MyJoinedClubList({ order }: ClubListProps) { const onDelete = async (clubId: number) => { try { const res = await leaveClub(clubId); - console.log(res); if (res) { showToast({ message: '취소된 모임을 삭제하였습니다.', @@ -134,7 +133,7 @@ export default function MyJoinedClubList({ order }: ClubListProps) { { + const { mutateAsync: editInfo, isPending } = useEditInfoMutation(); + + const onSubmit = async (data: EditInfoParams) => { + const formData = toFormData(data); + return await editInfo(formData); + }; + return { onSubmit, isLoading: isPending }; +}; diff --git a/src/features/profile/types/index.ts b/src/features/profile/types/index.ts index e473b60e..bc35e47d 100644 --- a/src/features/profile/types/index.ts +++ b/src/features/profile/types/index.ts @@ -38,7 +38,9 @@ export interface ClubListProps { } export interface EditInfoParams { - nickname?: string; - description?: string; - image?: string | null; + image?: string | File; + user?: { + nickname?: string; + description?: string; + }; } diff --git a/src/features/profile/utils/infoEditFormUtils.tsx b/src/features/profile/utils/infoEditFormUtils.tsx new file mode 100644 index 00000000..93cf606f --- /dev/null +++ b/src/features/profile/utils/infoEditFormUtils.tsx @@ -0,0 +1,23 @@ +import { EditInfoParams } from '../types'; + +export const toFormData = (data: EditInfoParams) => { + const formData = new FormData(); + + const imageFile = data.image instanceof File ? data.image : null; + + if (imageFile) { + formData.append('image', imageFile); + } + const userData = { + nickname: data?.user?.nickname, + description: data?.user?.description, + }; + formData.append( + 'user', + new Blob([JSON.stringify(userData)], { + type: 'application/json', + }), + ); + + return formData; +}; diff --git a/src/mocks/mockDatas.ts b/src/mocks/mockDatas.ts index e7868b83..ed2cb21e 100644 --- a/src/mocks/mockDatas.ts +++ b/src/mocks/mockDatas.ts @@ -253,11 +253,11 @@ export const mockReviews: Review[] = [ rating: 5, content: '이 책클럽은 정말 좋았습니다. 책 선정도 훌륭하고, 참여자들 간의 토론도 활발했어요.', - image: 'https://example.com/images/review1.jpg', + userImage: 'https://example.com/images/review1.jpg', createdAt: '2025-01-10T14:30:00Z', - clubImgUrl: 'https://example.com/images/club1.jpg', + bookClubImageUrl: 'https://example.com/images/club1.jpg', nickname: '진영', - clubName: '문학 사랑 모임', + bookClubTitle: '문학 사랑 모임', bookClubType: 'FREE', }, { @@ -267,11 +267,11 @@ export const mockReviews: Review[] = [ rating: 4, content: '모임 분위기는 좋았지만, 책의 주제가 조금 어려웠어요. 그래도 유익한 시간이었어요.', - image: 'https://example.com/images/review2.jpg', + userImage: 'https://example.com/images/review2.jpg', createdAt: '2025-01-12T16:45:00Z', - clubImgUrl: 'https://example.com/images/club2.jpg', + bookClubImageUrl: 'https://example.com/images/club2.jpg', nickname: '민지', - clubName: '책과 커피 모임', + bookClubTitle: '책과 커피 모임', bookClubType: 'FREE', }, { @@ -282,9 +282,9 @@ export const mockReviews: Review[] = [ content: '책은 좋았지만, 온라인 모임이라 참여자들과의 소통이 부족했던 것 같아요.', createdAt: '2025-01-14T17:00:00Z', - clubImgUrl: 'https://example.com/images/club3.jpg', + bookClubImageUrl: 'https://example.com/images/club3.jpg', nickname: '수연', - clubName: 'SF 소설 모임', + bookClubTitle: 'SF 소설 모임', bookClubType: 'FIXED', }, { @@ -295,9 +295,9 @@ export const mockReviews: Review[] = [ content: '좋은 모임이었지만, 장소가 조금 좁았어요. 그 외엔 정말 유익한 시간이었습니다.', createdAt: '2025-01-15T18:10:00Z', - clubImgUrl: 'https://example.com/images/club4.jpg', + bookClubImageUrl: 'https://example.com/images/club4.jpg', nickname: '지훈', - clubName: '역사 탐방 모임', + bookClubTitle: '역사 탐방 모임', bookClubType: 'FREE', }, { @@ -307,11 +307,11 @@ export const mockReviews: Review[] = [ rating: 5, content: '시집에 대한 다양한 해석을 나누어서 매우 흥미로운 모임이었어요. 다음 모임이 기대됩니다.', - image: 'https://example.com/images/review5.jpg', + userImage: 'https://example.com/images/review5.jpg', createdAt: '2025-01-16T12:00:00Z', - clubImgUrl: 'https://example.com/images/club5.jpg', + bookClubImageUrl: 'https://example.com/images/club5.jpg', nickname: '정민', - clubName: '시집 독서 모임', + bookClubTitle: '시집 독서 모임', bookClubType: 'FIXED', }, { @@ -322,9 +322,9 @@ export const mockReviews: Review[] = [ content: '경제학 토론이 매우 유익했고, 다른 사람들의 의견을 들을 수 있어 좋았습니다.', createdAt: '2025-01-17T13:30:00Z', - clubImgUrl: 'https://example.com/images/club6.jpg', + bookClubImageUrl: 'https://example.com/images/club6.jpg', nickname: '현수', - clubName: '경제학 토론 모임', + bookClubTitle: '경제학 토론 모임', bookClubType: 'FREE', }, { @@ -335,9 +335,9 @@ export const mockReviews: Review[] = [ content: '영화와 책을 비교하는 재미는 있었으나, 일부 영화가 책의 내용을 잘 반영하지 못한 것 같아요.', createdAt: '2025-01-18T19:00:00Z', - clubImgUrl: 'https://example.com/images/club7.jpg', + bookClubImageUrl: 'https://example.com/images/club7.jpg', nickname: '태영', - clubName: '문학과 영화 모임', + bookClubTitle: '문학과 영화 모임', bookClubType: 'FIXED', }, { @@ -347,11 +347,11 @@ export const mockReviews: Review[] = [ rating: 5, content: '여행 사진과 이야기를 나누는 모임은 정말 즐거웠어요. 다른 사람들의 경험을 듣는 것이 너무 흥미로웠습니다.', - image: 'https://example.com/images/review8.jpg', + userImage: 'https://example.com/images/review8.jpg', createdAt: '2025-01-19T20:30:00Z', - clubImgUrl: 'https://example.com/images/club8.jpg', + bookClubImageUrl: 'https://example.com/images/club8.jpg', nickname: '수아', - clubName: '여행 사진 모임', + bookClubTitle: '여행 사진 모임', bookClubType: 'FREE', }, { @@ -362,9 +362,9 @@ export const mockReviews: Review[] = [ content: '디지털 기술에 대한 최신 정보를 나눌 수 있어 좋았고, 많은 토론이 이루어졌습니다.', createdAt: '2025-01-20T21:45:00Z', - clubImgUrl: 'https://example.com/images/club9.jpg', + bookClubImageUrl: 'https://example.com/images/club9.jpg', nickname: '정호', - clubName: '디지털 기술 토론 모임', + bookClubTitle: '디지털 기술 토론 모임', bookClubType: 'FREE', }, { @@ -375,9 +375,9 @@ export const mockReviews: Review[] = [ content: '고전 문학을 다시 한번 되새길 수 있는 좋은 시간이었어요. 다만, 책 선정이 조금 아쉬웠습니다.', createdAt: '2025-01-21T22:30:00Z', - clubImgUrl: 'https://example.com/images/club10.jpg', + bookClubImageUrl: 'https://example.com/images/club10.jpg', nickname: '지은', - clubName: '고전 문학 독서 모임', + bookClubTitle: '고전 문학 독서 모임', bookClubType: 'FREE', }, ]; diff --git a/src/types/review.ts b/src/types/review.ts index 7e116757..d1ac6daf 100644 --- a/src/types/review.ts +++ b/src/types/review.ts @@ -5,20 +5,20 @@ export interface Review { bookClubId: number; rating: number; content: string; - image?: string | undefined; + nickname: string; + userImage?: string | undefined; createdAt: string; - clubImgUrl?: string; - nickname?: string; - clubName: string; + bookClubImageUrl?: string; + bookClubTitle: string; bookClubType: 'FREE' | 'FIXED'; } export interface DetailReview - extends Omit { - nickname: string; + extends Omit { bookClubType?: 'FREE' | 'FIXED'; - clubName?: string; + bookClubTitle?: string; + bookClubImageUrl?: string; } export interface ClubDetailReviewFilters { From 23fb3ee27ee3e242fac10664d4c400faca0d4da8 Mon Sep 17 00:00:00 2001 From: Sungu Kim <108677235+haegu97@users.noreply.github.com> Date: Sun, 5 Jan 2025 17:29:03 +0900 Subject: [PATCH 12/18] =?UTF-8?q?=EB=A9=94=EC=8B=9C=EC=A7=80=20=EC=9E=85?= =?UTF-8?q?=EB=A0=A5=20input=20=ED=95=98=EB=8B=A8=EC=97=90=20=EA=B3=A0?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/chat/[id]/page.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/app/chat/[id]/page.tsx b/src/app/chat/[id]/page.tsx index f3d7d151..940f664f 100644 --- a/src/app/chat/[id]/page.tsx +++ b/src/app/chat/[id]/page.tsx @@ -181,7 +181,7 @@ function ChatRoomPage() { }; return ( -
+
@@ -192,7 +192,7 @@ function ChatRoomPage() { className="bg-gray-light-02" />

채팅

- +
{}} />
-
+
From 0c18142ef0490d810f379c56fe0484b9c171f27f Mon Sep 17 00:00:00 2001 From: Sungu Kim <108677235+haegu97@users.noreply.github.com> Date: Sun, 5 Jan 2025 18:37:15 +0900 Subject: [PATCH 13/18] =?UTF-8?q?=EC=B5=9C=EC=A2=85=20=EB=B0=9C=ED=91=9C?= =?UTF-8?q?=20=EC=A0=84=20=EB=A6=AC=ED=8E=99=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/chat/[id]/page.tsx | 2 +- src/constants/navigation.ts | 2 +- src/features/bookclub/components/BookClubMainPage.tsx | 1 + src/features/bookclub/components/ClubListSection.tsx | 2 +- .../components/chat-bubble/components/OpponentBubble.tsx | 6 +++--- .../components/chat-bubble/components/atoms/atoms.tsx | 5 +++-- src/features/chat-room/types/chatBubbleList.ts | 1 + src/features/chat/components/chat-card/ChatCard.tsx | 4 ++-- .../chat/components/chat-card/types/variantChatCard.ts | 3 ++- src/features/chat/container/BookClubChatContainer.tsx | 4 ++++ src/features/profile/container/JoinedClubList.tsx | 5 ++++- src/features/profile/container/MyJoinedClubList.tsx | 5 ++++- 12 files changed, 27 insertions(+), 13 deletions(-) diff --git a/src/app/chat/[id]/page.tsx b/src/app/chat/[id]/page.tsx index 940f664f..fc884556 100644 --- a/src/app/chat/[id]/page.tsx +++ b/src/app/chat/[id]/page.tsx @@ -192,7 +192,7 @@ function ChatRoomPage() { className="bg-gray-light-02" />

채팅

- +
void; isConsecutive?: boolean; @@ -14,7 +14,7 @@ function OpponentBubble({ content, time, name, - profileImage, + image, isHost, onProfileClick, isConsecutive, @@ -25,7 +25,7 @@ function OpponentBubble({ {!isConsecutive && ( diff --git a/src/features/chat-room/components/chat-bubble/components/atoms/atoms.tsx b/src/features/chat-room/components/chat-bubble/components/atoms/atoms.tsx index f8a3a1b9..557394ac 100644 --- a/src/features/chat-room/components/chat-bubble/components/atoms/atoms.tsx +++ b/src/features/chat-room/components/chat-bubble/components/atoms/atoms.tsx @@ -80,12 +80,13 @@ export function ChatBubbleTime({ interface ChatBubbleProfileProps extends HTMLAttributes { name: string; - imageUrl: string; + image?: string; isHost?: boolean; } export function ChatBubbleProfile({ name, + image, isHost, className, onClick, @@ -98,7 +99,7 @@ export function ChatBubbleProfile({ onClick={onClick} data-testid="profile-image" > - + {isHost && (
diff --git a/src/features/chat-room/types/chatBubbleList.ts b/src/features/chat-room/types/chatBubbleList.ts index e5178116..7710f91c 100644 --- a/src/features/chat-room/types/chatBubbleList.ts +++ b/src/features/chat-room/types/chatBubbleList.ts @@ -9,6 +9,7 @@ export interface ChatMessageType extends BaseMessage { userNickname: string; userId: number; content: string; + image?: string; } export interface SystemMessageType extends BaseMessage { diff --git a/src/features/chat/components/chat-card/ChatCard.tsx b/src/features/chat/components/chat-card/ChatCard.tsx index 4d20f415..804b245b 100644 --- a/src/features/chat/components/chat-card/ChatCard.tsx +++ b/src/features/chat/components/chat-card/ChatCard.tsx @@ -185,7 +185,7 @@ function ChatCard({ imageUrl, isHost, title, - currentParticipants, + memberCount, lastMessage, lastMessageTime, unreadCount, @@ -202,7 +202,7 @@ function ChatCard({
{title} - +
{unreadCount && unreadCount > 0 && ( <> diff --git a/src/features/chat/components/chat-card/types/variantChatCard.ts b/src/features/chat/components/chat-card/types/variantChatCard.ts index f80e7de0..c85f7f1d 100644 --- a/src/features/chat/components/chat-card/types/variantChatCard.ts +++ b/src/features/chat/components/chat-card/types/variantChatCard.ts @@ -8,10 +8,11 @@ interface CommonProps extends React.HTMLAttributes { } export interface BookClubProps extends CommonProps { - currentParticipants: number; + memberCount: number; lastMessage?: string; lastMessageTime?: string; unreadCount?: number; + hostId: number; } export interface ChatRoomHeaderProps extends CommonProps { diff --git a/src/features/chat/container/BookClubChatContainer.tsx b/src/features/chat/container/BookClubChatContainer.tsx index 66e7cb2b..b2d25b73 100644 --- a/src/features/chat/container/BookClubChatContainer.tsx +++ b/src/features/chat/container/BookClubChatContainer.tsx @@ -9,6 +9,7 @@ import Link from 'next/link'; import { getRecentChats, getStompClient } from '@/features/chat/utils/socket'; import { findRecentMessage } from '@/features/chat/utils/chatRoom'; import { formatDateForUI } from '@/lib/utils/formatDateForUI'; +import { useAuthStore } from '@/store/authStore'; export default function BookClubChatContainer() { const [recentMessages, setRecentMessages] = useState< @@ -23,6 +24,8 @@ export default function BookClubChatContainer() { bookClubs.my()._ctx.joined({ order: 'DESC', page: 1, size: 10 }), ); + const { user } = useAuthStore(); + useEffect(() => { const fetchRecentChats = async () => { try { @@ -63,6 +66,7 @@ export default function BookClubChatContainer() { variant="bookClub" props={{ ...bookClub, + isHost: user?.id === bookClub.hostId, lastMessage: recentMessage?.content || '', lastMessageTime: recentMessage?.date ? formatDateForUI(recentMessage.date, 'CHAT_ROOM') diff --git a/src/features/profile/container/JoinedClubList.tsx b/src/features/profile/container/JoinedClubList.tsx index 066e4c28..c06ec317 100644 --- a/src/features/profile/container/JoinedClubList.tsx +++ b/src/features/profile/container/JoinedClubList.tsx @@ -57,7 +57,10 @@ export default function JoinedClubList({ order }: ClubListProps) {
) : ( JoinedList?.filter( - (bookClub) => !bookClub.isInactive && bookClub.hostId !== user.id, + (bookClub) => + !bookClub.isInactive && + bookClub.hostId !== user.id && + bookClub.isJoined === true, )?.map((bookClub) => (
) : ( myJoinedList - ?.filter((bookClub) => bookClub.hostId !== user?.id) + ?.filter( + (bookClub) => + bookClub.hostId !== user?.id && bookClub.isJoined === true, + ) ?.map((bookClub) => (
Date: Sun, 5 Jan 2025 18:41:20 +0900 Subject: [PATCH 14/18] =?UTF-8?q?=EC=B5=9C=EC=A2=85=20=EB=B0=9C=ED=91=9C?= =?UTF-8?q?=20=EC=A0=84=20=EB=A6=AC=ED=8E=99=ED=86=A0=EB=A7=812?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/chat/[id]/page.tsx | 2 +- src/features/chat/container/BookClubChatContainer.tsx | 2 +- src/features/chat/utils/socket.ts | 5 +++-- src/features/club-details/components/ReviewSection.tsx | 2 +- src/features/club-wish/mocks/wishPageMockData.ts | 2 +- src/features/profile/container/CreatedClubList.tsx | 2 +- src/features/profile/container/JoinedClubList.tsx | 2 +- src/features/profile/container/MyCreatedClubList.tsx | 2 +- src/features/profile/container/MyJoinedClubList.tsx | 2 +- src/features/profile/container/MyWrittenReviewList.tsx | 2 +- src/features/profile/container/WrittenReviewList.tsx | 2 +- src/lib/constants/filters.ts | 2 +- 12 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/app/chat/[id]/page.tsx b/src/app/chat/[id]/page.tsx index fc884556..5f1bc9d9 100644 --- a/src/app/chat/[id]/page.tsx +++ b/src/app/chat/[id]/page.tsx @@ -39,7 +39,7 @@ function ChatRoomPage() { const [isConnected, setIsConnected] = useState(false); const { data } = useQuery( - bookClubs.my()._ctx.joined({ order: 'DESC', page: 1, size: 10 }), + bookClubs.my()._ctx.joined({ order: 'DESC', page: 1, size: 100 }), ); const bookClubDetail = data?.bookClubs?.find( diff --git a/src/features/chat/container/BookClubChatContainer.tsx b/src/features/chat/container/BookClubChatContainer.tsx index b2d25b73..c75bb051 100644 --- a/src/features/chat/container/BookClubChatContainer.tsx +++ b/src/features/chat/container/BookClubChatContainer.tsx @@ -21,7 +21,7 @@ export default function BookClubChatContainer() { >([]); const { data, isLoading, error } = useQuery( - bookClubs.my()._ctx.joined({ order: 'DESC', page: 1, size: 10 }), + bookClubs.my()._ctx.joined({ order: 'DESC', page: 1, size: 100 }), ); const { user } = useAuthStore(); diff --git a/src/features/chat/utils/socket.ts b/src/features/chat/utils/socket.ts index de1d5cb4..87ca018b 100644 --- a/src/features/chat/utils/socket.ts +++ b/src/features/chat/utils/socket.ts @@ -13,7 +13,8 @@ export interface ChatMessage { userNickname: string; type: 'CHAT' | 'JOIN' | 'LEAVE'; content: string; - user?: string; + user: string; + image?: string; } export interface HistoryResponse { @@ -35,7 +36,7 @@ export const initializeSocket = async (token: string) => { const response = await apiClient.get('/book-clubs/my-joined', { params: { order: 'DESC', - size: 10, + size: 100, page: 1, }, }); diff --git a/src/features/club-details/components/ReviewSection.tsx b/src/features/club-details/components/ReviewSection.tsx index 6d4dad53..43f8378b 100644 --- a/src/features/club-details/components/ReviewSection.tsx +++ b/src/features/club-details/components/ReviewSection.tsx @@ -10,7 +10,7 @@ function ReviewSection({ idAsNumber }: { idAsNumber: number }) { const [filters, setFilters] = useState({ order: 'DESC', page: 1, - size: 10, + size: 100, }); const { data, isLoading, error } = useQuery({ diff --git a/src/features/club-wish/mocks/wishPageMockData.ts b/src/features/club-wish/mocks/wishPageMockData.ts index 3ce8c944..a586ee0e 100644 --- a/src/features/club-wish/mocks/wishPageMockData.ts +++ b/src/features/club-wish/mocks/wishPageMockData.ts @@ -5,6 +5,6 @@ export const filters: BookClubParams = { meetingType: 'ALL', order: 'DESC', page: 1, - size: 10, + size: 100, searchKeyword: '', }; diff --git a/src/features/profile/container/CreatedClubList.tsx b/src/features/profile/container/CreatedClubList.tsx index 9300ba39..d71db2e1 100644 --- a/src/features/profile/container/CreatedClubList.tsx +++ b/src/features/profile/container/CreatedClubList.tsx @@ -19,7 +19,7 @@ export default function CreatedClubList({ order }: ClubListProps) { const defaultClubImage = '/images/defaultBookClub.jpg'; const { data, isLoading, error } = useQuery( - bookClubs.user(user?.id)._ctx.created({ order, page: 1, size: 10 }), + bookClubs.user(user?.id)._ctx.created({ order, page: 1, size: 100 }), ); const { onConfirmUnLike } = useUnLikeClub(); diff --git a/src/features/profile/container/JoinedClubList.tsx b/src/features/profile/container/JoinedClubList.tsx index c06ec317..6200773c 100644 --- a/src/features/profile/container/JoinedClubList.tsx +++ b/src/features/profile/container/JoinedClubList.tsx @@ -21,7 +21,7 @@ export default function JoinedClubList({ order }: ClubListProps) { const defaultClubImage = '/images/defaultBookClub.jpg'; const { data, isLoading, error } = useQuery( - bookClubs.user(user?.id)._ctx.joined({ order, page: 1, size: 10 }), + bookClubs.user(user?.id)._ctx.joined({ order, page: 1, size: 100 }), ); const { onConfirmUnLike } = useUnLikeClub(); diff --git a/src/features/profile/container/MyCreatedClubList.tsx b/src/features/profile/container/MyCreatedClubList.tsx index b5564797..995e0f81 100644 --- a/src/features/profile/container/MyCreatedClubList.tsx +++ b/src/features/profile/container/MyCreatedClubList.tsx @@ -21,7 +21,7 @@ export default function MyCreatedClubList({ order }: ClubListProps) { useCancelClub(); const { data, isLoading, error } = useQuery( - bookClubs.my()._ctx.created({ order, page: 1, size: 10 }), + bookClubs.my()._ctx.created({ order, page: 1, size: 100 }), ); const myCreatedList: BookClub[] = data?.bookClubs || []; diff --git a/src/features/profile/container/MyJoinedClubList.tsx b/src/features/profile/container/MyJoinedClubList.tsx index 1aa277b6..6176b401 100644 --- a/src/features/profile/container/MyJoinedClubList.tsx +++ b/src/features/profile/container/MyJoinedClubList.tsx @@ -32,7 +32,7 @@ export default function MyJoinedClubList({ order }: ClubListProps) { const today = new Date(); const defaultClubImage = '/images/defaultBookClub.jpg'; const { data, isLoading, error } = useQuery( - bookClubs.my()._ctx.joined({ order, page: 1, size: 10 }), + bookClubs.my()._ctx.joined({ order, page: 1, size: 100 }), ); const { mutateAsync: leaveClub } = useLeaveBookClub(); diff --git a/src/features/profile/container/MyWrittenReviewList.tsx b/src/features/profile/container/MyWrittenReviewList.tsx index b7faf989..80fce149 100644 --- a/src/features/profile/container/MyWrittenReviewList.tsx +++ b/src/features/profile/container/MyWrittenReviewList.tsx @@ -10,7 +10,7 @@ import ProfileWrittenReview from '../components/clubs/ProfileWrittenReview'; export default function MyWrittenReviewList({ order }: ClubListProps) { const { data, isLoading, error } = useQuery( - bookClubs.my()._ctx.reviews({ order, page: 1, size: 10 }), + bookClubs.my()._ctx.reviews({ order, page: 1, size: 100 }), ); const myReviewList: Review[] = data?.reviews || []; diff --git a/src/features/profile/container/WrittenReviewList.tsx b/src/features/profile/container/WrittenReviewList.tsx index 94b80ca8..f1b52c00 100644 --- a/src/features/profile/container/WrittenReviewList.tsx +++ b/src/features/profile/container/WrittenReviewList.tsx @@ -13,7 +13,7 @@ export default function WrittenReviewList({ order }: ClubListProps) { const user = useGetUserByPath(); const { data, isLoading, error } = useQuery( - bookClubs.user(user?.id)._ctx.reviews({ order, page: 1, size: 10 }), + bookClubs.user(user?.id)._ctx.reviews({ order, page: 1, size: 100 }), ); const ReviewList: Review[] = data?.reviews || []; diff --git a/src/lib/constants/filters.ts b/src/lib/constants/filters.ts index 0ee948a3..98505373 100644 --- a/src/lib/constants/filters.ts +++ b/src/lib/constants/filters.ts @@ -5,6 +5,6 @@ export const DEFAULT_FILTERS: BookClubParams = { meetingType: 'ALL', order: 'DESC', page: 1, - size: 10, + size: 100, searchKeyword: '', }; From 2083659bdf535e3233af765bf40b92a9e6542e57 Mon Sep 17 00:00:00 2001 From: Sungu Kim <108677235+haegu97@users.noreply.github.com> Date: Sun, 5 Jan 2025 18:58:08 +0900 Subject: [PATCH 15/18] =?UTF-8?q?console.log=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/chat/[id]/page.tsx | 6 +--- src/features/auth/api/auth.ts | 2 +- src/features/auth/api/refreshAccessToken.ts | 1 - .../bookclub/hooks/useFetchBookClubList.ts | 2 -- .../chat/container/BookClubChatContainer.tsx | 3 +- src/features/chat/container/ChatContainer.tsx | 1 - .../chat/container/ExchangeChatContainer.tsx | 31 ++----------------- src/features/chat/utils/socket.ts | 28 +++-------------- 8 files changed, 11 insertions(+), 63 deletions(-) diff --git a/src/app/chat/[id]/page.tsx b/src/app/chat/[id]/page.tsx index 5f1bc9d9..74df14f6 100644 --- a/src/app/chat/[id]/page.tsx +++ b/src/app/chat/[id]/page.tsx @@ -64,17 +64,13 @@ function ChatRoomPage() { await new Promise((resolve, reject) => { const checkConnection = setInterval(() => { - console.log(`소켓 연결 시도 ${attempts + 1}회`); - if (client?.connected) { - console.log('소켓 연결 성공!'); clearInterval(checkConnection); resolve(true); } attempts++; if (attempts >= maxAttempts) { - console.log('소켓 연결 최대 시도 횟수 초과'); clearInterval(checkConnection); reject(new Error('소켓 연결 타임아웃')); } @@ -197,7 +193,7 @@ function ChatRoomPage() {
} - onClick={() => console.log('메뉴 열기 버튼 클릭')} + onClick={() => {}} className="bg-gray-light-02" />
diff --git a/src/features/auth/api/auth.ts b/src/features/auth/api/auth.ts index 01abd80b..41db7216 100644 --- a/src/features/auth/api/auth.ts +++ b/src/features/auth/api/auth.ts @@ -30,7 +30,7 @@ export const login = async (data: LoginFormData) => { await getUserInfo(); const token = getCookie('auth_token'); - console.log('token', token); + if (token) { initializeSocket(token); } diff --git a/src/features/auth/api/refreshAccessToken.ts b/src/features/auth/api/refreshAccessToken.ts index 453566c9..f5b9c73d 100644 --- a/src/features/auth/api/refreshAccessToken.ts +++ b/src/features/auth/api/refreshAccessToken.ts @@ -15,7 +15,6 @@ export const refreshAccessToken = async (refreshToken: string) => { throw new Error('토큰 갱신 실패'); } - console.log('리프레시 성공'); return response.json(); } catch (error) { console.error('토큰 갱신 에러:', error); diff --git a/src/features/bookclub/hooks/useFetchBookClubList.ts b/src/features/bookclub/hooks/useFetchBookClubList.ts index 8d72f6a1..2b70ca45 100644 --- a/src/features/bookclub/hooks/useFetchBookClubList.ts +++ b/src/features/bookclub/hooks/useFetchBookClubList.ts @@ -13,8 +13,6 @@ const useBookClubList = () => { const clubList = data?.bookClubs; - // console.log('useQuery 데이터:', clubList); - const updateFilters = (newFilters: Partial) => { setFilters((prevFilters) => ({ ...prevFilters, ...newFilters })); }; diff --git a/src/features/chat/container/BookClubChatContainer.tsx b/src/features/chat/container/BookClubChatContainer.tsx index c75bb051..6aa99cdc 100644 --- a/src/features/chat/container/BookClubChatContainer.tsx +++ b/src/features/chat/container/BookClubChatContainer.tsx @@ -30,7 +30,7 @@ export default function BookClubChatContainer() { const fetchRecentChats = async () => { try { const response = await getRecentChats(); - console.log('최근 채팅 내용 조회 성공:', response); + setRecentMessages(response || []); } catch (error) { console.error('최근 채팅 내용 조회 실패:', error); @@ -46,7 +46,6 @@ export default function BookClubChatContainer() { }, []); const bookClubChats = data?.bookClubs || []; - console.log('bookClubChats', bookClubChats); if (isLoading) return
로딩중...
; if (error) return
에러가 발생했습니다
; diff --git a/src/features/chat/container/ChatContainer.tsx b/src/features/chat/container/ChatContainer.tsx index 8400ba97..2de2a76c 100644 --- a/src/features/chat/container/ChatContainer.tsx +++ b/src/features/chat/container/ChatContainer.tsx @@ -17,7 +17,6 @@ export default function ChatContainer() { const connectSocket = async () => { try { await initializeSocket(token); - console.log('채팅 페이지에서 소켓 재연결 성공'); } catch (error) { console.error('채팅 페이지에서 소켓 재연결 실패:', error); } diff --git a/src/features/chat/container/ExchangeChatContainer.tsx b/src/features/chat/container/ExchangeChatContainer.tsx index b330c466..b3661841 100644 --- a/src/features/chat/container/ExchangeChatContainer.tsx +++ b/src/features/chat/container/ExchangeChatContainer.tsx @@ -1,39 +1,14 @@ import React from 'react'; -import ChatCard from '../components/chat-card/ChatCard'; - -interface BookClubChatData { - imageUrl: string; - isHost: boolean; - title: string; - currentParticipants: number; - lastMessage: string; - lastMessageTime: string; - unreadCount: number; -} export default function ExchangeChatContainer() { const message = '아직 참여중인 채팅이 없어요.'; - const mockBookClubChats: BookClubChatData[] = []; return (
- {mockBookClubChats.length === 0 ? ( -
- {message} -
- ) : ( - mockBookClubChats.map((chat, index) => ( - console.log('채팅방 클릭'), - }} - /> - )) - )} +
+ {message} +
); diff --git a/src/features/chat/utils/socket.ts b/src/features/chat/utils/socket.ts index 87ca018b..11420edd 100644 --- a/src/features/chat/utils/socket.ts +++ b/src/features/chat/utils/socket.ts @@ -28,7 +28,6 @@ export interface ChatHistoryResponse { export const initializeSocket = async (token: string) => { if (stompClient?.connected) { - console.log('이미 연결된 소켓이 있습니다.'); return stompClient; } @@ -52,20 +51,14 @@ export const initializeSocket = async (token: string) => { }); client.onConnect = () => { - console.log('소켓 연결 성공'); - bookClubs.forEach((club: BookClub) => { - console.log('구독 시도:', club.id); - client.subscribe(`/topic/group-chat/${club.id}`, (message) => { - const chatMessage: ChatMessage = JSON.parse(message.body); - console.log(`모임 ${club.title}의 새 메시지 수신:`, chatMessage); + client.subscribe(`/topic/group-chat/${club.id}`, () => { + // const chatMessage: ChatMessage = JSON.parse(message.body); }); }); }; - client.onDisconnect = () => { - console.log('소켓 연결 끊김'); - }; + client.onDisconnect = () => {}; client.onStompError = (frame) => { console.error('Stomp 에러:', frame); @@ -102,13 +95,10 @@ export const sendMessage = (roomId: number, content: string) => { return; } - console.log(`메시지 전송 시도: roomId=${roomId}, content=${content}`); - client.publish({ destination: `/app/group-chat/${roomId}/sendMessage`, body: JSON.stringify({ content }), }); - console.log('메시지 전송 성공'); }; export const getChatHistory = (roomId: number) => { @@ -120,7 +110,7 @@ export const getChatHistory = (roomId: number) => { (message) => { try { const historyData: ChatHistoryResponse = JSON.parse(message.body); - console.log('채팅 히스토리 수신:', historyData); + resolve(historyData); subscription.unsubscribe(); } catch (error) { @@ -136,7 +126,6 @@ export const getChatHistory = (roomId: number) => { destination: `/app/group-chat/history/${roomId}`, body: JSON.stringify({}), }); - console.log('채팅 히스토리 요청 전송'); } catch (error) { console.error('채팅 히스토리 요청 실패:', error); subscription.unsubscribe(); @@ -147,17 +136,12 @@ export const getChatHistory = (roomId: number) => { export const getRecentChats = () => { const client = getStompClient(); - console.log('getRecentChats - 클라이언트 상태:', { - connected: client.connected, - active: client.active, - }); return new Promise((resolve, reject) => { const subscription = client.subscribe('/user/queue/recent', (message) => { - console.log('구독 콜백 실행됨'); try { const recentData: ChatMessage[] = JSON.parse(message.body); - console.log('최신 채팅 데이터:', recentData); + resolve(recentData); subscription.unsubscribe(); } catch (error) { @@ -168,12 +152,10 @@ export const getRecentChats = () => { }); try { - console.log('서버로 요청 전송 시도'); client.publish({ destination: '/app/group-chat/recent', body: JSON.stringify({}), }); - console.log('서버로 요청 전송 완료'); } catch (error) { console.error('최신 채팅 요청 실패:', error); subscription.unsubscribe(); From 4efde5b4a70361bb99dd4abcbf1bd7f44fa9c372 Mon Sep 17 00:00:00 2001 From: Sungu Kim <108677235+haegu97@users.noreply.github.com> Date: Sun, 5 Jan 2025 19:06:02 +0900 Subject: [PATCH 16/18] =?UTF-8?q?=EB=A1=9C=EB=94=A9=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=EB=A1=9C=20=EA=B5=90=EC=B2=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat/container/BookClubChatContainer.tsx | 54 ++++++++++--------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/src/features/chat/container/BookClubChatContainer.tsx b/src/features/chat/container/BookClubChatContainer.tsx index 6aa99cdc..e12518f7 100644 --- a/src/features/chat/container/BookClubChatContainer.tsx +++ b/src/features/chat/container/BookClubChatContainer.tsx @@ -10,6 +10,7 @@ import { getRecentChats, getStompClient } from '@/features/chat/utils/socket'; import { findRecentMessage } from '@/features/chat/utils/chatRoom'; import { formatDateForUI } from '@/lib/utils/formatDateForUI'; import { useAuthStore } from '@/store/authStore'; +import Loading from '@/components/loading/Loading'; export default function BookClubChatContainer() { const [recentMessages, setRecentMessages] = useState< @@ -47,35 +48,38 @@ export default function BookClubChatContainer() { const bookClubChats = data?.bookClubs || []; - if (isLoading) return
로딩중...
; if (error) return
에러가 발생했습니다
; return ( -
-
- {bookClubChats.map((bookClub: BookClubProps, id: number) => { - const recentMessage = findRecentMessage( - recentMessages, - Number(bookClub.id), - ); +
+ {isLoading ? ( + + ) : ( +
+ {bookClubChats.map((bookClub: BookClubProps, id: number) => { + const recentMessage = findRecentMessage( + recentMessages, + Number(bookClub.id), + ); - return ( - - - - ); - })} -
+ return ( + + + + ); + })} +
+ )}
); } From 42ac92f7c313a95e9b45b11e43165ba2f1d9c9d1 Mon Sep 17 00:00:00 2001 From: Sungu Kim <108677235+haegu97@users.noreply.github.com> Date: Sun, 5 Jan 2025 19:09:29 +0900 Subject: [PATCH 17/18] =?UTF-8?q?=EB=B9=8C=EB=93=9C=EC=97=90=EB=9F=AC=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat-room/components/chat-bubble/ChatBubble.stories.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/features/chat-room/components/chat-bubble/ChatBubble.stories.tsx b/src/features/chat-room/components/chat-bubble/ChatBubble.stories.tsx index cdfe9b75..dddb7ba5 100644 --- a/src/features/chat-room/components/chat-bubble/ChatBubble.stories.tsx +++ b/src/features/chat-room/components/chat-bubble/ChatBubble.stories.tsx @@ -39,7 +39,7 @@ export const OpponentMessage: Story = { * 늦게 오시는 분은 미리 말씀 부탁드려요!`, time: '오후 2:31', name: '김철수', - profileImage: 'https://picsum.photos/200', + image: 'https://picsum.photos/200', isHost: true, }, }, From 818bf8a7076a94de32434341a56776e65663ab31 Mon Sep 17 00:00:00 2001 From: Sungu Kim <108677235+haegu97@users.noreply.github.com> Date: Sun, 5 Jan 2025 19:20:53 +0900 Subject: [PATCH 18/18] =?UTF-8?q?=EB=B9=8C=EB=93=9C=EC=97=90=EB=9F=AC=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/atoms/atoms.stories.tsx | 2 +- .../chat/components/chat-card/ChatCard.tsx | 2 +- .../chat-card/types/variantChatCard.ts | 4 +- src/mocks/mockDatas.ts | 70 ++++++++++--------- 4 files changed, 40 insertions(+), 38 deletions(-) diff --git a/src/features/chat-room/components/chat-bubble/components/atoms/atoms.stories.tsx b/src/features/chat-room/components/chat-bubble/components/atoms/atoms.stories.tsx index a9bcda96..00fae048 100644 --- a/src/features/chat-room/components/chat-bubble/components/atoms/atoms.stories.tsx +++ b/src/features/chat-room/components/chat-bubble/components/atoms/atoms.stories.tsx @@ -33,7 +33,7 @@ export const Time: StoryObj = { export const Profile: StoryObj = { render: () => ( diff --git a/src/features/chat/components/chat-card/ChatCard.tsx b/src/features/chat/components/chat-card/ChatCard.tsx index 804b245b..ec47f3f9 100644 --- a/src/features/chat/components/chat-card/ChatCard.tsx +++ b/src/features/chat/components/chat-card/ChatCard.tsx @@ -202,7 +202,7 @@ function ChatCard({
{title} - +
{unreadCount && unreadCount > 0 && ( <> diff --git a/src/features/chat/components/chat-card/types/variantChatCard.ts b/src/features/chat/components/chat-card/types/variantChatCard.ts index c85f7f1d..19cebbaf 100644 --- a/src/features/chat/components/chat-card/types/variantChatCard.ts +++ b/src/features/chat/components/chat-card/types/variantChatCard.ts @@ -8,11 +8,11 @@ interface CommonProps extends React.HTMLAttributes { } export interface BookClubProps extends CommonProps { - memberCount: number; + memberCount?: number; lastMessage?: string; lastMessageTime?: string; unreadCount?: number; - hostId: number; + hostId?: number; } export interface ChatRoomHeaderProps extends CommonProps { diff --git a/src/mocks/mockDatas.ts b/src/mocks/mockDatas.ts index ed2cb21e..55224133 100644 --- a/src/mocks/mockDatas.ts +++ b/src/mocks/mockDatas.ts @@ -2,6 +2,8 @@ import { BookClub } from '@/types/bookclubs'; import { Review } from '@/types/review'; import { User } from '@/types/user'; +const defaultBookClub = '/images/defaultBookClub.jpg'; +const defaultProfile = '/images/profile.png'; export const mockBookClubs: BookClub[] = [ { id: 1, @@ -11,7 +13,7 @@ export const mockBookClubs: BookClub[] = [ bookClubType: 'FREE', targetDate: '2025-02-10', endDate: '2025-01-30', - imageUrl: 'https://example.com/images/club1.jpg', + imageUrl: defaultBookClub, memberLimit: 20, memberCount: 15, city: '서울', @@ -20,7 +22,7 @@ export const mockBookClubs: BookClub[] = [ detailAddress: '5층', hostId: 101, hostNickname: '진영', - hostProfileImage: 'https://example.com/images/host1.jpg', + hostProfileImage: defaultProfile, averageScore: 4.5, clubStatus: 'confirmed', isLiked: true, @@ -35,7 +37,7 @@ export const mockBookClubs: BookClub[] = [ bookClubType: 'FREE', targetDate: '2025-03-01', endDate: '2025-02-25', - imageUrl: 'https://example.com/images/club2.jpg', + imageUrl: defaultBookClub, memberLimit: 15, memberCount: 10, city: '부산', @@ -44,7 +46,7 @@ export const mockBookClubs: BookClub[] = [ detailAddress: "커피숍 '북카페'", hostId: 102, hostNickname: '민지', - hostProfileImage: 'https://example.com/images/host2.jpg', + hostProfileImage: defaultProfile, averageScore: 4.8, clubStatus: 'pending', isLiked: false, @@ -59,7 +61,7 @@ export const mockBookClubs: BookClub[] = [ bookClubType: 'FIXED', targetDate: '2025-04-10', endDate: '2025-03-30', - imageUrl: 'https://example.com/images/club3.jpg', + imageUrl: defaultBookClub, memberLimit: 25, memberCount: 18, city: '서울', @@ -68,7 +70,7 @@ export const mockBookClubs: BookClub[] = [ detailAddress: '도서관 3층', hostId: 103, hostNickname: '수연', - hostProfileImage: 'https://example.com/images/host3.jpg', + hostProfileImage: defaultProfile, averageScore: 4.2, clubStatus: 'confirmed', isLiked: true, @@ -83,7 +85,7 @@ export const mockBookClubs: BookClub[] = [ bookClubType: 'FREE', targetDate: '2025-05-15', endDate: '2025-05-01', - imageUrl: 'https://example.com/images/club4.jpg', + imageUrl: defaultBookClub, memberLimit: 30, memberCount: 25, city: '대구', @@ -92,7 +94,7 @@ export const mockBookClubs: BookClub[] = [ detailAddress: '역사 박물관 앞', hostId: 104, hostNickname: '지훈', - hostProfileImage: 'https://example.com/images/host4.jpg', + hostProfileImage: defaultProfile, averageScore: 4.7, clubStatus: 'closed', isLiked: false, @@ -107,7 +109,7 @@ export const mockBookClubs: BookClub[] = [ bookClubType: 'FIXED', targetDate: '2025-06-01', endDate: '2025-05-20', - imageUrl: 'https://example.com/images/club5.jpg', + imageUrl: defaultBookClub, memberLimit: 12, memberCount: 10, city: '서울', @@ -116,7 +118,7 @@ export const mockBookClubs: BookClub[] = [ detailAddress: '온라인 Zoom', hostId: 105, hostNickname: '정민', - hostProfileImage: 'https://example.com/images/host5.jpg', + hostProfileImage: defaultProfile, averageScore: 5.0, clubStatus: 'confirmed', isLiked: true, @@ -131,7 +133,7 @@ export const mockBookClubs: BookClub[] = [ bookClubType: 'FREE', targetDate: '2025-07-10', endDate: '2025-06-30', - imageUrl: 'https://example.com/images/club6.jpg', + imageUrl: defaultBookClub, memberLimit: 20, memberCount: 16, city: '인천', @@ -140,7 +142,7 @@ export const mockBookClubs: BookClub[] = [ detailAddress: '송도 도서관', hostId: 106, hostNickname: '현수', - hostProfileImage: 'https://example.com/images/host6.jpg', + hostProfileImage: defaultProfile, averageScore: 3.8, clubStatus: 'pending', isLiked: false, @@ -155,7 +157,7 @@ export const mockBookClubs: BookClub[] = [ bookClubType: 'FIXED', targetDate: '2025-08-01', endDate: '2025-07-20', - imageUrl: 'https://example.com/images/club7.jpg', + imageUrl: defaultBookClub, memberLimit: 18, memberCount: 14, city: '서울', @@ -164,7 +166,7 @@ export const mockBookClubs: BookClub[] = [ detailAddress: '온라인 Zoom', hostId: 107, hostNickname: '태영', - hostProfileImage: 'https://example.com/images/host7.jpg', + hostProfileImage: defaultProfile, averageScore: 4.1, clubStatus: 'confirmed', isLiked: true, @@ -179,7 +181,7 @@ export const mockBookClubs: BookClub[] = [ bookClubType: 'FREE', targetDate: '2025-09-05', endDate: '2025-08-30', - imageUrl: 'https://example.com/images/club8.jpg', + imageUrl: defaultBookClub, memberLimit: 10, memberCount: 8, city: '서울', @@ -188,7 +190,7 @@ export const mockBookClubs: BookClub[] = [ detailAddress: "홍대 카페 '여행자'", hostId: 108, hostNickname: '수아', - hostProfileImage: 'https://example.com/images/host8.jpg', + hostProfileImage: defaultProfile, averageScore: 3.9, clubStatus: 'pending', isLiked: false, @@ -203,7 +205,7 @@ export const mockBookClubs: BookClub[] = [ bookClubType: 'FREE', targetDate: '2025-10-10', endDate: '2025-09-30', - imageUrl: 'https://example.com/images/club9.jpg', + imageUrl: defaultBookClub, memberLimit: 30, memberCount: 20, city: '대전', @@ -212,7 +214,7 @@ export const mockBookClubs: BookClub[] = [ detailAddress: '온라인 Discord', hostId: 109, hostNickname: '정호', - hostProfileImage: 'https://example.com/images/host9.jpg', + hostProfileImage: defaultProfile, averageScore: 4.6, clubStatus: 'closed', isLiked: true, @@ -227,7 +229,7 @@ export const mockBookClubs: BookClub[] = [ bookClubType: 'FREE', targetDate: '2025-11-01', endDate: '2025-10-25', - imageUrl: 'https://example.com/images/club10.jpg', + imageUrl: defaultBookClub, memberLimit: 25, memberCount: 20, city: '서울', @@ -236,7 +238,7 @@ export const mockBookClubs: BookClub[] = [ detailAddress: '강북 도서관', hostId: 110, hostNickname: '지은', - hostProfileImage: 'https://example.com/images/host10.jpg', + hostProfileImage: defaultProfile, averageScore: 4.3, clubStatus: 'confirmed', isLiked: false, @@ -253,9 +255,9 @@ export const mockReviews: Review[] = [ rating: 5, content: '이 책클럽은 정말 좋았습니다. 책 선정도 훌륭하고, 참여자들 간의 토론도 활발했어요.', - userImage: 'https://example.com/images/review1.jpg', + userImage: defaultProfile, createdAt: '2025-01-10T14:30:00Z', - bookClubImageUrl: 'https://example.com/images/club1.jpg', + bookClubImageUrl: defaultBookClub, nickname: '진영', bookClubTitle: '문학 사랑 모임', bookClubType: 'FREE', @@ -267,9 +269,9 @@ export const mockReviews: Review[] = [ rating: 4, content: '모임 분위기는 좋았지만, 책의 주제가 조금 어려웠어요. 그래도 유익한 시간이었어요.', - userImage: 'https://example.com/images/review2.jpg', + userImage: defaultBookClub, createdAt: '2025-01-12T16:45:00Z', - bookClubImageUrl: 'https://example.com/images/club2.jpg', + bookClubImageUrl: defaultBookClub, nickname: '민지', bookClubTitle: '책과 커피 모임', bookClubType: 'FREE', @@ -282,7 +284,7 @@ export const mockReviews: Review[] = [ content: '책은 좋았지만, 온라인 모임이라 참여자들과의 소통이 부족했던 것 같아요.', createdAt: '2025-01-14T17:00:00Z', - bookClubImageUrl: 'https://example.com/images/club3.jpg', + bookClubImageUrl: defaultBookClub, nickname: '수연', bookClubTitle: 'SF 소설 모임', bookClubType: 'FIXED', @@ -295,7 +297,7 @@ export const mockReviews: Review[] = [ content: '좋은 모임이었지만, 장소가 조금 좁았어요. 그 외엔 정말 유익한 시간이었습니다.', createdAt: '2025-01-15T18:10:00Z', - bookClubImageUrl: 'https://example.com/images/club4.jpg', + bookClubImageUrl: defaultBookClub, nickname: '지훈', bookClubTitle: '역사 탐방 모임', bookClubType: 'FREE', @@ -307,9 +309,9 @@ export const mockReviews: Review[] = [ rating: 5, content: '시집에 대한 다양한 해석을 나누어서 매우 흥미로운 모임이었어요. 다음 모임이 기대됩니다.', - userImage: 'https://example.com/images/review5.jpg', + userImage: defaultBookClub, createdAt: '2025-01-16T12:00:00Z', - bookClubImageUrl: 'https://example.com/images/club5.jpg', + bookClubImageUrl: defaultBookClub, nickname: '정민', bookClubTitle: '시집 독서 모임', bookClubType: 'FIXED', @@ -322,7 +324,7 @@ export const mockReviews: Review[] = [ content: '경제학 토론이 매우 유익했고, 다른 사람들의 의견을 들을 수 있어 좋았습니다.', createdAt: '2025-01-17T13:30:00Z', - bookClubImageUrl: 'https://example.com/images/club6.jpg', + bookClubImageUrl: defaultBookClub, nickname: '현수', bookClubTitle: '경제학 토론 모임', bookClubType: 'FREE', @@ -335,7 +337,7 @@ export const mockReviews: Review[] = [ content: '영화와 책을 비교하는 재미는 있었으나, 일부 영화가 책의 내용을 잘 반영하지 못한 것 같아요.', createdAt: '2025-01-18T19:00:00Z', - bookClubImageUrl: 'https://example.com/images/club7.jpg', + bookClubImageUrl: defaultBookClub, nickname: '태영', bookClubTitle: '문학과 영화 모임', bookClubType: 'FIXED', @@ -347,9 +349,9 @@ export const mockReviews: Review[] = [ rating: 5, content: '여행 사진과 이야기를 나누는 모임은 정말 즐거웠어요. 다른 사람들의 경험을 듣는 것이 너무 흥미로웠습니다.', - userImage: 'https://example.com/images/review8.jpg', + userImage: defaultBookClub, createdAt: '2025-01-19T20:30:00Z', - bookClubImageUrl: 'https://example.com/images/club8.jpg', + bookClubImageUrl: defaultBookClub, nickname: '수아', bookClubTitle: '여행 사진 모임', bookClubType: 'FREE', @@ -362,7 +364,7 @@ export const mockReviews: Review[] = [ content: '디지털 기술에 대한 최신 정보를 나눌 수 있어 좋았고, 많은 토론이 이루어졌습니다.', createdAt: '2025-01-20T21:45:00Z', - bookClubImageUrl: 'https://example.com/images/club9.jpg', + bookClubImageUrl: defaultBookClub, nickname: '정호', bookClubTitle: '디지털 기술 토론 모임', bookClubType: 'FREE', @@ -375,7 +377,7 @@ export const mockReviews: Review[] = [ content: '고전 문학을 다시 한번 되새길 수 있는 좋은 시간이었어요. 다만, 책 선정이 조금 아쉬웠습니다.', createdAt: '2025-01-21T22:30:00Z', - bookClubImageUrl: 'https://example.com/images/club10.jpg', + bookClubImageUrl: defaultBookClub, nickname: '지은', bookClubTitle: '고전 문학 독서 모임', bookClubType: 'FREE',