-
Notifications
You must be signed in to change notification settings - Fork 2
Fix/160 리팩토링 #161
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix/160 리팩토링 #161
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,82 @@ | ||||||||||||||||||||||
| 'use client'; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| import { useState, useCallback } from 'react'; | ||||||||||||||||||||||
| import { useQuery } from '@tanstack/react-query'; | ||||||||||||||||||||||
| import { AxiosResponse } from 'axios'; | ||||||||||||||||||||||
| import useUserStore from '@/stores/authStore'; | ||||||||||||||||||||||
| import BookingInterface from '@/components/FloatingBox/BookingInterface'; | ||||||||||||||||||||||
| import { privateInstance } from '@/apis/privateInstance'; | ||||||||||||||||||||||
| import { GroupedSchedule } from '@/types/activityDetailType'; | ||||||||||||||||||||||
| import { padMonth } from '../utils/MonthFormatChange'; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| export default function BookingSection({ | ||||||||||||||||||||||
| activityId, | ||||||||||||||||||||||
| userId, | ||||||||||||||||||||||
| price, | ||||||||||||||||||||||
| }: { | ||||||||||||||||||||||
| activityId: string; | ||||||||||||||||||||||
| userId: number; | ||||||||||||||||||||||
| price: number; | ||||||||||||||||||||||
| }) { | ||||||||||||||||||||||
| const [year, setYear] = useState(new Date().getFullYear()); | ||||||||||||||||||||||
| const [month, setMonth] = useState(new Date().getMonth() + 1); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| const currentUserId = useUserStore((state) => | ||||||||||||||||||||||
| state.user ? state.user.id : null, | ||||||||||||||||||||||
| ); | ||||||||||||||||||||||
| const isOwner = currentUserId != null && userId != null && currentUserId === userId; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| const { data: schedulesData } = useQuery({ | ||||||||||||||||||||||
| queryKey: ['available-schedule', activityId, year, month], | ||||||||||||||||||||||
| queryFn: async () => { | ||||||||||||||||||||||
| const prevMonth = month === 1 ? 12 : month - 1; | ||||||||||||||||||||||
| const prevYear = month === 1 ? year - 1 : year; | ||||||||||||||||||||||
| const nextMonth = month === 12 ? 1 : month + 1; | ||||||||||||||||||||||
| const nextYear = month === 12 ? year + 1 : year; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
|
|
||||||||||||||||||||||
| const currentResponse = await privateInstance.get<GroupedSchedule[]>( | ||||||||||||||||||||||
| `/activities/${activityId}/available-schedule?year=${year}&month=${padMonth(month)}`, | ||||||||||||||||||||||
| ); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
|
|
||||||||||||||||||||||
| const sideResults = await Promise.allSettled([ | ||||||||||||||||||||||
| privateInstance.get<GroupedSchedule[]>( | ||||||||||||||||||||||
| `/activities/${activityId}/available-schedule?year=${prevYear}&month=${padMonth(prevMonth)}`, | ||||||||||||||||||||||
| ), | ||||||||||||||||||||||
| privateInstance.get<GroupedSchedule[]>( | ||||||||||||||||||||||
| `/activities/${activityId}/available-schedule?year=${nextYear}&month=${padMonth(nextMonth)}`, | ||||||||||||||||||||||
| ), | ||||||||||||||||||||||
| ]); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| const sideData = sideResults | ||||||||||||||||||||||
| .filter( | ||||||||||||||||||||||
| (r): r is PromiseFulfilledResult<AxiosResponse<GroupedSchedule[]>> => r.status === 'fulfilled', | ||||||||||||||||||||||
| ) | ||||||||||||||||||||||
| .flatMap((r) => r.value.data); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| return [...sideData, ...currentResponse.data]; | ||||||||||||||||||||||
|
Comment on lines
+32
to
+58
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 현재 월 요청이 이전/다음 월 요청을 블로킹하여 직렬 레이턴시가 발생합니다. 현재 흐름에서 ⚡ 수정 제안: 세 요청 병렬화- const currentResponse = await privateInstance.get<GroupedSchedule[]>(
- `/activities/${activityId}/available-schedule?year=${year}&month=${padMonth(month)}`,
- );
-
- const sideResults = await Promise.allSettled([
- privateInstance.get<GroupedSchedule[]>(
- `/activities/${activityId}/available-schedule?year=${prevYear}&month=${padMonth(prevMonth)}`,
- ),
- privateInstance.get<GroupedSchedule[]>(
- `/activities/${activityId}/available-schedule?year=${nextYear}&month=${padMonth(nextMonth)}`,
- ),
- ]);
+ const [currentResult, ...sideResults] = await Promise.allSettled([
+ privateInstance.get<GroupedSchedule[]>(
+ `/activities/${activityId}/available-schedule?year=${year}&month=${padMonth(month)}`,
+ ),
+ privateInstance.get<GroupedSchedule[]>(
+ `/activities/${activityId}/available-schedule?year=${prevYear}&month=${padMonth(prevMonth)}`,
+ ),
+ privateInstance.get<GroupedSchedule[]>(
+ `/activities/${activityId}/available-schedule?year=${nextYear}&month=${padMonth(nextMonth)}`,
+ ),
+ ]);
+
+ if (currentResult.status === 'rejected') throw currentResult.reason;
const sideData = sideResults
.filter(
(r): r is PromiseFulfilledResult<AxiosResponse<GroupedSchedule[]>> => r.status === 'fulfilled',
)
.flatMap((r) => r.value.data);
- return [...sideData, ...currentResponse.data];
+ return [...sideData, ...currentResult.value.data];🤖 Prompt for AI Agents |
||||||||||||||||||||||
| }, | ||||||||||||||||||||||
| enabled: !!activityId && !!year && !!month && !isOwner, | ||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick | 🔵 Trivial
♻️ 수정 제안- enabled: !!activityId && !!year && !!month && !isOwner,
+ enabled: !!activityId && !isOwner,📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||
| }); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| const handleMonthChange = useCallback((year: number, month: number) => { | ||||||||||||||||||||||
| setTimeout(() => { | ||||||||||||||||||||||
| setYear(year); | ||||||||||||||||||||||
| setMonth(month); | ||||||||||||||||||||||
| }); | ||||||||||||||||||||||
| }, []); | ||||||||||||||||||||||
|
Comment on lines
+63
to
+68
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick | 🔵 Trivial
React 18+에서는 상태 업데이트가 자동으로 배치 처리되므로, ♻️ 수정 제안 const handleMonthChange = useCallback((year: number, month: number) => {
- setTimeout(() => {
- setYear(year);
- setMonth(month);
- });
+ setYear(year);
+ setMonth(month);
}, []);📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||
|
|
||||||||||||||||||||||
| if (isOwner) return null; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| return ( | ||||||||||||||||||||||
| <div className='md:row-span-2'> | ||||||||||||||||||||||
| <BookingInterface | ||||||||||||||||||||||
| schedules={schedulesData ?? []} | ||||||||||||||||||||||
| onMonthChange={handleMonthChange} | ||||||||||||||||||||||
| isOwner={isOwner} | ||||||||||||||||||||||
| price={price} | ||||||||||||||||||||||
| /> | ||||||||||||||||||||||
| </div> | ||||||||||||||||||||||
| ); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -12,17 +12,23 @@ import { useQueryClient } from '@tanstack/react-query'; | |
| import { useDeleteActivity } from '../hooks/useDeleteActivity'; | ||
| import Popup from '@/components/Popup'; | ||
| import { TitleProps } from '@/types/activityDetailType'; | ||
| import useUserStore from '@/stores/authStore'; | ||
|
|
||
| function Title({ | ||
| title, | ||
| category, | ||
| rating, | ||
| reviewCount, | ||
| address, | ||
| isOwner, | ||
| userId, | ||
| }: TitleProps) { | ||
| const [isPopupOpen, setIsPopupOpen] = useState(false); | ||
|
|
||
| const currentUserId = useUserStore((state) => | ||
| state.user ? state.user.id : null, | ||
| ); | ||
| const isOwner = currentUserId != null && userId != null && currentUserId === userId; | ||
|
Comment on lines
+27
to
+30
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick | 🔵 Trivial 소유자 판별 로직이 여러 컴포넌트에 중복되어 있습니다.
♻️ 리팩토링 제안// hooks/useIsOwner.ts
import useUserStore from '@/stores/authStore';
export function useIsOwner(userId: number): boolean {
const currentUserId = useUserStore((state) =>
state.user ? state.user.id : null,
);
return currentUserId != null && currentUserId === userId;
}🤖 Prompt for AI Agents |
||
|
|
||
| const { id } = useParams(); | ||
| const router = useRouter(); | ||
| const queryClient = useQueryClient(); | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,72 @@ | ||||||
| import ReviewCardSkeleton from './components/Skeletons/ReviewCardSkeleton'; | ||||||
| import SkeletonBookingInterface from './components/Skeletons/BookingInterfaceSkeleton'; | ||||||
|
|
||||||
| export default function Loading() { | ||||||
| return ( | ||||||
| <div className='mx-auto max-w-1200 animate-pulse p-4 sm:px-20 lg:p-8'> | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 외부 컨테이너의 Line 6의 외부 외부 🛠️ 수정 제안- <div className='mx-auto max-w-1200 animate-pulse p-4 sm:px-20 lg:p-8'>
+ <div className='mx-auto max-w-1200 p-4 sm:px-20 lg:p-8'>📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||
| {/* 타이틀 */} | ||||||
| <div className='mb-6 flex items-start justify-between'> | ||||||
| <div className='flex w-full flex-col gap-10'> | ||||||
| <div className='h-16 w-24 rounded bg-gray-300' /> | ||||||
| <div className='h-42 w-3/4 rounded bg-gray-300' /> | ||||||
| <div className='flex gap-10'> | ||||||
| <div className='h-20 w-50 rounded bg-gray-300' /> | ||||||
| <div className='h-20 w-170 rounded bg-gray-300' /> | ||||||
| </div> | ||||||
| </div> | ||||||
| </div> | ||||||
|
|
||||||
| {/* 이미지그리드 */} | ||||||
| <div className='relative block aspect-square h-[300px] w-full overflow-hidden rounded-lg bg-gray-300 md:hidden' /> | ||||||
| <div className='hidden h-[500px] grid-cols-4 grid-rows-4 gap-6 md:grid'> | ||||||
| <div className='col-span-2 row-span-4 rounded-lg bg-gray-300' /> | ||||||
| {[...Array(4)].map((_, i) => ( | ||||||
| <div | ||||||
| key={i} | ||||||
| className='col-span-1 row-span-2 rounded-lg bg-gray-300' | ||||||
| /> | ||||||
| ))} | ||||||
| </div> | ||||||
|
|
||||||
| {/* 설명/예약인터페이스/장소 */} | ||||||
| <div className='mt-86 grid gap-10 grid-cols-1 md:grid-cols-3'> | ||||||
| {/* 설명 */} | ||||||
| <div className='md:col-span-2'> | ||||||
| <div className='mb-10 h-34 w-90 rounded bg-gray-300' /> | ||||||
| <div className='mb-4 h-180 w-full rounded bg-gray-300' /> | ||||||
| </div> | ||||||
|
|
||||||
| {/* 예약인터페이스 */} | ||||||
| <div className='md:row-span-2'> | ||||||
| <SkeletonBookingInterface /> | ||||||
| </div> | ||||||
|
|
||||||
| {/* 체험 장소/리뷰 */} | ||||||
| <div className='md:col-span-2 space-y-8'> | ||||||
| {/* 장소 */} | ||||||
| <div className='mb-40'> | ||||||
| <div className='mb-10 h-34 w-90 rounded bg-gray-300' /> | ||||||
| <div className='h-[480px] w-full rounded-lg bg-gray-400 shadow-md' /> | ||||||
| <div className='mt-8 flex items-center space-x-3'> | ||||||
| <div className='h-6 w-6 rounded-full bg-gray-300' /> | ||||||
| <div className='h-20 w-1/2 rounded bg-gray-300' /> | ||||||
| </div> | ||||||
| </div> | ||||||
|
|
||||||
| {/* 리뷰 */} | ||||||
| <div> | ||||||
| <div className='mt-10 flex flex-col space-y-8'> | ||||||
| <div className='mb-10 h-34 w-50 rounded bg-gray-300' /> | ||||||
| <div className='mb-5 h-50 w-120 rounded bg-gray-300' /> | ||||||
| <div className='relative min-h-450 flex-col gap-30'> | ||||||
| {[...Array(3)].map((_, index) => ( | ||||||
| <ReviewCardSkeleton key={index} /> | ||||||
| ))} | ||||||
| </div> | ||||||
| </div> | ||||||
| </div> | ||||||
| </div> | ||||||
| </div> | ||||||
| </div> | ||||||
| ); | ||||||
| } | ||||||
|
Comment on lines
+4
to
+72
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick | 🔵 Trivial
🤖 Prompt for AI Agents |
||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick | 🔵 Trivial
userId != null조건이 항상true이므로 제거 가능합니다.userIdprop이number로 선언되어 있으므로userId != null가드는 불필요합니다.♻️ 수정 제안
📝 Committable suggestion
🤖 Prompt for AI Agents