Skip to content

Conversation

@yeonjin719
Copy link
Member

🚨 관련 이슈

#100

✨ 변경사항

  • 🐞 BugFix Something isn't working
  • 💻 CrossBrowsing Browser compatibility
  • 🌏 Deploy Deploy
  • 🎨 Design Markup & styling
  • 📃 Docs Documentation writing and editing (README.md, etc.)
  • ✨ Feature Feature
  • 🔨 Refactor Code refactoring
  • ⚙️ Setting Development environment setup
  • ✅ Test Test related (storybook, jest, etc.)

✏️ 작업 내용

  • 데이트 코스 생성
  • 데이트 코스 조회

😅 미완성 작업

  • 북마크, 데이크 코스 조회 API 500 에러로 테스트 못해봤습니다

📢 논의 사항 및 참고 사항

@yeonjin719 yeonjin719 self-assigned this Aug 15, 2025
@yeonjin719 yeonjin719 linked an issue Aug 15, 2025 that may be closed by this pull request
1 task
@coderabbitai
Copy link

coderabbitai bot commented Aug 15, 2025

Caution

Review failed

The pull request is closed.

Summary by CodeRabbit

  • New Features

    • 데이트 코스 북마크 생성/토글/삭제 지원 및 모달 연동
    • 필터 전역 저장(페이지 간 유지) 및 스코프 이동 시 자동 초기화
    • 코스/북마크 목록 API 연동, 페이지네이션, 총 개수 표시
    • 코스 만들기 서버 제출/결과 저장, 재시도 흐름 추가
    • 타임라인 정보 확장(이미지, 대표 메뉴, 카테고리, 도로명 주소, 평균가)
    • 사용자명 기반 헤더/로딩 문구 표시, 뒤로가기 버튼 추가
  • UX/Style

    • 페이지 네비게이터 비활성화 상태 제공, 라벨 정렬 개선
    • 일부 설정 페이지 상단 헤더 제거
  • Security

    • 로그아웃 및 토큰 재발급 실패 시 로컬 저장소 전체 정리
  • Chores

    • 쿼리 파라미터 직렬화 라이브러리 추가

Walkthrough

데이트코스 도메인 전반(타입·상수·검증·유틸), API 엔드포인트·쿼리키·훅·뮤테이션, Zustand 기반 필터/결과 저장소, 컴포넌트·페이지 연동 및 북마크 흐름, 라우트 경계 기반 필터 리셋·로컬키 정리, 로그아웃 시 localStorage 전체 정리, 그리고 qs 의존성 추가가 포함된 대규모 변경입니다.

Changes

