diff --git a/src/apis/epigramComment.ts b/src/apis/epigramComment.ts index 4368fb9a..22de9af7 100644 --- a/src/apis/epigramComment.ts +++ b/src/apis/epigramComment.ts @@ -30,6 +30,14 @@ export const getEpigramComments = async (params: CommentRequestType): Promise => { + const { id, ...restParams } = params; + const response = await httpClient.get(`/users/${id}/comments`, { + params: restParams, + }); + return response.data; +}; + export const postComment = async (commentData: PostCommentRequest) => { const response = await httpClient.post('/comments', commentData); return response.data; diff --git a/src/apis/queries.ts b/src/apis/queries.ts index ede08705..4bcb6463 100644 --- a/src/apis/queries.ts +++ b/src/apis/queries.ts @@ -4,9 +4,9 @@ import { EpigramRequestType } from '@/schema/epigram'; import { CommentRequestType } from '@/schema/comment'; import { GetMonthlyEmotionLogsRequestType } from '@/schema/emotion'; import { GetEpigramsParamsType } from '@/schema/epigrams'; -import { getMe, getUser } from './user'; +import { getMe, getUser, getMyContentCount } from './user'; import { getEpigram } from './epigram'; -import { getEpigramComments } from './epigramComment'; +import { getEpigramComments, getMyEpigramComments } from './epigramComment'; import getMonthlyEmotionLogs from './emotion'; import getEpigrams from './getEpigrams'; @@ -20,6 +20,10 @@ const queries = createQueryKeyStore({ queryKey: [request], queryFn: () => getUser(request), }), + getMyContentCount: (request: GetUserRequestType) => ({ + queryKey: ['getMyContentCount', request], + queryFn: () => getMyContentCount(request), + }), }, // NOTE: Epigram 관련 query함수 epigram: { @@ -39,6 +43,10 @@ const queries = createQueryKeyStore({ queryKey: ['epigramComments', request], queryFn: () => getEpigramComments(request), }), + getMyComments: (request: CommentRequestType) => ({ + queryKey: ['myEpigramComments', request], + queryFn: () => getMyEpigramComments(request), + }), }, emotion: { getMonthlyEmotionLogs: (request: GetMonthlyEmotionLogsRequestType) => ({ diff --git a/src/apis/user.ts b/src/apis/user.ts index 5f924dea..17b8ce80 100644 --- a/src/apis/user.ts +++ b/src/apis/user.ts @@ -1,4 +1,4 @@ -import type { GetUserResponseType, GetUserRequestType, PatchMeRequestType, PostPresignedUrlRequestType, PostPresignedUrlResponseType } from '@/schema/user'; +import type { GetUserResponseType, GetUserRequestType, PatchMeRequestType, PostPresignedUrlRequestType, PostPresignedUrlResponseType, GetMyContentCountType } from '@/schema/user'; import httpClient from '.'; export const getMe = async (): Promise => { @@ -23,3 +23,17 @@ export const createPresignedUrl = async (request: PostPresignedUrlRequestType): const response = await httpClient.post('/images/upload', formData); return response.data; }; + +export const getMyContentCount = async (request: GetUserRequestType): Promise => { + const { id } = request; + + // 에피그램 카운트 + const epigram = await httpClient.get(`/epigrams`, { params: { limit: 1, cursor: 0, writerId: id } }); + + // 댓글 카운트 + const comment = await httpClient.get(`/users/${id}/comments`, { params: { limit: 1, cursor: 0 } }); + + const response = { epigramCount: epigram.data.totalCount, commentCount: comment.data.totalCount }; + + return response; +}; diff --git a/src/hooks/useCommentsHook.ts b/src/hooks/useCommentsHook.ts new file mode 100644 index 00000000..9061f77b --- /dev/null +++ b/src/hooks/useCommentsHook.ts @@ -0,0 +1,7 @@ +import quries from '@/apis/queries'; +import { CommentRequestType } from '@/schema/comment'; +import { useQuery } from '@tanstack/react-query'; + +const useCommentsHook = (requset: CommentRequestType) => useQuery(quries.epigramComment.getMyComments(requset)); + +export default useCommentsHook; diff --git a/src/hooks/useGetMyContentHook.ts b/src/hooks/useGetMyContentHook.ts new file mode 100644 index 00000000..65b5f5f7 --- /dev/null +++ b/src/hooks/useGetMyContentHook.ts @@ -0,0 +1,7 @@ +import quries from '@/apis/queries'; +import { GetUserRequestType } from '@/schema/user'; +import { useQuery } from '@tanstack/react-query'; + +const useGetMyContentHook = (requset: GetUserRequestType) => useQuery(quries.user.getMyContentCount(requset)); + +export default useGetMyContentHook; diff --git a/src/pageLayout/MypageLayout/MyContent.tsx b/src/pageLayout/MypageLayout/MyContent.tsx index 81c91dda..036e4dbb 100644 --- a/src/pageLayout/MypageLayout/MyContent.tsx +++ b/src/pageLayout/MypageLayout/MyContent.tsx @@ -4,51 +4,143 @@ import MyEpigrams from '@/user/ui-content/MyEpigrams'; import Image from 'next/image'; import { useToast } from '@/components/ui/use-toast'; import { EpigramsResponse } from '@/types/epigram.types'; +import { CommentResponseType } from '@/schema/comment'; +import useCommentsHook from '@/hooks/useCommentsHook'; +import useGetMyContentHook from '@/hooks/useGetMyContentHook'; +import MyComment from '@/user/ui-content/MyComment'; +import UserInfo from '@/types/user'; +import useDeleteCommentMutation from '@/hooks/useDeleteCommentHook'; import spinner from '../../../public/spinner.svg'; interface MyContentProps { - userId: number; + user: UserInfo; } -export default function MyContent({ userId }: MyContentProps) { +export default function MyContent({ user }: MyContentProps) { const limit = 3; - const [cursor, setCursor] = useState(0); - const [epigrams, setEpigrams] = useState({ totalCount: 0, nextCursor: null, list: [] }); const [isLoadingMore, setIsLoadingMore] = useState(false); + const [selectedTab, setSelectedTab] = useState<'epigrams' | 'comments'>('epigrams'); const { toast } = useToast(); + /** ************ 내 에피그램/댓글 카운트 조회 ************* */ + const [epigramCount, setEpigramCount] = useState(0); + const [commentCount, setCommentCount] = useState(0); + const { data: count } = useGetMyContentHook({ id: user.id }); + useEffect(() => { + if (count) { + setEpigramCount(count.epigramCount); + setCommentCount(count.commentCount); + } + }, [count]); + + /** ************ 내 에피그램 조회 ************* */ + const [epigramCursor, setEpigramCursor] = useState(0); + const [epigrams, setEpigrams] = useState({ totalCount: 0, nextCursor: null, list: [] }); const epigramsRequest = { limit, - cursor, - writerId: userId, + cursor: epigramCursor, + writerId: user.id, + }; + const { data: epigramsData, isLoading: isEpigramsLoading, error: epigramsError } = useGetEpigrams(epigramsRequest); + + /** ************ 내 댓글 조회 ************* */ + const [commentCursor, setCommentCursor] = useState(0); + const [comments, setComments] = useState({ totalCount: 0, nextCursor: null, list: [] }); + const commentsRequest = { + limit, + cursor: commentCursor, + id: user.id, }; - const { data, isLoading, error } = useGetEpigrams(epigramsRequest); + const { data: commentData, isLoading: isCommentsLoading, error: commentsError, refetch: refetchComments } = useCommentsHook(commentsRequest); + // [내 에피그램] 탭 선택 시 useEffect(() => { - if (data && data.list.length > 0) { + if (selectedTab === 'epigrams' && epigramsData) { setEpigrams((prev) => ({ - totalCount: data.totalCount, - nextCursor: data.nextCursor, - list: [...prev.list, ...data.list], + totalCount: epigramsData.totalCount, + nextCursor: epigramsData.nextCursor, + list: [...prev.list, ...epigramsData.list], + })); + setIsLoadingMore(false); + } + }, [epigramsData, selectedTab]); + + // [내 댓글] 탭 선택 시 + useEffect(() => { + if (selectedTab === 'comments' && commentData) { + setComments((prev) => ({ + totalCount: commentData.totalCount, + nextCursor: commentData.nextCursor, + list: [...prev.list, ...commentData.list], })); setIsLoadingMore(false); } - }, [data]); + }, [commentData, selectedTab]); - const handleMoreEpigramLoad = () => { - if (epigrams.nextCursor !== null) { - setCursor(epigrams.nextCursor); + // 더보기 버튼 클릭 시 + const handleMoreLoad = () => { + if (selectedTab === 'epigrams' && epigrams.nextCursor) { + setEpigramCursor(epigrams.nextCursor); + setIsLoadingMore(true); + } else if (selectedTab === 'comments' && comments.nextCursor) { + setCommentCursor(comments.nextCursor); setIsLoadingMore(true); } }; - if (isLoading && !isLoadingMore) { + // [내 에피그램] [내 댓글] 탭 선택 + const handleTabClick = (tab: 'epigrams' | 'comments') => { + setSelectedTab(tab); + // 데이터 초기화 + if (tab === 'epigrams') { + setEpigrams({ totalCount: 0, nextCursor: null, list: [] }); + setEpigramCursor(0); + } else { + setComments({ totalCount: 0, nextCursor: null, list: [] }); + setCommentCursor(0); + } + setIsLoadingMore(false); + }; + + // 댓글 삭제 + const deleteCommentMutation = useDeleteCommentMutation(); + const handleDeleteComment = async (commentId: number) => { + try { + await deleteCommentMutation.mutateAsync(commentId); + setComments((prev) => ({ + totalCount: prev.totalCount - 1, + nextCursor: prev.nextCursor, + list: prev.list.filter((comment) => comment.id !== commentId), + })); + setCommentCount((prev) => prev - 1); + toast({ + title: '댓글이 삭제되었습니다.', + variant: 'destructive', + }); + } catch (error) { + toast({ + title: '댓글 삭제 실패했습니다.', + variant: 'destructive', + }); + } + }; + + // 댓글 수정 + const handleEditComment = () => { + setComments({ totalCount: 0, nextCursor: null, list: [] }); + setCommentCursor(0); + refetchComments(); + }; + + // 로딩 중 + if ((isEpigramsLoading || isCommentsLoading) && !isLoadingMore) { return 로딩중; } - if (error) { + // 에러 + if (epigramsError || commentsError) { toast({ - description: error.message, + description: epigramsError?.message || commentsError?.message, className: 'border-state-error text-state-error font-semibold', }); } @@ -56,16 +148,29 @@ export default function MyContent({ userId }: MyContentProps) { return (
- -
- + {selectedTab === 'epigrams' && } + {selectedTab === 'comments' && ( + + )} {isLoadingMore && (
로딩중 diff --git a/src/pageLayout/MypageLayout/MyPageLayout.tsx b/src/pageLayout/MypageLayout/MyPageLayout.tsx index fd1e8763..3ae052d7 100644 --- a/src/pageLayout/MypageLayout/MyPageLayout.tsx +++ b/src/pageLayout/MypageLayout/MyPageLayout.tsx @@ -35,7 +35,7 @@ export default function MyPageLayout() {
- +
); diff --git a/src/schema/user.ts b/src/schema/user.ts index 6cc1cf00..45565a5e 100644 --- a/src/schema/user.ts +++ b/src/schema/user.ts @@ -30,9 +30,16 @@ export const PostPresignedUrlResponse = z.object({ url: z.string().url(), }); +export const GetMyContentCount = z.object({ + epigramCount: z.number(), + commentCount: z.number(), +}); + export type GetUserResponseType = z.infer; export type GetUserRequestType = z.infer; export type PatchMeRequestType = z.infer; export type PostPresignedUrlRequestType = z.infer; export type PostPresignedUrlResponseType = z.infer; + +export type GetMyContentCountType = z.infer; diff --git a/src/user/ui-content/MyComment.tsx b/src/user/ui-content/MyComment.tsx new file mode 100644 index 00000000..9f015575 --- /dev/null +++ b/src/user/ui-content/MyComment.tsx @@ -0,0 +1,154 @@ +import React, { useState } from 'react'; +import Image from 'next/image'; +import { useRouter } from 'next/router'; +import { textSizeStyles, gapStyles, paddingStyles, contentWidthStyles } from '@/styles/CommentCardStyles'; +import { CommentType } from '@/schema/comment'; +import { Button } from '@/components/ui/button'; +import DeleteAlertModal from '@/components/epigram/DeleteAlertModal'; +import CommentTextarea from '@/components/epigram/Comment/CommentTextarea'; +import NONE_EPI from '../../../public/none-epi.svg'; + +const sizeStyles = { + sm: 'w-[360px]', + md: 'md:w-[384px]', + lg: 'lg:w-[640px]', +}; + +interface MyCommentProps { + comments: CommentType[]; + totalCount: number; + onMoreEpigramLoad: () => void; + onDeleteComment: (commentId: number) => void; + onEditComment: () => void; +} + +function MyComment({ comments, totalCount, onMoreEpigramLoad, onDeleteComment, onEditComment }: MyCommentProps) { + const router = useRouter(); + const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false); + const [selectedCommentId, setSelectedCommentId] = useState(null); + + // NOTE: 현재 수정 중인 댓글의 ID 상태 + const [editingCommentId, setEditingCommentId] = useState(null); + + const handleMoveToMain = () => { + router.push('/epigrams'); + }; + + const handleDeleteComment = () => { + if (selectedCommentId !== null) { + onDeleteComment(selectedCommentId); + setIsDeleteModalOpen(false); + } + }; + + const handleEditComment = (comment: CommentType) => { + setEditingCommentId(comment.id); + }; + + const handleEditComplete = () => { + setEditingCommentId(null); + onEditComment(); + }; + + // 에피그램 상세 페이지 이동 + const handleCommentClick = (epigramId: number) => { + router.push(`/epigrams/${epigramId}`); + }; + + const handleKeyDown = (event: React.KeyboardEvent, epigramId: number) => { + if (event.key === 'Enter' || event.key === ' ') { + handleCommentClick(epigramId); + } + }; + + return totalCount > 0 ? ( +
+ {comments.map((comment) => { + const formattedDate = new Date(comment.createdAt).toLocaleString(); + + return ( +
+
+
+
+ 프로필 이미지 +
+
+ {editingCommentId === comment.id ? ( +
+ +
+ ) : ( +
+
+
+
+ {comment.writer.nickname} +
+
{formattedDate}
+
+
+ + +
+
+
handleCommentClick(comment.epigramId)} + onKeyDown={(event) => handleKeyDown(event, comment.epigramId)} + role='button' + tabIndex={0} + className={`w-full text-zinc-800 font-normal font-pretendard ${textSizeStyles.sm.content} ${textSizeStyles.md.content} ${textSizeStyles.lg.content} ${contentWidthStyles.sm} ${contentWidthStyles.md} ${contentWidthStyles.lg}`} + > + {comment.content} +
+
+ )} +
+
+ ); + })} + {totalCount > comments.length && ( +
+ +
+ )} + +
+ ) : ( +
+ 돋보기아이콘 +
+
+

아직 작성한 댓글이 없어요!

+

댓글을 달고 다른 사람들과 교류해보세요.

+
+ +
+
+ ); +} + +export default MyComment; diff --git a/src/user/ui-content/MyEpigrams.tsx b/src/user/ui-content/MyEpigrams.tsx index f5a7d45e..6aad1ea8 100644 --- a/src/user/ui-content/MyEpigrams.tsx +++ b/src/user/ui-content/MyEpigrams.tsx @@ -37,7 +37,7 @@ function MyEpigrams({ epigrams, totalCount, onMoreEpigramLoad }: MyEpigramProps) // 에피그램 상세 페이지 이동 const handleEpigramClick = (epigramId: number) => { - router.push(`/epigram/${epigramId}`); + router.push(`/epigrams/${epigramId}`); }; const handleKeyDown = (event: React.KeyboardEvent, epigramId: number) => { diff --git a/src/user/ui-profile/Calendar.tsx b/src/user/ui-profile/Calendar.tsx deleted file mode 100644 index 4ce6d06a..00000000 --- a/src/user/ui-profile/Calendar.tsx +++ /dev/null @@ -1,96 +0,0 @@ -import React, { useState } from 'react'; -import Image from 'next/image'; -import { subMonths } from 'date-fns'; -import { EmotionLog, EmotionTypeEN } from '@/types/emotion'; -import useCalendar from '../../hooks/useCalendar'; -import { DAY_LIST, DATE_MONTH_FIXER, iconPaths } from '../utill/constants'; -import CalendarHeader from './CalendarHeader'; - -interface CalendarProps { - currentDate: Date; // 현재 날짜 - setCurrentDate: React.Dispatch>; // 현재 날짜를 설정하는 함수 - monthlyEmotionLogs: EmotionLog[]; -} - -export default function Calendar({ currentDate, setCurrentDate, monthlyEmotionLogs }: CalendarProps) { - // 캘린더 함수 호출 - const { weekCalendarList } = useCalendar(currentDate); - // 감정 필터 - const [selectedEmotion, setSelectedEmotion] = useState(null); - - // 달력에 출력할 수 있게 매핑 - const emotionMap: Record = Array.isArray(monthlyEmotionLogs) - ? monthlyEmotionLogs.reduce>((acc, log) => { - const date = new Date(log.createdAt); - const dateString = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`; - acc[dateString] = log.emotion as EmotionTypeEN; - return acc; - }, {}) - : {}; - - // 이전 달 클릭 - const handlePrevMonth = () => setCurrentDate((prevDate) => subMonths(prevDate, DATE_MONTH_FIXER)); - // 다음 달 클릭 - const handleNextMonth = () => setCurrentDate((prevDate) => subMonths(prevDate, -DATE_MONTH_FIXER)); - - // 감정 필터 - const handleEmotionSelect = (emotion: EmotionTypeEN) => { - // 현재 선택된 감정과 같으면 초기화 - if (selectedEmotion === emotion) { - setSelectedEmotion(null); - } else { - setSelectedEmotion(emotion); - } - }; - - // 필터링된 감정 맵 생성 - const filteredEmotionMap = selectedEmotion ? Object.fromEntries(Object.entries(emotionMap).filter(([, value]) => value === selectedEmotion)) : emotionMap; - - return ( -
- {/* 캘린더 헤더 */} - - {/* 캘린더 */} -
-
- {DAY_LIST.map((day) => ( -
- {day} -
- ))} -
- {weekCalendarList.map((week, weekIndex) => ( - // TODO: index 값 Lint error. 임시로 주석 사용. 추후 수정 예정 - // eslint-disable-next-line react/no-array-index-key -
- {week.map((day, dayIndex) => { - // 현재 날짜와 비교 - const isToday = day === currentDate.getDate() && currentDate.getMonth() === new Date().getMonth() && currentDate.getFullYear() === new Date().getFullYear(); - const dateString = `${currentDate.getFullYear()}-${String(currentDate.getMonth() + 1).padStart(2, '0')}-${String(day).padStart(2, '0')}`; - const emotion: EmotionTypeEN = filteredEmotionMap[dateString]; // 날짜에 해당하는 감정 가져오기 - const iconPath = emotion && iconPaths[emotion] ? iconPaths[emotion].path : '/icon/BW/SmileFaceBWIcon.svg'; - - return ( -
- {emotion ? ( -
-

{day}

- 감정 -
- ) : ( -

{day}

- )} -
- ); - })} -
- ))} -
-
- ); -} diff --git a/src/user/ui-profile/CalendarHeader.tsx b/src/user/ui-profile/CalendarHeader.tsx deleted file mode 100644 index 2c337e1c..00000000 --- a/src/user/ui-profile/CalendarHeader.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import { DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, DropdownMenuGroup, DropdownMenu } from '@/components/ui/dropdown-menu'; -import { Button } from '@/components/ui/button'; -import Image from 'next/image'; -import { EmotionTypeEN } from '@/types/emotion'; -import ARROW_BOTTOM_ICON from '../../../public/icon/arrow-bottom-icon.svg'; -import ARROW_RIGHT_ICON from '../../../public/icon/arrow-right-icon.svg'; -import ARROW_LEFT_ICON from '../../../public/icon/arrow-left-icon.svg'; -import { iconPaths } from '../utill/constants'; - -interface CalendarHeaderProps { - currentDate: Date; - onPrevMonth: () => void; - onNextMonth: () => void; - onEmotionSelect: (emotion: EmotionTypeEN) => void; - selectEmotion: EmotionTypeEN | null; -} - -export default function CalendarHeader({ currentDate, onPrevMonth, onNextMonth, onEmotionSelect, selectEmotion }: CalendarHeaderProps) { - return ( -
-
-
{`${currentDate.getFullYear()}년 ${currentDate.getMonth() + 1}월`}
- - - - - - - {Object.entries(iconPaths).map(([emotionKey, { path, name }]) => ( - - - - ))} - - - -
-
- - -
-
- ); -} diff --git a/src/user/ui-profile/Chart.tsx b/src/user/ui-profile/Chart.tsx deleted file mode 100644 index 6e89af4c..00000000 --- a/src/user/ui-profile/Chart.tsx +++ /dev/null @@ -1,96 +0,0 @@ -import { EmotionLog, EmotionTypeEN } from '@/types/emotion'; -import Image from 'next/image'; -import { iconPaths } from '../utill/constants'; - -interface ChartProps { - monthlyEmotionLogs: EmotionLog[]; -} - -export default function Chart({ monthlyEmotionLogs }: ChartProps) { - // 감정별 빈도수 계산 - const emotionCounts = monthlyEmotionLogs.reduce( - (count, log) => { - const { emotion } = log; - return { - ...count, // 기존의 count를 복사 - [emotion]: (count[emotion] || 0) + 1, // 현재 감정의 개수 증가 - }; - }, - {} as Record, - ); - - // 감정 종류 및 총 감정 수 계산 - const TOTAL_COUNT = monthlyEmotionLogs.length; - const EMOTIONS: EmotionTypeEN[] = ['MOVED', 'HAPPY', 'WORRIED', 'SAD', 'ANGRY']; - const RADIUS = 90; // 원의 반지름 - const CIRCUMFERENCE = 2 * Math.PI * RADIUS; - - // 가장 많이 나타나는 감정 찾기 - const maxEmotion = EMOTIONS.reduce((max, emotion) => (emotionCounts[emotion] > emotionCounts[max] ? emotion : max), EMOTIONS[0]); - - // 원형 차트의 각 감정에 대한 strokeDasharray와 strokeDashoffset 계산 - let offset = 0; - - return ( -
-

감정 차트

-
-
- - - {EMOTIONS.map((emotion) => { - const count = emotionCounts[emotion] || 0; - const percentage = TOTAL_COUNT > 0 ? count / TOTAL_COUNT : 0; // 0으로 나누기 방지 - const strokeDasharray = `${CIRCUMFERENCE * percentage} ${CIRCUMFERENCE * (1 - percentage)}`; - - // 색상 설정 - let strokeColor; - switch (emotion) { - case 'HAPPY': - strokeColor = '#FBC85B'; - break; - case 'SAD': - strokeColor = '#E3E9F1'; - break; - case 'WORRIED': - strokeColor = '#C7D1E0'; - break; - case 'ANGRY': - strokeColor = '#EFF3F8'; - break; - default: - strokeColor = '#48BB98'; - } - - const circle = ; - - offset += CIRCUMFERENCE * percentage; // 다음 원을 위한 offset 업데이트 - return circle; - })} - - {/* 중앙에 가장 많이 나타나는 감정 출력 */} -
- 감정 -

{iconPaths[maxEmotion].name}

-
-
-
-
- {EMOTIONS.map((emotion) => { - const count = emotionCounts[emotion] || 0; - const percentage = TOTAL_COUNT > 0 ? Math.floor((count / TOTAL_COUNT) * 100) : 0; // 퍼센트 계산 및 소수점 버리기 - - return ( -
-

- 감정 -

{percentage}%

-
- ); - })} -
-
-
-
- ); -} diff --git a/src/user/ui-profile/EmotionMonthlyLogs.tsx b/src/user/ui-profile/EmotionMonthlyLogs.tsx deleted file mode 100644 index 18d4ada8..00000000 --- a/src/user/ui-profile/EmotionMonthlyLogs.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import { useMonthlyEmotionLogs } from '@/hooks/useGetEmotion'; -import { Emotion } from '@/types/emotion'; -import { useEffect, useState } from 'react'; -import Calendar from './Calendar'; -import Chart from './Chart'; - -interface EmotionMonthlyLogsProps { - userId: number; -} - -export default function EmotionMonthlyLogs({ userId }: EmotionMonthlyLogsProps) { - // 현재 날짜를 상태로 관리 - const [currentDate, setCurrentDate] = useState(new Date()); - - // 감정 달력 객체 상태 추가 - const [emotionRequest, setEmotionRequest] = useState({ - userId, - year: currentDate.getFullYear(), - month: currentDate.getMonth() + 1, - }); - - // '월'이 변경될 때마다 request 업데이트 - useEffect(() => { - setEmotionRequest({ - userId, - year: currentDate.getFullYear(), - month: currentDate.getMonth() + 1, - }); - }, [currentDate]); - - // 월별 감정 로그 조회 - const { data: monthlyEmotionLogs = [] } = useMonthlyEmotionLogs(emotionRequest); - - return ( - <> - - - - ); -}