-
Notifications
You must be signed in to change notification settings - Fork 2
[Feature] 데이트 코스 관련 API 연동 #111
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
Conversation
|
Caution Review failedThe pull request is closed. Summary by CodeRabbit
Walkthrough데이트코스 도메인 전반(타입·상수·검증·유틸), API 엔드포인트·쿼리키·훅·뮤테이션, Zustand 기반 필터/결과 저장소, 컴포넌트·페이지 연동 및 북마크 흐름, 라우트 경계 기반 필터 리셋·로컬키 정리, 로그아웃 시 localStorage 전체 정리, 그리고 qs 의존성 추가가 포함된 대규모 변경입니다. Changes
Sequence Diagram(s)sequenceDiagram
participant UI as Page/Components
participant Store as useFilterStore
participant Hook as useGetCourse|useGetBookmarkedCourse
participant API as getDateCourse/getBookmarkedDateCourse
UI->>Store: 필터값 읽기/갱신
UI->>Hook: 쿼리 호출({filters,page,size,isBookmarked})
Hook->>API: fetch(params)
API-->>Hook: data(result,totalPages,...)
Hook-->>UI: 응답/로딩/에러 상태
UI->>UI: 리스트 렌더링 / 페이지네이션 반영
sequenceDiagram
participant DC as DateCourseComponent
participant HB as useBookmark
participant API as postMakeBookmark/postBookmark/deleteBookmark
DC->>DC: 북마크 버튼 클릭
alt not bookmarked && make=true
DC->>HB: usePostMakeBookmark.mutate({datePlaceIds,name})
HB->>API: postMakeBookmark
API-->>HB: {dateCourseId}
HB-->>DC: onSuccess -> set bookmarked, dateCourseId
else not bookmarked && make=false
DC->>HB: usePostBookmark.mutate({dateCourseId})
HB->>API: postBookmark
API-->>HB: ok
HB-->>DC: onSuccess -> bookmarked=true
else bookmarked
DC->>DC: DeleteBookmarkModal 오픈
DC->>HB: useDeleteBookmark.mutate({dateCourseId})
HB->>API: deleteBookmark
API-->>HB: ok
HB-->>DC: onSuccess -> bookmarked=false
end
sequenceDiagram
participant MR as MakeCourseResult
participant Store as useFilterStore
participant Sig as SigStorage(LocalStorage)
participant Hook as useCourse(useMakeCourse)
participant API as postDateCourse
MR->>Store: 필터값 조회
MR->>Sig: get() -> excludedCourseSignatures
MR->>Hook: mutate({filters,excludedCourseSignatures})
Hook->>API: postDateCourse
API-->>Hook: data(result{signature,...})
Hook-->>MR: onSuccess
MR->>Sig: appendUnique(signature)
MR->>MR: 결과 스토어 업데이트 / 라우팅
Estimated code review effort🎯 5 (Critical) | ⏱️ ~90 minutes Possibly related issues
Possibly related PRs
Suggested reviewers
Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. 📜 Recent review detailsConfiguration used: .coderabbit.yaml 💡 Knowledge Base configuration:
You can enable these sources in your CodeRabbit configuration. 📒 Files selected for processing (26)
✨ Finishing Touches
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
Status, Documentation and Community
|
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.
Actionable comments posted: 65
🔭 Outside diff range comments (8)
src/components/modal/SettingModal.tsx (2)
26-35: logout 성공 시 localStorage.clear()의 광범위 삭제는 부작용 위험. 캐시/스토어 초기화도 함께 처리 권장
- 전체 clear는 사용자 설정(테마, 온보딩 플래그 등)까지 제거될 수 있습니다. 인증 관련 키만 제거하거나, 보존 화이트리스트를 두는 유틸을 도입해 주세요.
- React Query/상태 스토어는 남아 있으므로 세션 잔존 데이터가 보일 수 있습니다. 쿼리 캐시 초기화가 필요합니다.
아래처럼 쿼리 캐시 초기화를 추가하고, clear 유틸로 교체하는 것을 권장합니다.
logoutMutate(undefined, { onSuccess: () => { - localStorage.clear(); + // 인증/세션 관련 키만 제거하는 유틸을 사용하세요. + // e.g., clearAuthStorage(); + queryClient.clear(); navigate('/'); },컴포넌트 상단 추가(파일 외 삽입 코드):
import { useQueryClient } from '@tanstack/react-query'; // ... const queryClient = useQueryClient();또한 에러 시 alert 대신 통일된 토스트(예: sonner)를 사용하면 UX가 일관됩니다.
62-62: SVG color는 'currentColor' 사용을 권장대소문자 무관하게 동작하지만, 관례적으로
currentColor를 사용합니다. 팀 코드 컨벤션과 맞추면 가독성이 좋아집니다.src/components/dateCourse/placeButton.tsx (1)
1-13: 타이포(Cancle, Timmer)와 클릭/접근성 개선 제안
- 변수명 오탈자(Cancle → Cancel, Timmer → Timer)가 있어 검색/재사용성이 떨어집니다.
- 아이콘 단독 onClick은 포커스/키보드 접근성이 부족합니다. button으로 감싸고 aria-label을 제공하는 것을 권장합니다.
아래처럼 수정해 주세요.
-import Cancle from '@/assets/icons/cancel.svg?react'; -import Timmer from '@/assets/icons/timer_Blank.svg?react'; +import CancelIcon from '@/assets/icons/cancel.svg?react'; +import TimerIcon from '@/assets/icons/timer_Blank.svg?react'; @@ - <div className="flex font-body1 items-center justify-center text-center gap-[4px]"> - <Timmer stroke="#616161" /> + <div className="flex font-body1 items-center justify-center text-center gap-[4px]"> + <TimerIcon stroke="#616161" /> <div className="text-default-gray-700 text-center">{placeName}</div> </div> - - <Cancle stroke="#616161" onClick={onClick} /> + <button + type="button" + onClick={onClick} + aria-label={`${placeName} 제거`} + className="outline-none focus-visible:ring-2 focus-visible:ring-primary-500 rounded" + > + <CancelIcon stroke="#616161" /> + </button>아이콘 컬러는
stroke="currentColor"로 주고, 부모에 텍스트 색을 지정하면 테마 전환에 유연합니다.src/api/axiosInstance.ts (2)
29-33: 인터셉터에서return;은 호출 측에undefined를 전달해 체인을 깨뜨릴 수 있음현재 경로(
/,/signup)에서 조기 종료 시, 명시적으로Promise.reject(error)또는 적절한 대체 응답을 반환해야 합니다. 단순return;은 호출부에서 예외적인 undefined 응답을 유발할 수 있습니다.- if (currentUrl === '/' || currentUrl === '/signup') { - isRedirecting = false; - return; - } + if (currentUrl === '/' || currentUrl === '/signup') { + isRedirecting = false; + return Promise.reject(error); + }
21-21: 401 감지를 응답 바디 문자열 대신 HTTP 상태 코드로 전환 권장
error.response?.data.error === 'Unauthorized'는 백엔드 메시지 변경에 취약합니다.error.response?.status === 401기반으로 분기하는 것이 견고합니다.src/components/dateCourse/infoElement.tsx (1)
18-20: tags가 undefined일 때 '#undefined'가 렌더링되는 버그
(Array.isArray(tags) ? tags : [tags])는 tags가 undefined면[undefined]가 되어KeywordButton에 전달됩니다. 필터링이 필요합니다.- <div className="flex flex-1 flex-wrap gap-[8px]"> - {(Array.isArray(tags) ? tags : [tags]).map((tag, idx) => { - return <KeywordButton tag={tag} key={idx} />; - })} - </div> + <div className="flex flex-1 flex-wrap gap-[8px]"> + {(Array.isArray(tags) ? tags : tags != null ? [tags] : []) + .filter((t): t is string => typeof t === 'string' && t.length > 0) + .map((tag, idx) => ( + <KeywordButton tag={tag} key={idx} /> + ))} + </div>src/components/modal/deleteBookmarkModal.tsx (2)
46-50: 취소 동작 시 부모 상태 동기화 누락취소 버튼에서도 changeState(false)를 호출해 isOpen과 isVisible 간 상태 불일치 가능성을 줄이는 것이 안전합니다.
- onClick={onClose} + onClick={() => { + changeState(false); + onClose(); + }}
51-53: 중복 요청 방지: 삭제 진행 중 버튼 비활성화삭제 진행(isPending) 중에는 버튼을 비활성화하여 중복 요청을 방지하세요.
- <button onClick={handleDelete} className="py-[8px] w-[50%] flex justify-center rounded-rb-[32px] text-center font-body1 text-warning"> + <button + onClick={handleDelete} + disabled={isPending} + aria-busy={isPending} + className="py-[8px] w-[50%] flex justify-center rounded-rb-[32px] text-center font-body1 text-warning disabled:opacity-50" + >
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (2)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yamlyarn.lockis excluded by!**/yarn.lock,!**/*.lock,!**/*.lock
📒 Files selected for processing (33)
package.json(2 hunks)src/api/axiosInstance.ts(1 hunks)src/api/course/course.ts(2 hunks)src/app/routeChangeReset.tsx(1 hunks)src/components/common/navigator.tsx(2 hunks)src/components/dateCourse/dateCourse.tsx(5 hunks)src/components/dateCourse/dateCourseSearchFilterOption.tsx(7 hunks)src/components/dateCourse/info.tsx(2 hunks)src/components/dateCourse/infoElement.tsx(1 hunks)src/components/dateCourse/placeButton.tsx(1 hunks)src/components/dateCourse/timeline.tsx(1 hunks)src/components/home/level.tsx(2 hunks)src/components/modal/SettingModal.tsx(1 hunks)src/components/modal/dateCourseSearchFilterModal.tsx(2 hunks)src/components/modal/deleteBookmarkModal.tsx(1 hunks)src/constants/dateCourseQuestion.ts(1 hunks)src/constants/level.ts(1 hunks)src/hooks/course/useBookmark.ts(1 hunks)src/hooks/course/useCourse.ts(1 hunks)src/hooks/course/useGetBookmarkedCourse.ts(1 hunks)src/hooks/course/useGetCourse.ts(1 hunks)src/layout/layout.tsx(1 hunks)src/pages/dateCourse/BookmarkedDateCourse.tsx(2 hunks)src/pages/dateCourse/CoursePage.tsx(3 hunks)src/pages/dateCourse/FindDateCourse.tsx(2 hunks)src/pages/dateCourse/MakeCourseResult.tsx(1 hunks)src/pages/dateCourse/MakeCourseStep.tsx(2 hunks)src/queryKey/queryKey.ts(1 hunks)src/store/useDateCourseResultStore.ts(1 hunks)src/store/useFilterStore.ts(1 hunks)src/types/dateCourse/dateCourse.ts(6 hunks)src/utils/appendSignature.ts(1 hunks)src/utils/dateCourseValidation.tsx(6 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (27)
src/components/modal/SettingModal.tsx (1)
src/api/auth/auth.ts (1)
Promise(33-36)
src/hooks/course/useBookmark.ts (5)
src/hooks/customQuery.ts (1)
useCoreMutation(20-72)src/api/course/course.ts (3)
postBookmark(54-57)postMakeBookmark(58-61)deleteBookmark(62-65)src/hooks/auth/useAccount.ts (2)
useChangeNickname(14-16)useAccount(7-39)src/hooks/auth/useAuth.ts (1)
useAuth(5-14)src/hooks/home/useUserRegion.ts (1)
useUserRegion(6-8)
src/hooks/course/useCourse.ts (4)
src/hooks/customQuery.ts (1)
useCoreMutation(20-72)src/api/course/course.ts (1)
postDateCourse(31-52)src/hooks/auth/useAccount.ts (1)
useChangeNickname(14-16)src/hooks/home/useUserRegion.ts (1)
useUserRegion(6-8)
src/api/axiosInstance.ts (1)
src/api/auth/auth.ts (2)
logout(33-36)Promise(28-31)
src/components/dateCourse/placeButton.tsx (1)
src/pages/dateCourse/MakeCourse.tsx (1)
MakeCourse(8-29)
src/hooks/course/useGetBookmarkedCourse.ts (7)
src/types/dateCourse/dateCourse.ts (1)
TCourseFilter(218-226)src/hooks/customQuery.ts (1)
useCoreQuery(8-18)src/queryKey/queryKey.ts (1)
dateCourseKeys(32-57)src/api/course/course.ts (1)
getBookmarkedDateCourse(91-110)src/hooks/home/useDateCourseStates.ts (1)
useCoreQuery(7-12)src/hooks/course/useSearchRegion.tsx (1)
useSearchRegion(6-11)src/hooks/auth/useAccount.ts (1)
useGetMemberGrade(29-31)
src/hooks/course/useGetCourse.ts (8)
src/types/dateCourse/dateCourse.ts (1)
TCourseFilter(218-226)src/hooks/customQuery.ts (1)
useCoreQuery(8-18)src/queryKey/queryKey.ts (1)
dateCourseKeys(32-57)src/api/course/course.ts (1)
getDateCourse(67-89)src/hooks/home/useDateCourseStates.ts (1)
useCoreQuery(7-12)src/hooks/course/useSearchRegion.tsx (1)
useSearchRegion(6-11)src/hooks/home/useUserGrade.ts (1)
useCoreQuery(7-12)src/hooks/auth/useAccount.ts (1)
useGetMemberGrade(29-31)
src/components/dateCourse/info.tsx (1)
src/components/dateCourse/infoElement.tsx (1)
InfoElement(10-24)
src/components/dateCourse/timeline.tsx (2)
src/types/dateCourse/dateCourse.ts (1)
TTimeline(135-155)src/components/dateCourse/keywordButton.tsx (1)
KeywordButton(3-15)
src/constants/level.ts (6)
src/types/home/level.ts (1)
IGradeInfo(7-13)src/api/home/level.ts (1)
Promise(6-9)src/hooks/home/useUserGrade.ts (1)
getUserGrade(8-8)src/pages/dateTest/DateTestStep.tsx (1)
ProgressBar(11-18)src/components/datetest/ProgressBar.tsx (2)
IProgressBarProps(1-4)ProgressBar(6-14)src/api/auth/account.ts (1)
getMemberGrade(30-33)
src/components/home/level.tsx (1)
src/constants/level.ts (1)
getProgressToNextGrade(19-30)
src/pages/dateCourse/CoursePage.tsx (2)
src/hooks/course/useGetBookmarkedCourse.ts (1)
useGetBookmarkedCourse(12-48)src/hooks/home/useUserGrade.ts (1)
useUserGrade(7-12)
src/pages/dateCourse/MakeCourseStep.tsx (4)
src/hooks/course/useCourse.ts (1)
useCourse(5-8)src/utils/dateCourseValidation.tsx (6)
BudgetTimeValidation(131-144)TotalTimeMealValidation(98-108)KeywordMealValidation(110-119)KeywordGroupOverValidation(121-129)MealTimeValidation(27-72)DateTimeStartValidation(79-96)src/components/dateCourse/dateCourseLoading.tsx (1)
DateCourseLoading(4-18)src/components/dateCourse/dateCourseSearchFilterOption.tsx (1)
DateCourseSearchFilterOption(15-211)
src/components/dateCourse/infoElement.tsx (3)
src/components/dateCourse/dateKeyword.tsx (3)
tag(17-29)DateKeyword(5-33)toggleItem(24-26)src/components/dateCourse/keywordButton.tsx (1)
KeywordButton(3-15)src/components/home/banner.tsx (1)
tag(72-81)
src/components/common/navigator.tsx (3)
src/pages/dateTest/DateTestStep.tsx (1)
currentStep(48-54)src/pages/notice/Notice.tsx (1)
p(95-95)src/components/home/banner.tsx (2)
setCurrentIndex(52-54)prevIndex(46-46)
src/store/useDateCourseResultStore.ts (1)
src/types/dateCourse/dateCourse.ts (2)
TDatePlaces(110-126)TDateCourseSearchCondInfo(95-103)
src/queryKey/queryKey.ts (1)
src/hooks/home/useDateCourseStates.ts (1)
useCoreQuery(7-12)
src/pages/dateCourse/MakeCourseResult.tsx (4)
src/hooks/course/useCourse.ts (1)
useCourse(5-8)src/hooks/home/useUserGrade.ts (1)
useUserGrade(7-12)src/utils/appendSignature.ts (1)
SigStorage(1-46)src/components/dateCourse/dateCourseLoading.tsx (1)
DateCourseLoading(4-18)
src/components/modal/dateCourseSearchFilterModal.tsx (3)
src/utils/dateCourseValidation.tsx (6)
BudgetTimeValidation(131-144)TotalTimeMealValidation(98-108)KeywordMealValidation(110-119)KeywordGroupOverValidation(121-129)MealTimeValidation(27-72)DateTimeStartValidation(79-96)src/hooks/course/useGetCourse.ts (1)
useGetCourse(12-28)src/constants/dateCourseQuestion.ts (1)
DateCourseQuestion(1-72)
src/store/useFilterStore.ts (2)
src/store/useAuthStore.ts (1)
set(13-21)src/store/useModalStore.ts (1)
set(11-37)
src/components/modal/deleteBookmarkModal.tsx (1)
src/hooks/course/useBookmark.ts (1)
useBookmark(5-10)
src/components/dateCourse/dateCourseSearchFilterOption.tsx (2)
src/types/dateCourse/dateCourse.ts (1)
TDateCourseSearchFilterOption(47-57)src/components/dateCourse/dateCourseOptionButton.tsx (1)
DateCourseOptionButton(3-15)
src/api/course/course.ts (5)
src/types/dateCourse/dateCourse.ts (12)
TPostDateCourseRequest(104-106)TPostDateCourseResponse(108-108)TPostBookmarkRequest(166-168)TPostBookmarkResponse(170-172)TPostMakeBookmarkRequest(174-177)TPostMakeBookmarkResponse(179-181)TDeleteBookmarkRequest(183-185)TDeleteBookmarkResponse(187-189)TGetDateCourseRequest(191-194)TGetDateCourseResponse(196-203)TGetBookmarkedDateCourseRequest(205-208)TGetBookmarkedDateCourseResponse(210-216)src/api/axiosInstance.ts (1)
axiosInstance(11-14)src/api/home/dateCourse.ts (1)
Promise(5-8)src/pages/dateCourse/MakeCourse.tsx (1)
MakeCourse(8-29)src/components/home/dateCourseStore.tsx (1)
DateCourseStore(9-32)
src/pages/dateCourse/BookmarkedDateCourse.tsx (2)
src/hooks/course/useGetBookmarkedCourse.ts (1)
useGetBookmarkedCourse(12-48)src/components/common/navigator.tsx (1)
Navigator(10-43)
src/components/dateCourse/dateCourse.tsx (4)
src/types/dateCourse/dateCourse.ts (2)
TDateCourse(157-164)TDateCourseSearchCondInfo(95-103)src/hooks/course/useBookmark.ts (1)
useBookmark(5-10)src/api/course/course.ts (2)
postBookmark(54-57)postMakeBookmark(58-61)src/components/dateCourse/info.tsx (1)
Info(12-52)
src/pages/dateCourse/FindDateCourse.tsx (2)
src/hooks/course/useGetCourse.ts (1)
useGetCourse(12-28)src/components/common/navigator.tsx (1)
Navigator(10-43)
src/types/dateCourse/dateCourse.ts (1)
src/types/common/common.ts (1)
TCommonResponse(4-9)
🪛 Biome (2.1.2)
src/components/dateCourse/timeline.tsx
[error] 17-17: Static Elements should not be interactive.
To add interactivity such as a mouse or key event listener to a static element, give the element an appropriate role value.
(lint/a11y/noStaticElementInteractions)
[error] 17-17: Enforce to have the onClick mouse event with the onKeyUp, the onKeyDown, or the onKeyPress keyboard event.
Actions triggered using mouse events should have corresponding keyboard events to account for keyboard-only navigation.
(lint/a11y/useKeyWithClickEvents)
[error] 43-43: Provide a text alternative through the alt, aria-label or aria-labelledby attribute
Meaningful alternative text on elements helps users relying on screen readers to understand content's purpose within a page.
If the content is decorative, redundant, or obscured, consider hiding it from assistive technologies with the aria-hidden attribute.
(lint/a11y/useAltText)
src/components/common/navigator.tsx
[error] 24-25: Provide an explicit type prop for the button element.
The default type of a button is submit, which causes the submission of a form when placed inside a form element. This is likely not the behaviour that you want inside a React application.
Allowed button types are: submit, button or reset
(lint/a11y/useButtonType)
[error] 38-39: Provide an explicit type prop for the button element.
The default type of a button is submit, which causes the submission of a form when placed inside a form element. This is likely not the behaviour that you want inside a React application.
Allowed button types are: submit, button or reset
(lint/a11y/useButtonType)
🔇 Additional comments (17)
src/components/home/level.tsx (1)
4-4: 등급 진행 계산 로직의 중앙화, 방향 좋습니다컴포넌트 내부 계산을 상수/유틸로 추출해 일관성과 재사용성이 좋아졌습니다. 다른 곳에서도 동일한 규칙을 쉽게 공유할 수 있겠네요.
package.json (1)
30-30: qs 도입 검증 결과
- 패키지 버전은 최신(6.14.0)입니다.
- stringify 호출 위치:
src/api/course/course.ts:86(arrayFormat: 'repeat'설정).@types/qs는devDependencies에만 존재합니다.추가 검토 및 조치 항목:
- 번들 사이즈 영향 분석(npm bundle 분석 도구 활용)
- 보안 취약점 점검(npm audit 또는 GitHub Security 활용)
arrayFormat,encode등 옵션 일관화를 위한 헬퍼/코딩 가이드 정의src/components/modal/SettingModal.tsx (1)
23-25: useLogout 사용 방식 확인 필요 (함수 호출 누락 가능성)
const { useLogout } = useAuth();다음에const { mutate, isPending } = useLogout;로 바로 구조 분해하고 있어,useLogout이 "훅 반환 결과"인지 "훅 팩토리 함수"인지에 따라 호출 누락 가능성이 있습니다. 만약 훅이라면const { mutate, isPending } = useLogout();형태여야 합니다. 현재 구현 의도를 한번 확인해 주세요.src/components/dateCourse/placeButton.tsx (1)
9-9: 텍스트 중앙 정렬 변경 LGTM레이아웃 일관성 측면에서 중심 정렬이 더 자연스럽습니다.
src/components/common/navigator.tsx (1)
18-21: 빈 페이지군 안전장치(LB) 추가는 적절합니다페이지 계산이 0개일 때 1을 보장하는 처리는 엣지케이스에 유용합니다. 문제없습니다.
src/utils/appendSignature.ts (1)
4-13: 파서/마이그레이션 로직은 견고합니다단일 문자열/JSON 배열 혼재를 안전하게 처리하며, 예외 시 폴백이 적절합니다.
src/store/useFilterStore.ts (1)
27-36: 전반적으로 깔끔한 Zustand + persist 구성초기 상태, 부분 업데이트, reset 동작이 명확합니다. 세션 간 지속도 적절합니다.
src/app/routeChangeReset.tsx (1)
21-29: 라우트 스코프 기반 초기화 로직은 목적에 부합합니다스코프 간 이동 시에만 reset을 트리거하는 방식이 과초기화를 방지합니다. 적절합니다.
src/layout/layout.tsx (1)
8-16: 레이아웃 내 전역 리스너 배치 적절Footer 이후에 배치하여 앱 전역에서 라우트 변화를 감지하는 구조가 명확합니다.
src/types/dateCourse/dateCourse.ts (2)
22-22: TSignatureDish.price 타입 재확인 필요가격이 문자열로 선언되어 있는데, 금액 연산/정렬이 필요하다면 number가 더 적합합니다. API 스펙에 맞게 타입 재검토를 부탁드립니다.
34-39: TKeywordButtonProps.tag를 optional로 변경한 영향 검증 필요tag가 undefined일 수 있게 바뀌었습니다. KeywordButton가
tag?: string을 안전하게 처리하는지(빈 렌더/placeholder 처리 등) 사용처 전반(예: infoElement.tsx)에서 확인 부탁드립니다.src/hooks/course/useCourse.ts (1)
5-8: 패턴 일치, 문제 없어 보입니다프로젝트의 기존 훅 패턴(useCoreMutation 래핑)과 일관적입니다.
src/hooks/course/useBookmark.ts (1)
5-10: 북마크 관련 뮤테이션 훅 일원화, 좋습니다세 가지 뮤테이션을 단일 훅에서 노출하는 구조가 사용성/응집도 측면에서 적절합니다.
src/components/dateCourse/info.tsx (1)
31-51: 매핑/폴백 처리 잘 되었고, UI 구조 일관적입니다미지정 키에 대한 ‘알 수 없음’ 폴백과 배열 매핑이 명확합니다.
src/pages/dateCourse/CoursePage.tsx (1)
23-25: 페이지 인덱스 기준(0/1) 확인 권장API가 0-based 페이지 인덱스를 요구하는 경우 off-by-one 이슈가 발생할 수 있습니다. 스펙을 확인해 주세요.
src/hooks/course/useGetBookmarkedCourse.ts (1)
24-34: 쿼리 키에 포함된 필터 사용 여부 확인 요청
src/hooks/course/useGetBookmarkedCourse.ts(24–34행)에서dateCourseKeys.getBookmarkedDateCourse의queryKey에dateDurationTime,startTime이 포함되어 있습니다.- 백엔드 API가 해당 필터를 실제로 지원하는지 확인해 주세요. 지원하지 않는 필터를 키에 넣으면 캐시 파편화와 불필요한 재요청이 발생할 수 있습니다.
- 미사용 필터라면
queryKey에서 제거하는 것을 권장합니다.가능한 조정 예시 (파일:
src/queryKey/queryKey.ts):getBookmarkedDateCourse: ({ budget, datePlaces, mealTypes, transportation, userPreferredKeywords, size, page, }) => [ 'bookmark', budget, datePlaces, mealTypes, transportation, userPreferredKeywords, size, page, ],src/components/dateCourse/dateCourse.tsx (1)
70-70:datePlaces배열이 비어있을 수 있음
datePlaces가 빈 배열일 때undefined를 참조하게 됩니다.다음과 같이 수정하세요:
- const lastEndTime = datePlaces[datePlaces.length - 1]?.endTime; + const lastEndTime = datePlaces?.length > 0 ? datePlaces[datePlaces.length - 1].endTime : undefined;Likely an incorrect or invalid review comment.
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.
Review continued from previous batch...
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.
Actionable comments posted: 79
🔭 Outside diff range comments (8)
src/api/axiosInstance.ts (2)
41-64: catch 경로에서 isRedirecting 플래그가 해제되지 않아 이후 요청이 영구 차단될 수 있음토큰 재발급 실패 시 catch 블록에서 isRedirecting을 false로 되돌리지 않아, 이후 401 응답에 대해 인터셉터가 항상 early-return 하며 정상 복구/리다이렉트가 불가능해질 수 있습니다.
다음과 같이 reject 직전에 isRedirecting을 해제해 주세요.
} catch (errors) { if (axios.isAxiosError(errors)) { const refreshError = errors as AxiosError<IRefreshResponse>; if (refreshError.response?.data.message === 'The token is null.') { console.error('refreshToken이 없습니다. 로그인 페이지로 이동합니다.'); void logout(); localStorage.clear(); } else if (refreshError.response?.data.message === 'The token is invalid.') { console.error('refreshToken이 만료되었습니다. 로그인 페이지로 이동합니다.'); void logout(); localStorage.clear(); } else { console.error('알 수 없는 오류가 발생했습니다', errors); void logout(); localStorage.clear(); } } else { console.error('알 수 없는 오류가 발생했습니다', errors); void logout(); localStorage.clear(); } + // 재시도/후속 401 처리를 위해 플래그 원복 + isRedirecting = false; return Promise.reject(errors); }
29-32: 에러 인터셉터에서 에러를 정상 전파하려면Promise.reject를 반환하세요에러 핸들러에서
return;은 호출부에undefined를 전달해 에러가 무시될 수 있습니다. 에러를 상위로 전달하려면 명시적으로Promise.reject(error)를 반환해야 합니다.
- 파일:
src/api/axiosInstance.ts29–32줄- 문제:
return;→ 호출부에undefined반환(에러 미전파)- 수정 제안:
- isRedirecting = false; - return; + isRedirecting = false; + return Promise.reject(error);src/utils/dateCourseValidation.tsx (1)
74-77: 주석의 시간 포맷 예시 업데이트 필요주석은 공백 구분 예시('2025-07-18 22:30')인데, 본문은 'T' 구분을 가정하고 있었습니다. 상기의 파싱 보강과 함께 주석도 최신 포맷으로 정리해 주세요.
src/components/modal/deleteBookmarkModal.tsx (2)
36-44: 모달 접근성(ARIA) 속성 추가 제안.스크린 리더 지원을 위해 role, aria-modal, aria-labelledby를 추가하는 것을 권장합니다.
- <div className="z-[1000] fixed top-0 left-0 w-[100vw] h-[100vh] bg-black/30 flex items-center justify-center"> - <div className="flex flex-col rounding-32 bg-default-gray-100 pt-[40px] gap-[40px]"> - <div className="flex flex-col items-center justify-center px-[70px] w-fit"> - <div className="font-heading3 text-default-gray-800 select-none">저장된 데이트 코스 삭제</div> + <div className="z-[1000] fixed top-0 left-0 w-[100vw] h-[100vh] bg-black/30 flex items-center justify-center"> + <div + className="flex flex-col rounding-32 bg-default-gray-100 pt-[40px] gap-[40px]" + role="dialog" + aria-modal="true" + aria-labelledby="delete-bookmark-title" + > + <div className="flex flex-col items-center justify-center px-[70px] w-fit"> + <div id="delete-bookmark-title" className="font-heading3 text-default-gray-800 select-none">저장된 데이트 코스 삭제</div>
51-53: 삭제 버튼 비활성화 및 접근성 보완.API 호출 중 버튼을 비활성화하고 aria-disabled를 추가해 주세요.
- <button onClick={handleDelete} className="py-[8px] w-[50%] flex justify-center rounded-rb-[32px] text-center font-body1 text-warning"> + <button + onClick={handleDelete} + disabled={isPending} + aria-disabled={isPending} + className="py-[8px] w-[50%] flex justify-center rounded-rb-[32px] text-center font-body1 text-warning disabled:opacity-50 disabled:cursor-not-allowed" + >src/components/dateCourse/timeline.tsx (1)
31-64: 세부 패널에 id 연결로 접근성 보완.토글 버튼의
aria-controls와 매칭되도록 상세 영역에 id를 부여해 주세요.- {open && ( - <div className="flex lg:items-start self-stretch w-full gap-[9px] flex-col lg:flex-row items-center"> + {open && ( + <div id="timeline-panel" className="flex lg:items-start self-stretch w-full gap-[9px] flex-col lg:flex-row items-center">src/components/modal/dateCourseSearchFilterModal.tsx (1)
171-196: 렌더 시 value/onChange/errorMessage 모두 id 기반으로 접근하도록 수정현재 idx 기반 접근으로 필드가 엇갈립니다. 아래처럼 교체해 주세요.
- {Questions.map((q, idx) => ( + {Questions.map((q) => ( <DateCourseSearchFilterOption key={q.id} title={q.filterTitle} subTitle={q.subTitle} options={q.options} - value={valueByIndex(idx)} - onChange={(v) => updateByIndex(idx, v)} + value={valueById(q.id)} + onChange={(v) => updateById(q.id, v)} type={q.type} apiRequestValue={q.apiRequestValue} - errorMessage={errorMessages[idx] ?? ''} + errorMessage={errorById[q.id] ?? ''} /> ))}src/types/dateCourse/dateCourse.ts (1)
35-39: KeywordButton 컴포넌트의tag기본값 또는 널 가드 추가 필요
TKeywordButtonProps에서tag?: string으로 변경됨에 따라,KeywordButton컴포넌트 내부에서#{tag}가undefined일 경우 “#undefined”로 렌더되거나 의도치 않은 UI가 표시될 수 있습니다. 런타임 이슈 및 UX 혼란 방지를 위해 다음 중 하나를 적용해 주세요.
- 컴포넌트 파라미터 기본값 설정
export default function KeywordButton({ tag = '', selected = false, onClick, isButton, }: TKeywordButtonProps) { … }- 렌더링 시 널(null)/undefined 검증
<div> {tag != null ? `#${tag}` : '-'} </div>점검 위치:
- src/types/dateCourse/dateCourse.ts:
TKeywordButtonProps의tag?: string- src/components/dateCourse/keywordButton.tsx: 함수 시그니처 및
#{tag}사용 부분
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (2)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yamlyarn.lockis excluded by!**/yarn.lock,!**/*.lock,!**/*.lock
📒 Files selected for processing (33)
package.json(2 hunks)src/api/axiosInstance.ts(1 hunks)src/api/course/course.ts(2 hunks)src/app/routeChangeReset.tsx(1 hunks)src/components/common/navigator.tsx(2 hunks)src/components/dateCourse/dateCourse.tsx(5 hunks)src/components/dateCourse/dateCourseSearchFilterOption.tsx(7 hunks)src/components/dateCourse/info.tsx(2 hunks)src/components/dateCourse/infoElement.tsx(1 hunks)src/components/dateCourse/placeButton.tsx(1 hunks)src/components/dateCourse/timeline.tsx(1 hunks)src/components/home/level.tsx(2 hunks)src/components/modal/SettingModal.tsx(1 hunks)src/components/modal/dateCourseSearchFilterModal.tsx(2 hunks)src/components/modal/deleteBookmarkModal.tsx(1 hunks)src/constants/dateCourseQuestion.ts(1 hunks)src/constants/level.ts(1 hunks)src/hooks/course/useBookmark.ts(1 hunks)src/hooks/course/useCourse.ts(1 hunks)src/hooks/course/useGetBookmarkedCourse.ts(1 hunks)src/hooks/course/useGetCourse.ts(1 hunks)src/layout/layout.tsx(1 hunks)src/pages/dateCourse/BookmarkedDateCourse.tsx(2 hunks)src/pages/dateCourse/CoursePage.tsx(3 hunks)src/pages/dateCourse/FindDateCourse.tsx(2 hunks)src/pages/dateCourse/MakeCourseResult.tsx(1 hunks)src/pages/dateCourse/MakeCourseStep.tsx(2 hunks)src/queryKey/queryKey.ts(1 hunks)src/store/useDateCourseResultStore.ts(1 hunks)src/store/useFilterStore.ts(1 hunks)src/types/dateCourse/dateCourse.ts(6 hunks)src/utils/appendSignature.ts(1 hunks)src/utils/dateCourseValidation.tsx(6 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (28)
src/hooks/course/useCourse.ts (3)
src/hooks/customQuery.ts (1)
useCoreMutation(20-72)src/api/course/course.ts (1)
postDateCourse(31-52)src/hooks/auth/useAuth.ts (1)
useAuth(5-14)
src/components/modal/SettingModal.tsx (1)
src/api/auth/auth.ts (1)
Promise(33-36)
src/hooks/course/useBookmark.ts (5)
src/hooks/customQuery.ts (1)
useCoreMutation(20-72)src/api/course/course.ts (3)
postBookmark(54-57)postMakeBookmark(58-61)deleteBookmark(62-65)src/hooks/auth/useAuth.ts (1)
useAuth(5-14)src/hooks/auth/useAccount.ts (3)
useChangeNickname(14-16)useAccount(7-39)useDeleteMember(19-21)src/hooks/home/useUserRegion.ts (1)
useUserRegion(6-8)
src/layout/layout.tsx (2)
src/components/layout/Footer.tsx (1)
Footer(3-38)src/layout/minimalLayout.tsx (1)
MinimalLayout(5-12)
src/api/axiosInstance.ts (1)
src/api/auth/auth.ts (2)
logout(33-36)Promise(28-31)
src/components/modal/deleteBookmarkModal.tsx (1)
src/hooks/course/useBookmark.ts (1)
useBookmark(5-10)
src/store/useFilterStore.ts (2)
src/store/useAuthStore.ts (2)
set(13-21)IAuthState(3-11)src/store/useModalStore.ts (1)
set(11-37)
src/components/dateCourse/info.tsx (2)
src/components/dateCourse/infoElement.tsx (1)
InfoElement(10-24)src/components/dateCourse/dateKeyword.tsx (1)
DateKeyword(5-33)
src/components/dateCourse/timeline.tsx (2)
src/types/dateCourse/dateCourse.ts (1)
TTimeline(135-155)src/components/dateCourse/keywordButton.tsx (1)
KeywordButton(3-15)
src/components/common/navigator.tsx (1)
src/pages/notice/Notice.tsx (1)
p(95-95)
src/components/dateCourse/infoElement.tsx (3)
src/components/dateCourse/dateKeyword.tsx (3)
tag(17-29)DateKeyword(5-33)toggleItem(24-26)src/components/dateCourse/keywordButton.tsx (1)
KeywordButton(3-15)src/components/home/dateRecommend.tsx (1)
tag(145-149)
src/hooks/course/useGetCourse.ts (4)
src/types/dateCourse/dateCourse.ts (1)
TCourseFilter(218-226)src/hooks/customQuery.ts (1)
useCoreQuery(8-18)src/queryKey/queryKey.ts (1)
dateCourseKeys(32-57)src/api/course/course.ts (1)
getDateCourse(67-89)
src/queryKey/queryKey.ts (2)
src/hooks/home/useDateCourseStates.ts (1)
useCoreQuery(7-12)src/components/dateCourse/dateKeyword.tsx (1)
DateKeyword(5-33)
src/hooks/course/useGetBookmarkedCourse.ts (7)
src/types/dateCourse/dateCourse.ts (1)
TCourseFilter(218-226)src/hooks/customQuery.ts (1)
useCoreQuery(8-18)src/queryKey/queryKey.ts (1)
dateCourseKeys(32-57)src/api/course/course.ts (1)
getBookmarkedDateCourse(91-110)src/hooks/home/useDateCourseStates.ts (1)
useCoreQuery(7-12)src/hooks/course/useSearchRegion.tsx (1)
useSearchRegion(6-11)src/hooks/auth/useAccount.ts (1)
useGetMemberGrade(29-31)
src/constants/level.ts (3)
src/types/home/level.ts (1)
IGradeInfo(7-13)src/api/home/level.ts (1)
Promise(6-9)src/hooks/home/useUserGrade.ts (1)
getUserGrade(8-8)
src/store/useDateCourseResultStore.ts (2)
src/types/dateCourse/dateCourse.ts (2)
TDatePlaces(110-126)TDateCourseSearchCondInfo(95-103)src/store/useAuthStore.ts (2)
set(13-21)IAuthState(3-11)
src/pages/dateCourse/MakeCourseResult.tsx (4)
src/hooks/course/useCourse.ts (1)
useCourse(5-8)src/hooks/home/useUserGrade.ts (1)
useUserGrade(7-12)src/utils/appendSignature.ts (1)
SigStorage(1-46)src/components/dateCourse/dateCourseLoading.tsx (1)
DateCourseLoading(4-18)
src/pages/dateCourse/CoursePage.tsx (2)
src/hooks/course/useGetBookmarkedCourse.ts (1)
useGetBookmarkedCourse(12-48)src/hooks/home/useUserGrade.ts (1)
useUserGrade(7-12)
src/app/routeChangeReset.tsx (1)
src/components/common/modalProvider.tsx (1)
ModalProvider(31-40)
src/components/home/level.tsx (3)
src/constants/level.ts (1)
getProgressToNextGrade(19-30)src/components/datetest/ProgressBar.tsx (2)
ProgressBar(6-14)IProgressBarProps(1-4)src/pages/dateTest/DateTestStep.tsx (2)
ProgressBar(11-18)DateTestStep(20-134)
src/api/course/course.ts (2)
src/types/dateCourse/dateCourse.ts (12)
TPostDateCourseRequest(104-106)TPostDateCourseResponse(108-108)TPostBookmarkRequest(166-168)TPostBookmarkResponse(170-172)TPostMakeBookmarkRequest(174-177)TPostMakeBookmarkResponse(179-181)TDeleteBookmarkRequest(183-185)TDeleteBookmarkResponse(187-189)TGetDateCourseRequest(191-194)TGetDateCourseResponse(196-203)TGetBookmarkedDateCourseRequest(205-208)TGetBookmarkedDateCourseResponse(210-216)src/api/axiosInstance.ts (1)
axiosInstance(11-14)
src/components/dateCourse/dateCourseSearchFilterOption.tsx (2)
src/types/dateCourse/dateCourse.ts (1)
TDateCourseSearchFilterOption(47-57)src/components/dateCourse/dateCourseOptionButton.tsx (1)
DateCourseOptionButton(3-15)
src/pages/dateCourse/MakeCourseStep.tsx (6)
src/hooks/course/useCourse.ts (1)
useCourse(5-8)src/utils/dateCourseValidation.tsx (6)
BudgetTimeValidation(131-144)TotalTimeMealValidation(98-108)KeywordMealValidation(110-119)KeywordGroupOverValidation(121-129)MealTimeValidation(27-72)DateTimeStartValidation(79-96)src/components/dateCourse/dateCourseLoading.tsx (1)
DateCourseLoading(4-18)src/components/datetest/ProgressBar.tsx (1)
ProgressBar(6-14)src/components/dateCourse/dateCourseSearchFilterOption.tsx (1)
DateCourseSearchFilterOption(15-211)src/components/common/Button.tsx (1)
Button(14-42)
src/components/dateCourse/dateCourse.tsx (4)
src/types/dateCourse/dateCourse.ts (2)
TDateCourse(157-164)TDateCourseSearchCondInfo(95-103)src/hooks/course/useBookmark.ts (1)
useBookmark(5-10)src/api/course/course.ts (2)
postBookmark(54-57)postMakeBookmark(58-61)src/components/dateCourse/info.tsx (1)
Info(12-52)
src/pages/dateCourse/BookmarkedDateCourse.tsx (2)
src/hooks/course/useGetBookmarkedCourse.ts (1)
useGetBookmarkedCourse(12-48)src/components/common/navigator.tsx (1)
Navigator(10-43)
src/pages/dateCourse/FindDateCourse.tsx (3)
src/hooks/course/useGetCourse.ts (1)
useGetCourse(12-28)src/components/common/navigator.tsx (1)
Navigator(10-43)src/pages/dateCourse/MakeCourse.tsx (2)
MakeCourse(8-29)navigate(13-13)
src/components/modal/dateCourseSearchFilterModal.tsx (5)
src/utils/dateCourseValidation.tsx (6)
BudgetTimeValidation(131-144)TotalTimeMealValidation(98-108)KeywordMealValidation(110-119)KeywordGroupOverValidation(121-129)MealTimeValidation(27-72)DateTimeStartValidation(79-96)src/hooks/course/useGetCourse.ts (1)
useGetCourse(12-28)src/constants/dateCourseQuestion.ts (1)
DateCourseQuestion(1-72)src/components/common/modal.tsx (1)
Modal(15-43)src/components/common/Button.tsx (1)
Button(14-42)
src/types/dateCourse/dateCourse.ts (1)
src/types/common/common.ts (1)
TCommonResponse(4-9)
🪛 Biome (2.1.2)
src/components/dateCourse/timeline.tsx
[error] 17-17: Static Elements should not be interactive.
To add interactivity such as a mouse or key event listener to a static element, give the element an appropriate role value.
(lint/a11y/noStaticElementInteractions)
[error] 17-17: Enforce to have the onClick mouse event with the onKeyUp, the onKeyDown, or the onKeyPress keyboard event.
Actions triggered using mouse events should have corresponding keyboard events to account for keyboard-only navigation.
(lint/a11y/useKeyWithClickEvents)
[error] 43-43: Provide a text alternative through the alt, aria-label or aria-labelledby attribute
Meaningful alternative text on elements helps users relying on screen readers to understand content's purpose within a page.
If the content is decorative, redundant, or obscured, consider hiding it from assistive technologies with the aria-hidden attribute.
(lint/a11y/useAltText)
src/components/common/navigator.tsx
[error] 24-25: Provide an explicit type prop for the button element.
The default type of a button is submit, which causes the submission of a form when placed inside a form element. This is likely not the behaviour that you want inside a React application.
Allowed button types are: submit, button or reset
(lint/a11y/useButtonType)
[error] 38-39: Provide an explicit type prop for the button element.
The default type of a button is submit, which causes the submission of a form when placed inside a form element. This is likely not the behaviour that you want inside a React application.
Allowed button types are: submit, button or reset
(lint/a11y/useButtonType)
🔇 Additional comments (22)
src/constants/level.ts (1)
21-21: 알 수 없는 등급 처리 개선 제안
currentIndex === -1일 때 예외를 던지면 UI 전체가 중단될 수 있습니다.
→ 예외 대신console.warn로그를 남기고 기본값(0)을 반환하는 방식을 고려해 주세요.- 최소한 호출부에서
try/catch로 안전망을 두는 것도 좋습니다.- BE에서 내려오는 등급 문자열이 항상
gradeTable과 완전히 일치(1:1)하는지 확인 부탁드립니다.대안 예시:
- if (currentIndex === -1) throw new Error(`Unknown grade: ${grade}`); + if (currentIndex === -1) { + console.warn(`[level] Unknown grade received: ${grade}`); + return 0; + }src/components/home/level.tsx (1)
4-4: 계산 로직을 헬퍼로 분리한 점 좋습니다뷰와 로직 분리가 잘 되어 유지보수성이 좋아졌습니다.
src/components/dateCourse/placeButton.tsx (1)
9-9: 텍스트 정렬 변경(LGTM)label을 가운데 정렬한 변경은 주변 아이콘과의 정렬 일관성 측면에서 자연스럽습니다. 기능 영향도 없습니다.
src/api/axiosInstance.ts (1)
47-61: 전역 localStorage.clear 사용 범위 재검토 요청다음 두 곳에서
localStorage.clear()가 호출되고 있습니다. 이로 인해 ‘signature’ 등 비인증 로컬 데이터까지 모두 삭제될 수 있으니, 전역 삭제가 분명한 정책인지 확인해 주세요.• src/components/modal/SettingModal.tsx (27–31)
• src/api/axiosInstance.ts (47, 51, 55, 60)현재 코드베이스에서 ‘signature’ 키 사용처는 찾지 못했습니다.
전역 삭제가 정책상 의도된 동작이 아니라면, 인증 관련 네임스페이스 키만 삭제하는clearAuthStorage(keys: string[])유틸 적용을 권장드립니다. 필요하시다면 예시 패치를 제공해 드리겠습니다.src/app/routeChangeReset.tsx (2)
7-14: 스코프 판별 로직 적절 — 경계 이동 시에만 초기화
pathname이 스코프 밖↔안 혹은 A↔B로 바뀌는 경우에만 초기화하는 로직이 과도한 리셋을 방지해 적절합니다.
7-13: SCOPES에 정의된 경로가 코드베이스에서 발견되지 않음 – 실제 라우트 구조 확인 필요자동 스크립트로 아래를 검사했으나 모두 미발견되었습니다.
path: "/makeCourse"/"/dateCourse"/"/findCourse"매칭src/app아래 해당 이름의 디렉터리(.tsx 포함)SCOPES에 정의된 경로가 실제 라우트(파일 시스템 기반 또는 설정된 라우팅)와 일치하는지 다시 한번 검토해 주세요.
src/layout/layout.tsx (1)
14-15: 레이아웃에 사이드이펙트 컴포넌트 추가 적절
<RouteChangeReset />는 UI에 영향이 없고 위치에 영향 받지 않는 사이드이펙트이므로 Footer 아래 배치는 문제 없습니다.package.json (1)
30-31: paramsSerializer 적용 확인 및 서버 기대 형식 검증 필요
- src/api/course/course.ts:86 에서
qs.stringify(params, { arrayFormat: 'repeat' })가 paramsSerializer로 설정되어 있는 것을 확인했습니다.- 백엔드가 실제로
repeat형식(param=a¶m=a)을 지원/기대하는지 문서나 담당자에게 꼭 확인해 주세요.- 이후 여러 API 호출에 동일 설정이 필요하다면, axiosInstance 레벨에서 기본
paramsSerializer로 리팩터링하는 것도 고려해 보세요.src/hooks/course/useGetCourse.ts (1)
23-27: 요청 파라미터 포함 확인됨
getDateCourse함수에 이미dateDurationTime과startTime이 API 요청의params로 전달되고 있어, 캐시 파편화나 필터 누락 우려가 없습니다.
- src/api/course/course.ts
- 라인 35: 파라미터로
dateDurationTime선언- 라인 38: 파라미터로
startTime선언- 라인 44, 48:
axiosInstance.get호출 시params에dateDurationTime,startTime포함따라서 해당 리뷰 코멘트는 적용되지 않습니다.
Likely an incorrect or invalid review comment.
src/pages/dateCourse/FindDateCourse.tsx (1)
6-6: 확인 완료: 파일명과 import 경로 대소문자 일치
src/components/common/graySvgButton.tsx파일명과 모든 import 경로에서 사용된graySvgButton이 정확히 일치함을 확인했습니다. 빌드 오류 없이 정상 동작합니다.src/components/dateCourse/info.tsx (1)
12-12: MealTag 대소문자 케이스 일치 여부 확인 요청TInfo 타입 정의와 컴포넌트에서 사용하는 프로퍼티 이름이 일치하는 것은 아래와 같습니다.
다만, 실제 API 응답 필드명이MealTag(대문자 M)로 내려오는지 최종 확인이 필요합니다.
- src/types/dateCourse/dateCourse.ts:
MealTag: string[]- src/components/dateCourse/info.tsx:
{ …, MealTag, … }: TInfo만약 API가
mealTag(소문자 m)로 내려온다면, 매핑 로직 혹은 타입·컴포넌트의 필드명을 일치시켜 런타임 오류를 방지해주세요.src/store/useDateCourseResultStore.ts (1)
30-30: setAll 병합 범위는 적절합니다.
TDataState만 병합 대상으로 제한되어 있어 스토어 액션이 덮어써질 위험이 없습니다. 현재 구현으로 충분합니다.src/pages/dateCourse/BookmarkedDateCourse.tsx (1)
50-50: 페이지네이션 end 계산은 적절합니다.
totalPages미존재 시 기본값 5로 처리되어 UI가 깨지지 않습니다.src/pages/dateCourse/MakeCourseResult.tsx (2)
29-35: Non-null 단언 연쇄 사용에 대한 확인 요청.
budget!,dateDurationTime!,transportation!,startTime!등은 값이 비어 있을 경우 런타임 오류를 일으킬 수 있습니다. 필수값 보장 여부를 재확인하거나, 안전한 기본값/유효성 검사를 적용해 주세요.필요 시 유효성 검사를 추가할 수 있습니다. 원하시면 폼 검증 스키마(Zod/Yup) 초안을 제공해 드립니다.
61-69: 빈 결과 UI는 명확합니다.실패 메시지와 재시도 안내가 사용자 친화적으로 보입니다. 이 구간은 그대로 좋습니다.
src/components/dateCourse/dateCourseSearchFilterOption.tsx (1)
89-101: 위 타입 정의에apiRequestValue는 없습니다.TDateCourseSearchFilterOption에apiRequestValue가 포함되어 있지 않아, 실제로는 어떤 타입인지 명확하지 않습니다. 따라서items배열의value필드가undefined혹은null이 될 수 있는 상황을 정확히 파악하기 어렵습니다.이 부분을 수동으로 검토해 주세요.
TDateCourseSearchFilterOption에apiRequestValue필드를 추가했는지, 혹은 다른 이름으로 정의했는지 확인apiRequestValue의 타입(nullable 여부)을 명확히 한 뒤,items생성 로직에 안전한 기본값(fallback)을 적용위 검토가 완료된 뒤, 안전장치(fallback)를 적용해 주세요.
src/pages/dateCourse/MakeCourseStep.tsx (1)
164-186: 시간 포맷 전제와의 불일치 가능성 주의검증 유틸(Budget/Meal/DateTimeStartValidation 등)은
startTime을YYYY-MM-DDTHH:mm으로 가정합니다. 현재 DateCourseSearchFilterOption에서 autoInit/입력 변경 시 포맷이 불일치하면 여기서의 검증 결과가 왜곡되거나 런타임 오류가 날 수 있습니다. 컴포넌트 포맷 수정을 함께 반영해 주세요.src/types/dateCourse/dateCourse.ts (5)
104-106: POST 요청: 제외 서명 필드 추가 방향 적합
excludedCourseSignatures: string[]로 서버의 중복 추천 방지/페이지네이션 유사 로직에 활용 가능해 보이며 설계 적절합니다.
108-109: POST 응답 단일/배열 여부 확인 필요
TPostDateCourseResponse = TCommonResponse<TDateCourse>로 단일 코스 반환을 가정하고 있습니다. 실제 API가 여러 코스를 생성/반환한다면 배열이어야 합니다.다음 중 무엇이 맞는지 BE 스펙 확인 부탁드립니다.
- export type TPostDateCourseResponse = TCommonResponse<TDateCourse>; + export type TPostDateCourseResponse = TCommonResponse<TDateCourse[]>;
166-173: 북마크 생성 요청/응답 타입 적절
dateCourseId중심의 심플한 계약으로 충분합니다. 응답을TCommonResponse<{ dateCourseId: number }>로 감싼 것도 일관적입니다.
183-189: 북마크 삭제 계약 간결하고 명확리소스 식별자만으로 충분하며, 응답도 간결합니다. 문제 없습니다.
191-204: 코스 조회: 페이징/카운트 필드 구성 적절
totalCount,totalPages,hasNextPage를 모두 제공해 프론트에서 유연하게 페이지네이션 구성 가능. 타입 정의 적합합니다.
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.
Review continued from previous batch...
🚨 관련 이슈
#100
✨ 변경사항
✏️ 작업 내용
😅 미완성 작업
📢 논의 사항 및 참고 사항