Cohort / File(s) Summary
Dependencies
package.json
런타임 qs(^6.14.0) 및 dev @types/qs 추가.
Axios / Logout cleanup
src/api/axiosInstance.ts, src/components/modal/SettingModal.tsx
토큰 리프레시 실패 및 로그아웃 이후 localStorage.clear() 호출 추가.
Course API
src/api/course/course.ts
데이트코스 관련 API 함수 추가: postDateCourse, postBookmark, postMakeBookmark, deleteBookmark, getDateCourse, getBookmarkedDateCourse. 쿼리 직렬화에 qs 사용(arrayFormat: 'repeat').
Types: DateCourse domain
src/types/dateCourse/dateCourse.ts
TTimeline 확장, 검색/응답/북마크 관련 다수 타입 추가·수정(필터·요청·응답 등).
Query Keys
src/queryKey/queryKey.ts
dateCourseKeys 추가 (getDateCourse, getBookmarkedDateCourse).
Hooks (queries/mutations)
src/hooks/course/*
useGetCourse, useGetBookmarkedCourse, useCourse, useBookmark 훅 추가(쿼리/뮤테이션 래핑).
Stores (Zustand)
src/store/useFilterStore.ts, src/store/useDateCourseResultStore.ts
필터 영속 저장소(useFilterStore)와 결과 저장소(useDateCourseResultStore) 추가(필드, setField, reset, setAll).
Pages → 데이터 연동
src/pages/dateCourse/*
Find/Bookmarked/CoursePage/MakeCourseStep/MakeCourseResult 등 정적 목록을 API 기반 데이터 드리븐 렌더링으로 전환, 필터 연동·페이징·생성/재생성 흐름 통합.
Components: DateCourse 관련
src/components/dateCourse/*, src/components/modal/*
DateCourse, Timeline, Info, InfoElement, PlaceButton 등 props·렌더링 모델 전환, 북마크 생성/삭제 흐름 추가, DeleteBookmarkModaldateCourseId prop 및 비동기 삭제 로직 적용.
Filter UI / Modal
src/components/dateCourse/dateCourseSearchFilterOption.tsx, src/components/modal/dateCourseSearchFilterModal.tsx
apiRequestValue/autoInit 도입, store 기반 값 바인딩 및 검증 로직 중앙화, useGetCourse/useGetBookmarkedCourse 연동.
Validation / Constants / Level
src/utils/dateCourseValidation.tsx, src/constants/dateCourseQuestion.ts, src/constants/level.ts
시간·식사 키를 열거형 키로 변경·매핑(mealTimeKorean, timeMap 등), DateCourseQuestion에 apiRequestValue 추가 및 0-base id 적용, getProgressToNextGrade 추가.
SigStorage & ClearOnLeave
src/utils/appendSignature.ts, src/utils/clearStorageOnLeave.tsx, src/layout/layout.tsx, src/app/routeChangeReset.tsx
시그니처 로컬스토리지 유틸(SigStorage) 추가(마이그레이션·중복 방지), 특정 경로 이탈 시 로컬키 제거 컴포넌트(ClearOnLeave) 및 라우트 스코프 변경 시 필터 리셋 컴포넌트(RouteScopeReset) 레이아웃에 삽입.
Navigator / Home / Misc
src/components/common/navigator.tsx, src/components/home/level.tsx, 기타 페이지들
네비게이터 경계/비활성화 처리 개선, 레벨 진행도 계산 로직 변경, 로그인 에러 리셋 effect 추가, 삭제 페이지 헤더 제거, DateCourseLoading에 사용자명 반영 등 소규모 UI 변경.

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: 리스트 렌더링 / 페이지네이션 반영
Loading
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
Loading
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: 결과 스토어 업데이트 / 라우팅
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~90 minutes

Possibly related issues

Possibly related PRs

Suggested reviewers

  • yujin5959
  • Seojegyeong
  • SEBINorSEBOUT

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 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.

📥 Commits

Reviewing files that changed from the base of the PR and between 3c0131b and 4547418.

📒 Files selected for processing (26)
  • src/api/course/course.ts (2 hunks)
  • src/components/common/navigator.tsx (1 hunks)
  • src/components/dateCourse/dateCourse.tsx (5 hunks)
  • src/components/dateCourse/dateCourseLoading.tsx (1 hunks)
  • src/components/dateCourse/dateCourseSearchFilterOption.tsx (8 hunks)
  • src/components/dateCourse/info.tsx (2 hunks)
  • src/components/dateCourse/infoElement.tsx (1 hunks)
  • src/components/dateCourse/timeline.tsx (1 hunks)
  • src/components/home/level.tsx (2 hunks)
  • src/components/modal/dateCourseSearchFilterModal.tsx (2 hunks)
  • src/constants/dateCourseQuestion.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/auth/LoginPage.tsx (2 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 (3 hunks)
  • src/pages/setting/DeleteConfirmPage.tsx (0 hunks)
  • src/pages/setting/DeleteReasonPage.tsx.tsx (0 hunks)
  • src/store/useDateCourseResultStore.ts (1 hunks)
  • src/types/dateCourse/dateCourse.ts (6 hunks)
  • src/utils/clearStorageOnLeave.tsx (1 hunks)
  • src/utils/dateCourseValidation.tsx (6 hunks)
✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a 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.

📥 Commits

Reviewing files that changed from the base of the PR and between 646e1eb and 3c0131b.

⛔ Files ignored due to path filters (2)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
  • yarn.lock is 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/qsdevDependencies에만 존재합니다.

추가 검토 및 조치 항목:

  • 번들 사이즈 영향 분석(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.getBookmarkedDateCoursequeryKeydateDurationTime, 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.

Copy link

@coderabbitai coderabbitai bot left a 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...

Copy link

@coderabbitai coderabbitai bot left a 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.ts 29–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: TKeywordButtonPropstag?: 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.

📥 Commits

Reviewing files that changed from the base of the PR and between 646e1eb and 3c0131b.

⛔ Files ignored due to path filters (2)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
  • yarn.lock is 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&param=a)을 지원/기대하는지 문서나 담당자에게 꼭 확인해 주세요.
  • 이후 여러 API 호출에 동일 설정이 필요하다면, axiosInstance 레벨에서 기본 paramsSerializer로 리팩터링하는 것도 고려해 보세요.
src/hooks/course/useGetCourse.ts (1)

23-27: 요청 파라미터 포함 확인됨
getDateCourse 함수에 이미 dateDurationTimestartTime이 API 요청의 params로 전달되고 있어, 캐시 파편화나 필터 누락 우려가 없습니다.

  • src/api/course/course.ts
    • 라인 35: 파라미터로 dateDurationTime 선언
    • 라인 38: 파라미터로 startTime 선언
    • 라인 44, 48: axiosInstance.get 호출 시 paramsdateDurationTime, 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는 없습니다. TDateCourseSearchFilterOptionapiRequestValue가 포함되어 있지 않아, 실제로는 어떤 타입인지 명확하지 않습니다. 따라서 items 배열의 value 필드가 undefined 혹은 null이 될 수 있는 상황을 정확히 파악하기 어렵습니다.

이 부분을 수동으로 검토해 주세요.

  • TDateCourseSearchFilterOptionapiRequestValue 필드를 추가했는지, 혹은 다른 이름으로 정의했는지 확인
  • apiRequestValue의 타입(nullable 여부)을 명확히 한 뒤, items 생성 로직에 안전한 기본값(fallback)을 적용

위 검토가 완료된 뒤, 안전장치(fallback)를 적용해 주세요.

src/pages/dateCourse/MakeCourseStep.tsx (1)

164-186: 시간 포맷 전제와의 불일치 가능성 주의

검증 유틸(Budget/Meal/DateTimeStartValidation 등)은 startTimeYYYY-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를 모두 제공해 프론트에서 유연하게 페이지네이션 구성 가능. 타입 정의 적합합니다.

Copy link

@coderabbitai coderabbitai bot left a 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...

@yeonjin719 yeonjin719 merged commit 0d5bdad into develop Aug 15, 2025
2 checks passed
@yeonjin719 yeonjin719 deleted the feature/#100 branch August 15, 2025 13:08
@coderabbitai coderabbitai bot mentioned this pull request Aug 15, 2025
10 tasks
@yeonjin719 yeonjin719 changed the title [Feature/#100] 데이트 코스 관련 API 연동 [Feature] 데이트 코스 관련 API 연동 Aug 18, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

✨ [Feature] 데이트코스 API 연동

2 participants