-
Notifications
You must be signed in to change notification settings - Fork 2
[Feature] 2차 피드백 반영 #97
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
WalkthroughFCM 디바이스 토큰 관리를 Provider/Context로 도입하고 Firebase 토큰 API를 재구성했습니다. React Query의 useCoreMutation에 낙관적 업데이트·롤백·무효화 흐름이 추가되었고, 쿼리 키 체계가 재정비되며 관련 훅·컴포넌트들이 이에 맞춰 수정되었습니다. eslint no-console 규칙도 추가되었습니다. Changes
Sequence Diagram(s)sequenceDiagram
participant User as User
participant Home as HomePage
participant DTP as DeviceTokenProvider
participant SW as ServiceWorker
participant FCM as FirebaseMessaging
participant BE as Backend(Alarm)
User->>Home: 첫 포인터 인터랙션
Home->>DTP: requestAndRegister()
DTP->>SW: 서비스워커 등록 요청
DTP->>FCM: generateToken()
FCM-->>DTP: deviceToken
DTP->>BE: postDeviceToken(deviceToken)
BE-->>DTP: 200 OK
Note over DTP: 포그라운드 메시지 수신 시 refetchKeys 무효화 및 onForegroundMessage 호출
sequenceDiagram
participant AX as axiosInstance
participant API as API Server
participant Auth as logout()
AX->>API: 요청
API-->>AX: 401 / 토큰 에러
AX->>AX: 리프레시 시도
alt 리프레시 실패 또는 토큰 없음/만료
AX->>Auth: logout()
AX->>AX: console.error 기록
else 리프레시 성공
AX->>API: 원래 요청 재시도
end
sequenceDiagram
participant UI as Component
participant useMut as useCoreMutation
participant QC as QueryClient
participant API as MutationFn
UI->>useMut: mutate(vars)
alt optimisticUpdate 존재
useMut->>QC: cancelQueries(key)
useMut->>QC: prev = getQueryData(key)
useMut->>QC: setQueryData(key, updateFn(prev, vars))
end
useMut->>API: 호출
alt 실패
useMut->>QC: setQueryData(key, prev) /* 롤백 */
useMut->>UI: userOnError(...)
else 성공
useMut->>QC: invalidateQueries(invalidateKeys)
useMut->>UI: userOnSuccess(...)
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes 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 settings in your CodeRabbit configuration. 📒 Files selected for processing (13)
✨ 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: 23
🔭 Outside diff range comments (4)
src/components/settingTab/InfoSetting.tsx (2)
33-37: 읽기 전용 onChange는 빈 함수 대신 명시적 no-op 사용 권장Line 36의 빈 함수는 린트 경고 가능성과 의도 전달이 약합니다. 명시적으로 no-op임을 드러내면서 타입도 만족하도록 변경을 제안합니다. 또한 가능하다면 EditableInputBox에 readOnly/disabled 지원을 추가해 시맨틱하게 처리하는 것도 고려해 주세요.
적용 diff:
- onChange={() => {}} // 읽기 전용 + onChange={(_e) => undefined} // 읽기 전용추가 제안(컴포넌트 개선): EditableInputBox에
readOnly?: boolean또는disabled?: booleanprop을 추가한 뒤 내부 input에 반영하면 접근성과 사용성이 좋아집니다.
45-45: Tailwind 클래스 오타 의심: rounding-32 → rounded-[32px]Tailwind 기본 규칙은
rounded-*입니다.rounding-32는 적용되지 않을 가능성이 높습니다. 의도대로 모서리 라운드를 주려면 아래처럼 수정하세요.적용 diff:
- <button className="bg-primary-500 px-6 py-2 rounding-32 text-white font-body1">취향 데이터 초기화</button> + <button className="bg-primary-500 px-6 py-2 rounded-[32px] text-white font-body1">취향 데이터 초기화</button>(프로젝트에서 토큰화된 유틸리티를 사용 중이라면 해당 네이밍에 맞춰
rounded-3xl등으로 교체해도 됩니다.)src/api/axiosInstance.ts (2)
41-46: catch 블록에서 잘못된 변수 참조로 조건 분기 오동작 가능axios.isAxiosError(errors)로 판별한 뒤에도 refreshError를 error로 캐스팅하고 있어, 재발급 요청의 에러가 아니라 원본 요청 에러를 기준으로 분기될 수 있습니다.
다음과 같이 수정해 주세요.
- if (axios.isAxiosError(errors)) { - const refreshError = error as AxiosError<IRefreshResponse>; + if (axios.isAxiosError(errors)) { + const refreshError = errors as AxiosError<IRefreshResponse>;
36-41: 토큰 재발급 실패(code !== '200') 시 isRedirecting 원복 및 로그아웃 누락refresh()가 에러를 던지지 않고 code !== '200'을 반환하는 경우, isRedirecting이 false로 복구되지 않고, 사용자 세션 정리도 수행되지 않습니다. 이 상태에서는 이후 401 응답에 대해 재시도가 차단될 수 있습니다.
다음과 같이 실패 분기를 명시해 주세요.
const refreshResponse = await refresh(); if (refreshResponse.code === '200') { isRedirecting = false; return axiosInstance(error.config); } + // 재발급 실패 분기 처리 + isRedirecting = false; + console.error('refreshToken 재발급 실패. 로그아웃 처리합니다.'); + logout(); + return Promise.reject(error);
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (21)
eslint.config.mjs(1 hunks)src/App.tsx(1 hunks)src/api/axiosInstance.ts(1 hunks)src/components/common/PasswordEdit.tsx(0 hunks)src/components/dateCourse/dateCourse.tsx(0 hunks)src/components/dateCourse/dateCourseSearchFilterOption.tsx(1 hunks)src/components/modal/dateCourseSearchFilterModal.tsx(0 hunks)src/components/settingTab/InfoSetting.tsx(1 hunks)src/firebase/firebase.ts(3 hunks)src/hooks/alarm/useDeviceToken.ts(0 hunks)src/hooks/alarm/usePostDeviceToken.ts(1 hunks)src/hooks/customQuery.ts(2 hunks)src/pages/TestInputPage.tsx(2 hunks)src/pages/auth/LoginPage.tsx(1 hunks)src/pages/auth/UserSetting.tsx(1 hunks)src/pages/home/HomePage.tsx(2 hunks)src/pages/notice/Notice.tsx(2 hunks)src/pages/question/Question.tsx(1 hunks)src/providers/deviceTokenProvider.tsx(1 hunks)src/queryKey/queryKey.ts(1 hunks)src/types/common/common.ts(1 hunks)
💤 Files with no reviewable changes (4)
- src/components/common/PasswordEdit.tsx
- src/components/dateCourse/dateCourse.tsx
- src/components/modal/dateCourseSearchFilterModal.tsx
- src/hooks/alarm/useDeviceToken.ts
🧰 Additional context used
🧬 Code Graph Analysis (14)
src/components/settingTab/InfoSetting.tsx (1)
src/components/common/EditableInputBox.tsx (2)
EditableInputBox(20-126)IEditableInputBoxProps(6-18)
src/pages/auth/UserSetting.tsx (3)
src/pages/auth/FindPw.tsx (2)
err(117-119)err(91-95)src/pages/RedirectPage.tsx (1)
err(39-45)src/pages/auth/RedirectPage.tsx (1)
err(42-48)
src/pages/TestInputPage.tsx (1)
src/components/common/EditableInputBox.tsx (2)
e(47-51)EditableInputBox(20-126)
src/hooks/alarm/usePostDeviceToken.ts (2)
src/hooks/alarm/useDeviceToken.ts (5)
useEffect(8-39)setupFCM(9-38)isSupported(10-26)setupFCM(28-31)window(35-37)src/hooks/auth/useAuth.ts (1)
useAuth(5-14)
src/pages/auth/LoginPage.tsx (3)
src/pages/auth/FindPw.tsx (2)
err(117-119)data(109-116)src/pages/RedirectPage.tsx (2)
LoginRedirect(10-56)err(39-45)src/pages/auth/RedirectPage.tsx (2)
err(42-48)code(25-54)
src/pages/question/Question.tsx (1)
src/components/common/EditableInputBox.tsx (1)
e(47-51)
src/providers/deviceTokenProvider.tsx (1)
src/hooks/alarm/useDeviceToken.ts (5)
useEffect(8-39)setupFCM(9-38)isSupported(10-26)setupFCM(28-31)window(35-37)
src/api/axiosInstance.ts (1)
src/api/auth/auth.ts (1)
Promise(28-31)
src/pages/home/HomePage.tsx (1)
src/hooks/alarm/useDeviceToken.ts (4)
window(35-37)setupFCM(9-38)setupFCM(28-31)isSupported(10-26)
src/pages/notice/Notice.tsx (1)
src/components/common/EditableInputBox.tsx (1)
e(47-51)
src/firebase/firebase.ts (2)
src/hooks/alarm/useDeviceToken.ts (4)
isSupported(10-26)setupFCM(9-38)useEffect(8-39)setupFCM(28-31)public/firebase-messaging-sw.js (3)
event(19-34)event(36-42)error(41-41)
src/queryKey/queryKey.ts (2)
src/hooks/alarm/useGetAlarm.ts (2)
TRequestGetAlarm(8-15)cursor(11-11)src/api/alarm/alarm.ts (1)
TRequestGetAlarm(5-13)
src/hooks/customQuery.ts (3)
src/hooks/auth/useAuth.ts (1)
useAuth(5-14)src/hooks/alarm/useGetAlarm.ts (1)
TRequestGetAlarm(8-15)src/hooks/course/useSearchRegion.tsx (1)
useSearchRegion(6-11)
src/types/common/common.ts (1)
src/hooks/auth/useAuth.ts (1)
useAuth(5-14)
🪛 Biome (2.1.2)
src/providers/deviceTokenProvider.tsx
[error] 71-71: This hook is being called conditionally, but all hooks must be called in the exact same order in every component render.
For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level
(lint/correctness/useHookAtTopLevel)
🔇 Additional comments (12)
src/pages/notice/Notice.tsx (1)
46-46: 에러 로깅을 console.error로 변경 — 적절합니다no-console 규칙과 에러 로깅의 목적에 부합합니다.
eslint.config.mjs (1)
37-37: no-console 규칙 추가 — 적절한 기본선입니다warn/error만 허용하는 구성이 실제 운영 로그 정책에 부합하며, 이번 PR 전반의 수정과 정합적입니다.
src/firebase/firebase.ts (1)
24-44: isSupported 가드와 권한 처리 흐름 정돈 잘 되었습니다지원 브라우저 가드, 권한 요청, SW registration 전달까지 전체 플로우가 명확해졌습니다. 삭제 API 추가도 일관성 있습니다.
src/types/common/common.ts (1)
17-21: 낙관적 업데이트 모델과 queryKey 제거 방향성 좋습니다
- TOptimisticUpdate로 업데이트 함수를 명확히 모델링한 점, UseQuery 옵션에서 queryKey를 분리한 점 모두 합리적입니다.
Also applies to: 37-40
src/pages/auth/LoginPage.tsx (1)
58-60: 에러 로깅 레벨 전환(LV: error) 적절합니다no-console 규칙과 일치하며, 사용자 메시지는 별도로 처리되어 UX도 유지됩니다.
src/App.tsx (1)
12-15: 전역 Provider 래핑 및 refetchKeys 연결 좋습니다알람 쿼리키를 Provider에 주입해 포그라운드 수신 시 무효화하는 흐름이 명확합니다. Home에서의 컨텍스트 소비와도 일관됩니다.
src/pages/home/HomePage.tsx (2)
17-28: 유저 제스처 기반 1회성 등록 흐름 적절합니다
pointerdown+{ once: true }로 권한/토큰 등록을 유저 제스처 뒤에 한 번만 트리거하는 방식이 안정적입니다. 에러 로깅도 적절합니다.
17-28: no-console 규칙 설정 확인 요청파일: src/pages/home/HomePage.tsx (17–28)
console.error사용 부분이 ESLintno-console규칙에서error/warn를 허용하는지 확인 부탁드립니다. 미허용 시 로거 유틸로 대체를 권장드립니다.- Nit:
{ once: true }옵션을 사용하므로removeEventListener호출은 실질적으로 불필요해 간결화를 위해 제거를 고려하셔도 좋습니다.src/queryKey/queryKey.ts (1)
4-12: 쿼리키 객체화 일관성 좋습니다
UseQueryOptions에 맞춘{ queryKey }형태로의 정비가 타입 안전성과 가독성을 높입니다.src/hooks/customQuery.ts (3)
21-24: 제네릭 타입 정의가 개선되었습니다!
TData와TVariables를 사용한 제네릭 타입 정의와 확장된 옵션 인터페이스가 타입 안정성을 높입니다.
38-44: 낙관적 업데이트 구현이 적절합니다쿼리 취소, 이전 데이터 캡처, 업데이트 적용 흐름이 올바르게 구현되었습니다.
46-54: 에러 처리 로직이 견고합니다낙관적 업데이트 롤백과 사용자 콜백 위임이 적절히 구현되었습니다.
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: 10
🔭 Outside diff range comments (5)
src/api/axiosInstance.ts (2)
26-61: 에러 삼킴 및 isRedirecting 누수: try/catch/finally로 정리하고 초기 분기에서 reject 반환현재 '/' 또는 '/signup' 경로에서 단순
return으로 에러가 사라지고(호출자가 응답을 받지 못함), refresh 실패(비-200)나 예외 시isRedirecting이false로 복원되지 않아 이후 요청이 영구적으로 가드에 막힐 수 있습니다. 아래처럼 수정해 주세요.isRedirecting = true; try { const currentUrl = window.location.pathname; if (currentUrl === '/' || currentUrl === '/signup') { - isRedirecting = false; - return; + return Promise.reject(error); } const refreshResponse = await refresh(); if (refreshResponse.code === '200') { - isRedirecting = false; - return axiosInstance(error.config); } - } catch (errors) { + } catch (errors) { if (axios.isAxiosError(errors)) { const refreshError = error as AxiosError<IRefreshResponse>; if (refreshError.response?.data.message === 'The token is null.') { console.error('refreshToken이 없습니다. 로그인 페이지로 이동합니다.'); logout(); } else if (refreshError.response?.data.message === 'The token is invalid.') { console.error('refreshToken이 만료되었습니다. 로그인 페이지로 이동합니다.'); logout(); } else { console.error('알 수 없는 오류가 발생했습니다', errors); logout(); } } else { console.error('알 수 없는 오류가 발생했습니다', errors); logout(); } return Promise.reject(errors); - } + } finally { + isRedirecting = false; + }
36-40: Non-200 refresh 응답 처리 재검토 권장
refresh()가 예외를 던지지 않고 비-200 형태로 실패를 표현하는 경우(예: 200 OK에 business code 4xx), 현재는 아무 처리 없이 외부return Promise.reject(error)흐름으로만 빠집니다. 명시적으로 실패 케이스를 로깅/로그아웃 처리하거나, 최소한 이유를 남겨 디버깅 가능성을 높이는 것을 권장합니다. 위의 try/catch/finally 정리와 함께 고려 부탁드립니다.src/firebase/firebase.ts (1)
46-53: SSR/비지원 환경 가드 보강: navigator 접근 전 window 체크SSR 환경에서
navigator자체 접근이 ReferenceError를 유발할 수 있습니다.typeof window !== 'undefined'로 감싸주세요.export const registerServiceWorker = async () => { try { - if ('serviceWorker' in navigator) { - await navigator.serviceWorker.register('/firebase-messaging-sw.js'); - } + if (typeof window !== 'undefined' && 'serviceWorker' in navigator) { + await navigator.serviceWorker.register('/firebase-messaging-sw.js'); + } } catch (err) { console.error('Service Worker registration failed:', err); } };src/hooks/customQuery.ts (2)
13-18: staleTime 기본값이 옵션을 덮어씁니다현재 구성에서는 전달된 options.staleTime이 항상 5분으로 덮어씌워집니다. 기본값만 제공하고, 옵션이 있으면 그대로 존중하도록 변경하세요.
적용 diff:
return useQuery({ queryKey: keyName, queryFn: query, - ...options, - staleTime: 1000 * 60 * 5, + ...options, + staleTime: options?.staleTime ?? 1000 * 60 * 5, });
8-12: useCoreQuery 반환 에러 타입 일관성 유지(TResponseError 사용)전역 에러 타입(TResponseError)과 반환 타입(UseQueryResult<TData, AxiosError>)이 불일치합니다. 일관되게 TResponseError를 사용하세요.
적용 diff:
-import type { TUseMutationCustomOptions, TUseQueryCustomOptions } from '@/types/common/common'; +import type { TUseMutationCustomOptions, TUseQueryCustomOptions, TResponseError } from '@/types/common/common'; ... -export function useCoreQuery<TQueryFnData, TData = TQueryFnData>( +export function useCoreQuery<TQueryFnData, TData = TQueryFnData>( keyName: QueryKey, query: QueryFunction<TQueryFnData, QueryKey>, options?: TUseQueryCustomOptions<TQueryFnData, TData>, -): UseQueryResult<TData, AxiosError> { +): UseQueryResult<TData, TResponseError> {
♻️ Duplicate comments (6)
src/api/axiosInstance.ts (2)
45-46: 이전 코멘트 반영 확인: refreshToken 부재 시 즉시 로그아웃 처리 적용됨이제 'The token is null.' 분기에서 logout이 호출되어 세션 정리가 보장됩니다. 좋습니다.
51-53: 이전 코멘트 반영 확인: 인터셉터에서 alert 제거 및 분기 단순화 완료비밀번호 오류 분기가 제거되고, 알 수 없는 오류는 일괄적으로 로그아웃 처리로 단순화되었습니다. 의도에 부합합니다.
src/hooks/alarm/usePostDeviceToken.ts (1)
5-8: 네이밍 일관성 확보 권장: 훅명과 파일명 정렬파일명이
usePostDeviceToken.ts인데 export 훅명이useFirebase입니다. 유지보수/탐색성을 위해 훅명을 파일명과 맞추는 것을 권장합니다. 이전 리뷰에서도 같은 제안을 드렸습니다.다음과 같이 변경 제안:
-export function useFirebase() { +export function usePostDeviceToken() { const postDeviceTokenMutation = useCoreMutation(postDeviceToken); return { postDeviceTokenMutation }; }사용처 업데이트 예시(파일 외 참고용):
// src/providers/deviceTokenProvider.tsx -import { useFirebase } from '@/hooks/alarm/usePostDeviceToken'; +import { usePostDeviceToken } from '@/hooks/alarm/usePostDeviceToken'; -const { postDeviceTokenMutation } = useFirebase(); +const { postDeviceTokenMutation } = usePostDeviceToken();src/firebase/firebase.ts (2)
18-22: SSR 안정성: 모듈 로드시 IIFE 초기화 제거하고 지연 초기화로 전환모듈 로드 시점의 즉시 실행 IIFE에서
isSupported()를 호출하면 SSR 환경에서window/self부재로 크래시 위험이 있습니다. 지연 초기화 헬퍼로 전환해 주세요.다음과 같이 수정 제안:
export let messaging: Messaging | null = null; -(async () => { - if (await isSupported()) { - messaging = getMessaging(app); - } -})(); + +export async function ensureMessaging(): Promise<Messaging | null> { + if (!(await isSupported())) return null; + if (!messaging) { + messaging = getMessaging(app); + } + return messaging; +}
24-41: generateToken에서 지연 초기화 사용 및 가드 강화
messaging전제 접근 대신ensureMessaging()로 안전하게 초기화하고, 미지원/권한 거부 시 빠르게 반환하도록 정리하면 안정성이 좋아집니다.-export async function generateToken(): Promise<string | null> { - if (!(await isSupported())) return null; - if (!messaging) messaging = getMessaging(app); +export async function generateToken(): Promise<string | null> { + const m = await ensureMessaging(); + if (!m) return null; // 권한 요청 (이미 허용/거부된 상태면 브라우저가 적절히 동작) if ('Notification' in window && Notification.permission !== 'granted') { const perm = await Notification.requestPermission(); if (perm !== 'granted') return null; } try { - const token = await getToken(messaging, { + const token = await getToken(m, { vapidKey: import.meta.env.VITE_FIREBASE_VAPID_KEY, serviceWorkerRegistration: await navigator.serviceWorker.ready, }); return token ?? null; } catch (e) { console.error('FCM getToken 실패:', e); return null; } }src/providers/deviceTokenProvider.tsx (1)
93-103: LGTM: unregister 시 onMessage 구독 해제까지 처리토큰 해제 시 구독 정리와 키 무효화까지 포함되어 있어 일관성이 좋습니다. 이전 리뷰 피드백도 충실히 반영되었습니다.
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (9)
src/App.tsx(1 hunks)src/api/axiosInstance.ts(1 hunks)src/firebase/firebase.ts(3 hunks)src/hooks/alarm/usePostDeviceToken.ts(1 hunks)src/hooks/customQuery.ts(2 hunks)src/pages/auth/UserSetting.tsx(1 hunks)src/providers/deviceTokenProvider.tsx(1 hunks)src/queryKey/queryKey.ts(1 hunks)src/types/common/common.ts(1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (9)
src/hooks/alarm/usePostDeviceToken.ts (2)
src/hooks/customQuery.ts (1)
useCoreMutation(21-66)src/api/alarm/alarm.ts (1)
postDeviceToken(15-18)
src/hooks/customQuery.ts (3)
src/types/common/common.ts (1)
TUseMutationCustomOptions(22-33)src/api/queryClient.ts (1)
queryClient(3-9)src/hooks/auth/useAuth.ts (1)
useAuth(5-14)
src/App.tsx (2)
src/queryKey/queryKey.ts (1)
alarmKeys(15-24)src/providers/deviceTokenProvider.tsx (1)
DeviceTokenProvider(27-118)
src/providers/deviceTokenProvider.tsx (5)
src/api/queryClient.ts (1)
queryClient(3-9)src/hooks/alarm/usePostDeviceToken.ts (1)
useFirebase(5-8)src/api/alarm/alarm.ts (1)
postDeviceToken(15-18)src/firebase/firebase.ts (4)
messaging(17-17)registerServiceWorker(46-54)generateToken(24-44)deleteFcmToken(56-65)src/hooks/alarm/useDeviceToken.ts (5)
useEffect(8-39)setupFCM(9-38)isSupported(10-26)setupFCM(28-31)window(35-37)
src/pages/auth/UserSetting.tsx (2)
src/pages/auth/FindPw.tsx (3)
err(117-119)data(109-116)err(91-95)src/pages/auth/LoginPage.tsx (1)
err(57-60)
src/firebase/firebase.ts (2)
public/firebase-messaging-sw.js (4)
messaging(17-17)event(19-34)event(36-42)error(41-41)src/hooks/alarm/useDeviceToken.ts (4)
isSupported(10-26)setupFCM(9-38)useEffect(8-39)setupFCM(28-31)
src/api/axiosInstance.ts (1)
src/api/auth/auth.ts (2)
logout(33-36)Promise(28-31)
src/types/common/common.ts (1)
src/hooks/auth/useAuth.ts (1)
useAuth(5-14)
src/queryKey/queryKey.ts (1)
src/hooks/alarm/useGetAlarm.ts (2)
TRequestGetAlarm(8-15)cursor(11-11)
🔇 Additional comments (8)
src/App.tsx (1)
12-17: LGTM: refetchKeys 메모이제이션과 Provider 래핑이 적절합니다렌더마다 배열 아이덴티티가 달라지는 문제를 해소했고, App 루트에서 Provider로 감싼 구조도 명확합니다.
src/firebase/firebase.ts (1)
37-38: LGTM: serviceWorkerRegistration에 ready 사용
navigator.serviceWorker.ready로 활성화된 registration을 보장해 레이스를 줄였습니다. 좋은 개선입니다.src/providers/deviceTokenProvider.tsx (2)
68-82: LGTM: 뮤테이션 별칭에서 use-접두사 제거로 훅 오인 방지
{ mutate: postDeviceToken }으로 별칭을 정리해 훅 규칙 오탐 가능성을 줄였습니다.
40-41: LGTM: Notification 접근 가드 적절
'Notification' in window체크로 SSR/비지원 환경에서도 안전합니다.src/hooks/customQuery.ts (1)
54-58: 여러 키 무효화 병렬 처리 적용 확인 — 좋습니다Promise.all로 invalidate를 병렬 처리하여 불필요한 지연을 줄였습니다. 의도와 구현이 일치합니다.
src/queryKey/queryKey.ts (1)
1-3: UseQueryOptions를 ‘satisfies’로 강제하는 접근은 OK — 단, 키 생성 규칙만 준수타입 안정성 측면에서 as const + satisfies UseQueryOptions 조합은 적절합니다. 위 중복 프리픽스만 해소하면 키 구조가 견고해집니다.
src/types/common/common.ts (2)
17-20: 낙관적 업데이트 타입 정의 명확하고 확장 가능 — 좋습니다캐시/변수 제네릭을 분리한 TOptimisticUpdate 정의가 명료합니다. 이후 캐시 스키마 변경에도 안전하게 확장 가능합니다.
35-39: TUseQueryCustomOptions에서 queryKey 제외 처리 적절 — 타입 정합성 OK쿼리 훅에서 queryKey를 내부에서 관리할 수 있어 오용을 줄입니다. useCoreQuery 반환 에러 타입만 TResponseError로 맞추면 이상 없습니다.
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: 1
🔭 Outside diff range comments (1)
src/components/modal/regionModal.tsx (1)
64-79: 잠재적 런타임 오류: optional chaining 누락으로 regionList 미존재 시 크래시 가능
regionList?.result.regions.length처럼 첫 단계에만?.이 적용되어result가 없을 때.regions접근에서 크래시가 날 수 있습니다. 아래처럼 전체 체인에 안전 연산자를 적용하거나 길이의 기본값을 사용해 주세요.- {regionList?.result.regions.length === 0 && <div className="text-center py-8 text-default-gray-500">검색 결과가 없습니다</div>} - {regionList?.result.regions.map((region, index) => ( + {(regionList?.result?.regions?.length ?? 0) === 0 && <div className="text-center py-8 text-default-gray-500">검색 결과가 없습니다</div>} + {regionList?.result?.regions?.map((region, index) => ( <li key={region.regionId}> <button type="button" onClick={() => handleRegionSelect(region.regionId)} className={`w-full text-left py-3 px-4 hover:bg-primary-100 transition-colors`} > <div className="font-medium">{region.name}</div> </button> - {index < regionList.result.regions.length - 1 && <div className="border-b border-default-gray-400" />} + {index < ((regionList?.result?.regions?.length ?? 0) - 1) && <div className="border-b border-default-gray-400" />} </li> ))}보다 깔끔하게는 상단에서
const regions = regionList?.result?.regions ?? [];로 파생 변수를 만들고 JSX에서는regions만 사용하도록 리팩터링하는 것을 권장합니다.
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (1)
src/components/modal/regionModal.tsx(2 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/components/modal/regionModal.tsx (4)
src/api/queryClient.ts (1)
queryClient(3-9)src/queryKey/queryKey.ts (1)
HomeKeys(26-36)src/hooks/home/useUserRegion.ts (2)
useGetUserRegion(10-12)useUserRegion(6-8)src/api/home/region.ts (1)
TPatchUserRegionRequest(5-8)
🔇 Additional comments (2)
src/components/modal/regionModal.tsx (2)
9-11: 동일한 queryClient 싱글톤 인스턴스 사용 확인됨
- src/main.tsx에서
<QueryClientProvider client={queryClient}>가src/api/queryClient.ts의 동일한queryClient를 주입- 다른
new QueryClient()호출이나 추가 Provider 설정 없음문제 없습니다.
28-31: invalidateQueries 호출은 중복이 아닙니다useCoreMutation 훅에 별도의 invalidateKeys 옵션을 전달하지 않았기 때문에, 내부 로직에서 HomeKeys.userRegion 쿼리를 무효화하지 않습니다. 따라서 컴포넌트에서 직접 invalidateQueries를 호출해야 합니다.
선택 사항: UX 지연을 줄이기 위해 옵티미스틱 업데이트를 적용해 볼 수 있습니다. 예를 들어, onSuccess에서 응답을 먼저 캐시에 반영한 뒤 백그라운드에서 무효화하면 사용자 경험이 개선됩니다.
- onSuccess: () => { - queryClient.invalidateQueries({ queryKey: HomeKeys.userRegion().queryKey }); + onSuccess: (data) => { + // 응답 데이터를 즉시 캐시에 반영 + queryClient.setQueryData(HomeKeys.userRegion().queryKey, data); + // 백그라운드에서 최신화 + queryClient.invalidateQueries({ queryKey: HomeKeys.userRegion().queryKey }); onClose(); },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.
Actionable comments posted: 14
🔭 Outside diff range comments (3)
src/api/axiosInstance.ts (2)
28-32: 에러 인터셉터에서 빈 return은 호출부를 깨뜨립니다 — Promise.reject(error)로 반환해 주세요에러 핸들러에서
return;은 호출부로undefined를 resolve하여 예기치 않은 런타임 오류를 유발할 수 있습니다. 일관되게 에러를 거부하도록 수정이 필요합니다.- if (currentUrl === '/' || currentUrl === '/signup') { - isRedirecting = false; - return; - } + if (currentUrl === '/' || currentUrl === '/signup') { + isRedirecting = false; + return Promise.reject(error); + }
26-61: isRedirecting 복구 누락으로 리프레시 실패 시 영구 true 고착 가능 — finally에서 반드시 false로 되돌리세요리프레시가 실패하거나
code !== '200'인 경우, 현재isRedirecting이false로 복구되지 않아 이후 401 응답이 모두 우회 처리(즉시 reject)되어 재시도가 불가능해질 수 있습니다.finally에서 복구를 보장하세요.isRedirecting = true; try { const currentUrl = window.location.pathname; if (currentUrl === '/' || currentUrl === '/signup') { isRedirecting = false; - return; + return Promise.reject(error); } const refreshResponse = await refresh(); if (refreshResponse.code === '200') { - isRedirecting = false; - return axiosInstance(error.config); } } 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(); } else if (refreshError.response?.data.message === 'The token is invalid.') { console.error('refreshToken이 만료되었습니다. 로그인 페이지로 이동합니다.'); void logout(); } else { console.error('알 수 없는 오류가 발생했습니다', errors); void logout(); } } else { console.error('알 수 없는 오류가 발생했습니다', errors); void logout(); } return Promise.reject(errors); - } + } finally { + isRedirecting = false; + }src/hooks/customQuery.ts (1)
8-13: useCoreQuery 반환 에러 타입을 TResponseError로 통일쿼리 옵션 타입(
TUseQueryCustomOptions)에서 에러를TResponseError로 정의했으므로, 반환 타입도 동일하게 맞추는 것이 일관적입니다.export function useCoreQuery<TQueryFnData, TData = TQueryFnData>( keyName: QueryKey, query: QueryFunction<TQueryFnData, QueryKey>, options?: TUseQueryCustomOptions<TQueryFnData, TData>, -): UseQueryResult<TData, AxiosError> { +): UseQueryResult<TData, TResponseError> {추가로 상단 import에
TResponseError를 포함해 주세요:// 상단 import 보완 import type { TUseMutationCustomOptions, TUseQueryCustomOptions, TResponseError } from '@/types/common/common';AxiosError 타입이 더 이상 사용되지 않는다면 불필요한 import를 제거해 주세요.
♻️ Duplicate comments (13)
src/firebase/firebase.ts (3)
40-45: getToken 호출 시 serviceWorker.ready 사용 적용 확인 (좋아요)
navigator.serviceWorker.ready로 활성화된 Registration을 보장하는 방향으로 반영되었습니다. 레이스 컨디션 완화에 도움이 됩니다.
61-70: deleteFcmToken 지연 초기화 일관성 적용 (좋아요)
ensureMessaging()을 통해 안전하게 초기화 후deleteToken을 호출하도록 정리된 점 좋습니다.
23-27: 모듈 로드 시점 IIFE 제거하고 지연 초기화만 유지하세요SSR/테스트 환경에서의 안전성과 예측 가능성을 위해 IIFE는 제거하는 편이 좋습니다.
ensureMessaging()만으로 초기화를 보장해 주세요.적용 diff:
(async () => { - if (await isSupported()) { - messaging = getMessaging(app); - } -})(); +// Eager 초기화 제거: 지연 초기화는 ensureMessaging()에서만 수행src/hooks/customQuery.ts (1)
21-27: useCoreMutation 기본 에러 타입을 TResponseError로 전환전역 에러 타입(
TResponseError)과 맞추면 호출부에서도 타입 일관성이 유지됩니다.export function useCoreMutation< TData, TVariables, - TError = AxiosError<{ message?: string }>, // 필요 시 TResponseError 등으로 대체 + TError = TResponseError, TContext extends { prevData?: unknown } = { prevData?: unknown }, TCache = unknown, >(mutation: MutationFunction<TData, TVariables>, options?: TUseMutationCustomOptions<TData, TVariables, TError, TContext, TCache>) {src/pages/auth/UserSetting.tsx (1)
65-67: optional chaining 누락: data 접근 시 런타임 오류 가능
err.response?.data.message는data가 undefined인 경우 에러가 납니다.data에도 optional chaining을 적용하세요.- setError(err.response?.data.message ?? '회원가입 중 문제가 발생했습니다.'); + setError(err.response?.data?.message ?? '회원가입 중 문제가 발생했습니다.');src/App.tsx (1)
3-3: refetchKeys 메모이제이션 반영 좋습니다이전 피드백대로 배열 리터럴을
useMemo로 고정해 재생성을 줄인 점 좋습니다. 상단 import(useMemo)도 적절합니다.Also applies to: 11-11
src/components/modal/regionModal.tsx (1)
29-30: Promise 무시 의도 명확화(Lint 친화) 반영 확인
invalidateQueries앞에void를 붙여 no-floating-promises 관련 경고를 방지한 점 좋습니다. 성공 콜백 내 흐름도 자연스럽습니다.src/providers/deviceTokenProvider.tsx (4)
34-36: 네이밍/훅 규칙 경고 해소 반영 확인뮤테이션 별칭을
postDeviceToken으로 정리해 훅 규칙 경고가 나지 않도록 한 점 좋습니다.
40-41: Notification 존재 가드 추가 반영 확인
'Notification' in window가드를 통해 SSR/비지원 환경에서의ReferenceError를 예방한 점 좋습니다.Also applies to: 66-66
91-91: 의존성 배열에 postDeviceToken 포함 반영 확인
requestAndRegister가 캡처하는postDeviceToken을 deps에 포함시켜 안정성을 높인 점 좋습니다.
21-25: onForegroundMessage payload 타입 구체화 권장콜백 payload를
unknown대신 Firebase 타입으로 지정하면 사용처에서 안전한 접근이 가능합니다.아래와 같이 타입을 명시해 주세요.
-import { isSupported, onMessage } from 'firebase/messaging'; +import { isSupported, onMessage, type MessagePayload } from 'firebase/messaging'; ... - onForegroundMessage?: (payload: unknown) => void; + onForegroundMessage?: (payload: MessagePayload) => void;src/queryKey/queryKey.ts (2)
4-6: createQueryKeys 자동 접두사로 인한 중복 키(Region) 제거 필요createQueryKeys는 ['region', 'search', ...]처럼 루트/메서드명을 자동으로 붙입니다. 현재 구현은 내부에서 'search'를 다시 넣어 ['region', 'search', 'search', keyword] 형태로 중복됩니다. 캐시 키 불일치 및 무효화 이슈를 유발할 수 있습니다.
아래처럼 파라미터만 반환하도록 수정해 주세요.
- search: (keyword: string) => ['search', keyword], + // 최종 키: ['region', 'search', keyword] + search: (keyword: string) => [keyword] as const,
9-11: createQueryKeys 자동 접두사로 인한 중복 키(Alarm) 제거 + cursor 표현 안정화 제안여기도 동일하게 메서드명이 자동으로 붙으므로 내부에서 'getAlarm'을 중복해서 넣으면 키가 ['alarm', 'getAlarm', 'getAlarm', size, cursor]가 됩니다. 파라미터만 반환하도록 바꿔 주세요.
또한 선택 파라미터(cursor)의 안정적 표현을 위해 객체 하나로 감싸는 방식도 고려해 볼 만합니다.
- 최소 수정안
- getAlarm: (size: number, cursor?: number) => ['getAlarm', size, cursor], + // 최종 키: ['alarm', 'getAlarm', size, cursor] + getAlarm: (size: number, cursor?: number) => [size, cursor] as const,
- 선택 개선안(추천, 키 구조 안정화 및 가독성 향상)
- getAlarm: (size: number, cursor?: number) => ['getAlarm', size, cursor], + // 최종 키: ['alarm', 'getAlarm', { size, cursor }] + getAlarm: (params: { size: number; cursor?: number }) => [params] as const,
📜 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 settings in your CodeRabbit configuration.
📒 Files selected for processing (12)
src/App.tsx(1 hunks)src/api/axiosInstance.ts(1 hunks)src/components/common/PasswordEdit.tsx(1 hunks)src/components/modal/regionModal.tsx(2 hunks)src/firebase/firebase.ts(3 hunks)src/hooks/auth/useAccount.ts(2 hunks)src/hooks/customQuery.ts(2 hunks)src/pages/auth/UserSetting.tsx(1 hunks)src/pages/home/HomePage.tsx(2 hunks)src/providers/deviceTokenProvider.tsx(1 hunks)src/queryKey/queryKey.ts(1 hunks)src/types/common/common.ts(1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (10)
src/components/modal/regionModal.tsx (5)
src/api/queryClient.ts (1)
queryClient(3-9)src/queryKey/queryKey.ts (1)
HomeKeys(13-23)src/hooks/home/useUserRegion.ts (2)
useGetUserRegion(10-12)useUserRegion(6-8)src/hooks/course/useSearchRegion.tsx (1)
useSearchRegion(6-11)src/api/home/region.ts (1)
TPatchUserRegionRequest(5-8)
src/pages/auth/UserSetting.tsx (2)
src/pages/auth/FindPw.tsx (2)
err(117-119)data(109-116)src/pages/auth/LoginPage.tsx (1)
err(57-60)
src/firebase/firebase.ts (2)
public/firebase-messaging-sw.js (4)
messaging(17-17)event(19-34)event(36-42)error(41-41)src/hooks/alarm/useDeviceToken.ts (4)
isSupported(10-26)setupFCM(9-38)useEffect(8-39)setupFCM(28-31)
src/api/axiosInstance.ts (1)
src/api/auth/auth.ts (2)
logout(33-36)Promise(28-31)
src/hooks/auth/useAccount.ts (4)
src/hooks/customQuery.ts (1)
useCoreMutation(21-73)src/api/auth/account.ts (1)
deleteMember(19-21)src/types/dates/preferences.ts (1)
TResetPreferencesResponse(3-3)src/api/dates/preferences.ts (1)
resetPreferences(6-9)
src/providers/deviceTokenProvider.tsx (5)
src/api/queryClient.ts (1)
queryClient(3-9)src/hooks/alarm/usePostDeviceToken.ts (1)
useFirebase(5-8)src/api/alarm/alarm.ts (1)
postDeviceToken(15-18)src/firebase/firebase.ts (4)
messaging(17-17)registerServiceWorker(51-59)generateToken(29-49)deleteFcmToken(61-70)src/hooks/alarm/useDeviceToken.ts (5)
useEffect(8-39)setupFCM(9-38)isSupported(10-26)setupFCM(28-31)window(35-37)
src/components/common/PasswordEdit.tsx (1)
src/pages/auth/FindPw.tsx (3)
err(117-119)data(109-116)isValid(101-123)
src/pages/home/HomePage.tsx (2)
src/providers/deviceTokenProvider.tsx (1)
useDeviceTokenContext(120-124)src/hooks/alarm/useDeviceToken.ts (4)
useEffect(8-39)window(35-37)setupFCM(9-38)setupFCM(28-31)
src/hooks/customQuery.ts (9)
src/types/common/common.ts (1)
TUseMutationCustomOptions(22-31)src/api/queryClient.ts (1)
queryClient(3-9)src/hooks/auth/useAuth.ts (1)
useAuth(5-14)src/hooks/home/useUserRegion.ts (1)
useUserRegion(6-8)src/hooks/home/useDatePlaceStates.ts (1)
useCoreQuery(6-11)src/hooks/home/useUserGrade.ts (1)
useCoreQuery(7-12)src/hooks/home/useKeywordStates.ts (1)
useCoreQuery(7-12)src/hooks/home/useDateCourseStates.ts (1)
useCoreQuery(7-12)src/hooks/home/useDateTimes.ts (1)
useCoreQuery(6-8)
src/App.tsx (2)
src/queryKey/queryKey.ts (1)
alarmKeys(8-11)src/providers/deviceTokenProvider.tsx (1)
DeviceTokenProvider(27-118)
🔇 Additional comments (9)
src/api/axiosInstance.ts (4)
43-43: catch 변수 참조 수정 LGTM
errors를 사용해AxiosError<IRefreshResponse>로 캐스팅하도록 바로잡은 점 좋습니다. 잘못된 분기 가능성을 줄였습니다.
45-46: refreshToken 부재 시 즉시 로그아웃 + Promise 무시 처리 적용 LGTM콘솔 에러 로깅 후
void logout()으로 미관측 거부를 방지한 점 적절합니다. UX/보안 측면에서 일관됩니다.
48-49: 만료 토큰 분기 처리 LGTM만료 케이스에서도 동일하게 로깅 후 세션 정리하는 흐름이 일관적입니다.
51-52: 알 수 없는 오류 케이스 간소화 LGTMalert 제거 및 로그/로그아웃으로 통일한 점이 HTTP 레이어의 책임 분리에 부합합니다.
src/types/common/common.ts (1)
17-20: TOptimisticUpdate 도입 잘 반영되었습니다키/업데이트 함수로 낙관적 업데이트를 모델링한 타입 정의가 명확하고 활용성이 높습니다.
src/hooks/customQuery.ts (1)
43-51: 낙관적 업데이트/롤백 로직 구성 훌륭합니다캐시 캡처→업데이트→컨텍스트 반환→에러 시 롤백 플로우가 깔끔합니다.
setQueryData<TCache>로 캐시 타입을 고정한 점도 👍src/hooks/auth/useAccount.ts (1)
49-51: LGTM — 일관된 제네릭 지정으로 가독성/안정성 확보
TResetPreferencesResponse와void변수 타입을 명시하신 부분이 명확하고 좋습니다. 별도 이슈 없습니다.src/pages/home/HomePage.tsx (1)
20-33: 초기화 트리거를 pointerdown 1회에 한정한 설계 적절사용자 상호작용 이후 1회만 등록하도록
once: true와 클린업을 함께 둔 점이 안정적입니다.requestAndRegister의존성도 정확합니다.src/queryKey/queryKey.ts (1)
4-4: all: null 패턴 적용 좋습니다루트 키 생성을 위해 all: null을 사용한 점이 createQueryKeys 권장 패턴과 부합합니다. 이후 invalidate 시에도 직관적입니다.
Also applies to: 9-9, 14-14, 26-26
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: 8
🔭 Outside diff range comments (2)
src/components/settingTab/InfoSetting.tsx (1)
98-105: readOnly 인풋의 불필요한 onChange 제거읽기 전용 + pointer-events-none 상태에서 빈 onChange는 불필요하며 리렌더 원인이 될 수 있습니다.
<EditableInputBox label="이메일" value={infoLoading ? '불러오는 중' : infoError ? '' : email} readOnly - onChange={() => {}} className="pointer-events-none" placeholder="이메일" />src/components/common/PasswordEdit.tsx (1)
114-162: 비밀번호 입력 필드에 autoComplete 명시 권장브라우저/패스워드 매니저 동작을 안정화하려면 autoComplete를 명시하세요.
- <input + <input type="password" placeholder="현재 비밀번호 입력" value={currentPw} onChange={(e) => setCurrentPw(e.target.value)} + autoComplete="current-password" className={inputClass(!!errors.currentPw)} disabled={isPending} /> ... - <input + <input type="password" placeholder="새 비밀번호 입력" value={newPw} onChange={(e) => setNewPw(e.target.value)} + autoComplete="new-password" className={inputClass(!!errors.newPw)} disabled={isPending} /> ... - <input + <input type="password" placeholder="새 비밀번호 확인" value={confirmPw} onChange={(e) => setConfirmPw(e.target.value)} + autoComplete="new-password" className={inputClass(!!errors.confirmPw)} disabled={isPending} />
♻️ Duplicate comments (2)
src/components/common/PasswordEdit.tsx (1)
62-66: 에러 핸들러 any 지양 및 타입 안전 접근 적용 필요이전 코멘트와 동일합니다. 공용 에러 타입을 사용해 any와 중복 캐스팅을 제거해 주세요.
아래처럼 수정 권장:
- onError: (err: any) => { - const msg = (err as any)?.response?.data?.message ?? '비밀번호 변경에 실패했습니다.'; + onError: (err) => { + const msg = err.response?.data?.message ?? '비밀번호 변경에 실패했습니다.'; alert(msg); },파일 상단에 공용 에러 타입을 import 하세요(선택 사항):
import type { TResponseError } from '@/types/common/common';그리고 필요 시 콜백 시그니처를 (err: TResponseError)로 지정해 주세요.
src/hooks/auth/useAccount.ts (1)
23-25: 인자 없는 뮤테이션은 제네릭을 명시해 TVariables를 void로 고정하는 것을 권장합니다프로젝트 전반의 “제네릭 생략” 방향은 합리적입니다. 다만 인자가 없는 뮤테이션의 경우, TVariables가 void로 확정되어야 호출부에서 mutate(undefined) 등이 일관되게 사용됩니다. 명시해 두면 시그니처 변경 시 타입 안전성이 높아집니다. (이 코멘트는 이전 리뷰 취지와 동일합니다.)
아래처럼 최소 범위로 보완 가능합니다:
- return useCoreMutation(deleteMember); + return useCoreMutation<void, void>(deleteMember);resetPreferences도 TVariables를 void로 고정해 두면 안전합니다. TData는 함수 시그니처로부터 추론하되, 명확성을 위해 아래처럼 선언할 수 있습니다(프로젝트 TS 4.5+ 가정).
- return useCoreMutation(resetPreferences); + return useCoreMutation<Awaited<ReturnType<typeof resetPreferences>>, void>(resetPreferences);만약 Awaited 사용이 어려운 환경이라면, 현행 그대로 두셔도 동작에는 문제 없습니다.
Also applies to: 38-40
📜 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 settings in your CodeRabbit configuration.
📒 Files selected for processing (7)
src/components/common/PasswordEdit.tsx(2 hunks)src/components/settingTab/AlarmSetting.tsx(2 hunks)src/components/settingTab/InfoSetting.tsx(3 hunks)src/hooks/auth/useAccount.ts(2 hunks)src/hooks/settingAlarm/useAlarms.ts(1 hunks)src/pages/setting/DeleteConfirmPage.tsx(1 hunks)src/types/auth/account.ts(2 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (6)
src/pages/setting/DeleteConfirmPage.tsx (3)
src/hooks/auth/useAuth.ts (1)
useAuth(5-14)src/hooks/home/useUserRegion.ts (1)
useUserRegion(6-8)src/components/modal/deleteBookmarkModal.tsx (1)
changeState(17-21)
src/types/auth/account.ts (3)
src/hooks/auth/useAuth.ts (1)
useAuth(5-14)src/hooks/customQuery.ts (1)
useCoreMutation(20-28)src/hooks/home/useUserRegion.ts (1)
useUserRegion(6-8)
src/hooks/auth/useAccount.ts (5)
src/hooks/customQuery.ts (2)
useCoreMutation(21-73)useCoreMutation(20-28)src/api/auth/account.ts (3)
changePassword(7-10)changeNickname(13-16)deleteMember(19-21)src/api/dates/preferences.ts (1)
resetPreferences(6-9)src/hooks/auth/useAuth.ts (1)
useAuth(5-14)src/hooks/home/useUserRegion.ts (1)
useUserRegion(6-8)
src/components/settingTab/AlarmSetting.tsx (1)
src/api/queryClient.ts (1)
queryClient(3-9)
src/components/settingTab/InfoSetting.tsx (7)
src/api/auth/account.ts (1)
changeNickname(13-16)src/api/queryClient.ts (1)
queryClient(3-9)src/hooks/auth/useAccount.ts (1)
QUERY_KEYS(6-9)src/hooks/customQuery.ts (1)
useCoreMutation(20-28)src/hooks/home/useUserGrade.ts (2)
useCoreQuery(7-12)getUserGrade(8-8)src/pages/auth/UserSetting.tsx (1)
User(23-178)src/hooks/auth/useAuth.ts (1)
useAuth(5-14)
src/components/common/PasswordEdit.tsx (2)
src/pages/auth/FindPw.tsx (4)
err(117-119)data(109-116)isValid(101-123)FindPw(17-207)src/hooks/auth/useAuth.ts (1)
useAuth(5-14)
🔇 Additional comments (6)
src/components/settingTab/AlarmSetting.tsx (1)
52-54: 성공 시 캐시 무효화 추가: 적절합니다성공 콜백에서 alarmSettings 쿼리를 명시적으로 무효화하는 흐름이 명확하고 안정적입니다. 낙관적 업데이트 + 오류 시 롤백 패턴과도 잘 맞습니다.
src/pages/setting/DeleteConfirmPage.tsx (1)
44-57: per-call 옵션으로 mutate 사용: 적절합니다성공/실패 사이드이펙트를 호출 지점으로 이동해 응집도가 높아졌고, UI 상태와 로직이 명확합니다.
src/types/auth/account.ts (1)
21-25: 뮤테이션 결과 타입 정리: 문제 없습니다useMutation의 제네릭 파라미터 지정이 명확해졌고, 퍼블릭 타입과의 호환성도 좋아 보입니다.
src/hooks/settingAlarm/useAlarms.ts (1)
17-18: ✅ 호출부 무효화 확인 완료다음 호출부에서
alarmSettings무효화가 적용된 것을 확인했습니다.
- src/components/settingTab/AlarmSetting.tsx:53 –
queryClient.invalidateQueries({ queryKey: ['alarmSettings'] })현재
usePatchAlarmSettings훅은 해당 컴포넌트에서만 사용되며, 패치 성공 시 무효화 패턴이 일관되게 적용된 것을 확인했습니다.src/hooks/auth/useAccount.ts (2)
13-15: 간결화 좋습니다변경된 useCoreMutation API와 per-call 옵션 패턴에 부합합니다.
18-20: 일관된 간결화 적용 확인닉네임 변경 훅도 동일 패턴으로 정리되어 좋습니다.
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: 16
🔭 Outside diff range comments (4)
src/components/modal/regionModal.tsx (1)
66-77: 잠재적 런타임 예외: 체이닝 안전성 강화 필요regionList?.result 뒤에 바로 .regions를 접근하고 있어, result가 undefined일 경우 런타임 에러 위험이 있습니다. 안전한 선택적 체이닝으로 방어하세요.
아래와 같이 수정하면 안전합니다.
- {regionList?.result.regions.length === 0 && <div className="text-center py-8 text-default-gray-500">검색 결과가 없습니다</div>} - {regionList?.result.regions.map((region, index) => ( + {regionList?.result?.regions?.length === 0 && ( + <div className="text-center py-8 text-default-gray-500">검색 결과가 없습니다</div> + )} + {regionList?.result?.regions?.map((region, index) => ( <li key={region.regionId}> <button type="button" onClick={() => handleRegionSelect(region.regionId)} className={`w-full text-left py-3 px-4 hover:bg-primary-100 transition-colors`} > <div className="font-medium">{region.name}</div> </button> - {index < regionList.result.regions.length - 1 && <div className="border-b border-default-gray-400" />} + {index < (regionList?.result?.regions?.length ?? 0) - 1 && ( + <div className="border-b border-default-gray-400" /> + )} </li> ))}추가로, 앞서 제안한 대로 훅에서 invalidate를 처리하도록 변경한다면 이 컴포넌트의 무효화 호출은 제거 가능합니다.
src/hooks/home/useKeywordStates.ts (1)
8-12: 주간 데이터 특성에 맞춘 staleTime 조정 제안weekly 성격의 데이터라면 5분(기본값)보다 더 긴 staleTime이 효율적일 수 있습니다. gcTime(30분)에 맞춰 staleTime도 30분으로 늘리면 재요청 빈도를 줄일 수 있습니다. 선택 사항입니다.
- return useCoreQuery(homeKeys.keywords().queryKey, getWeeklyKeywords, { - gcTime: 30 * 60 * 1000, - retry: 3, - }); + return useCoreQuery(homeKeys.keywords().queryKey, getWeeklyKeywords, { + staleTime: 30 * 60 * 1000, + gcTime: 30 * 60 * 1000, + retry: 3, + });src/pages/setting/DeleteConfirmPage.tsx (1)
1-20: 회원 탈퇴 시 토큰·민감 데이터 정리 로직 추가 필요현재 코드베이스를 살펴보면
- SettingModal.tsx:
localStorage.removeItem('accessToken')만 제거- 리프레시 토큰(
refreshToken) 제거 로직은 없음- DeleteConfirmPage.tsx(탈퇴 페이지)에서는 토큰 정리 로직이 전혀 호출되지 않음
회원 탈퇴 완료 시 반드시 아래 중 하나를 수행해 주세요:
logout()호출 (인터셉터나 스토어에서 access/refreshToken 모두 정리)- 또는 직접
localStorage.removeItem('accessToken')와localStorage.removeItem('refreshToken')호출예시 diff (DeleteConfirmPage.tsx 탈퇴 성공 핸들러에 추가):
--- a/src/pages/setting/DeleteConfirmPage.tsx +++ b/src/pages/setting/DeleteConfirmPage.tsx @@ (탈퇴 성공 후 콜백) - // 이후 페이지 이동만 처리 중 + // 인증 토큰·민감 데이터 삭제 + localStorage.removeItem('accessToken'); + localStorage.removeItem('refreshToken'); + // ※ 만약 logout() 호출 시 자동 정리가 된다면 해당 함수로 대체 가능
- 적용 위치
- src/pages/setting/DeleteConfirmPage.tsx: 탈퇴 성공 후 처리 로직
- 필요 시 src/components/modal/SettingModal.tsx의 refreshToken 제거 로직도 병기
src/components/settingTab/InfoSetting.tsx (1)
101-108: readOnly 입력의 불필요한 onChange 제거(선택)EditableInputBox의 onChange가 선택(prop optional)이라면, 빈 함수를 전달하지 않는 편이 리렌더를 줄입니다. 해당 prop이 필수라면 현 상태 유지하세요.
적용 diff:
- <EditableInputBox + <EditableInputBox label="이메일" value={infoLoading ? '불러오는 중' : infoError ? '' : email} readOnly - onChange={() => {}} className="pointer-events-none" placeholder="이메일" />
♻️ Duplicate comments (10)
src/queryKey/queryKey.ts (4)
4-4: all: null 적용 굿 — createQueryKeys 권장 패턴과 일치루트 키는 all: null로 정의하는 게 맞습니다. 중복 접두어 이슈를 예방합니다.
26-29: noticeKeys: 리터럴 축소(as const)로 키 안정성 강화튜플을 as const로 고정해 리터럴 타입을 유지하면 타입 안정성과 오타 방지가 좋아집니다.
- getAllNotices: (page: number, size: number, noticeCategory: 'SERVICE' | 'SYSTEM') => [page, size, noticeCategory], + getAllNotices: (page: number, size: number, noticeCategory: 'SERVICE' | 'SYSTEM') => + [page, size, noticeCategory] as const,
30-34: memberKeys: 메서드명 자동 접두 고려 — 파라미터 없는 키는 null 사용현재는 메서드명과 동일한 문자열을 파라미터 배열에 넣어 중복됩니다.
export const memberKeys = createQueryKeys('member', { all: null, - memberInfo: () => ['memberInfo'], - memberGrade: () => ['memberGrade'], + memberInfo: null, + memberGrade: null, });
5-5: 중복 접두어 제거 필요(regionKeys.search)createQueryKeys가 메서드명('search')을 자동으로 붙입니다. 파라미터만 반환하세요. 현 상태에선 최종 키가 ['region', 'search', 'search', keyword]처럼 중복됩니다.
다음과 같이 수정 제안드립니다.
- search: (keyword: string) => ['search', keyword], + search: (keyword: string) => [keyword] as const,src/components/modal/regionModal.tsx (1)
29-29: no-floating-promises 해결 잘하셨습니다invalidateQueries 앞에 void를 붙여 의도를 명확히 했습니다. 훌륭합니다.
src/components/settingTab/AlarmSetting.tsx (1)
7-9: 쿼리 키 상수화 및 queryClient 주입 반영 굿하드코딩된 키 대신 alarmKeys를 사용했고, queryClient 인스턴스를 가져와 무효화를 수행하도록 수정된 점 좋습니다. 이전 리뷰 지적사항이 해결됐습니다.
다음 스크립트로 해당 queryClient 인스턴스가 QueryClientProvider에 동일하게 주입되는지(싱글톤 보장) 확인해 주세요. 서로 다른 인스턴스를 쓰면 invalidate가 기대대로 동작하지 않습니다.
#!/bin/bash set -e echo "▶ QueryClientProvider 사용 위치 검색" rg -n "<QueryClientProvider" -A 3 echo echo "▶ '@/api/queryClient'에서 export된 queryClient import 여부" rg -n "import\\s+\\{\\s*queryClient\\s*\\}\\s+from\\s+['\"]@/api/queryClient['\"]" echo echo "▶ Provider에 동일 인스턴스가 주입되는지 확인" rg -n "<QueryClientProvider\\s+client=\\{queryClient\\}" -g '!node_modules' echo echo "▶ 중복 QueryClient 인스턴스 생성 여부 점검" rg -n "new\\s+QueryClient\\(" -g '!node_modules'src/components/settingTab/InfoSetting.tsx (2)
55-58: 쿼리 키 불일치 가능성: ['userGrade'] 대신 memberKeys.memberGrade().queryKey 사용 필요현재 setQueryData의 키가 queryKey 정의(memberGrade)와 불일치해 캐시 패치가 반영되지 않을 수 있습니다. 실제 사용 키로 교체하세요. old?.result 가드도 포함되어 있어 안전합니다.
적용 diff:
- queryClient.setQueryData(['userGrade'], (old: any) => (old ? { ...old, result: { ...old.result, username: next } } : old)); + queryClient.setQueryData(memberKeys.memberGrade().queryKey, (old: any) => + old?.result ? { ...old, result: { ...old.result, username: next } } : old, + );
62-63: onError 콜백의 any 제거 및 공용 에러 타입 사용에러 타입을 TResponseError로 지정해 헬퍼 시그니처와 일치시키세요.
적용 diff:
- onError: (err: any) => alert(getApiErrorMessage(err, '닉네임 변경에 실패했습니다.')), + onError: (err: TResponseError) => alert(getApiErrorMessage(err, '닉네임 변경에 실패했습니다.')), @@ - onError: (err: any) => alert(getApiErrorMessage(err, '초기화에 실패했습니다.')), + onError: (err: TResponseError) => alert(getApiErrorMessage(err, '초기화에 실패했습니다.')),Also applies to: 82-83
src/hooks/auth/useAccount.ts (1)
19-21: 과거 피드백 재확인: deleteMember 제네릭 명시 권장이전 코멘트와 동일하게, 명시적 제네릭으로 의도를 드러내면 시그니처 변경 시에도 타입 안정성이 높아집니다.
다음과 같이 수정을 제안드립니다:
- return useCoreMutation(deleteMember); + return useCoreMutation<void, void>(deleteMember);src/providers/deviceTokenProvider.tsx (1)
21-25: onForegroundMessage의 payload 타입을 Firebase MessagePayload로 구체화콜백 인자를 unknown으로 두면 사용처에서 안전한 접근이 어렵습니다. Firebase 타입을 사용해 의도를 명확히 해 주세요.
- onForegroundMessage?: (payload: unknown) => void; + onForegroundMessage?: (payload: import('firebase/messaging').MessagePayload) => void;
📜 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 settings in your CodeRabbit configuration.
📒 Files selected for processing (16)
src/components/common/PasswordEdit.tsx(3 hunks)src/components/modal/regionModal.tsx(2 hunks)src/components/settingTab/AlarmSetting.tsx(2 hunks)src/components/settingTab/InfoSetting.tsx(3 hunks)src/hooks/auth/useAccount.ts(1 hunks)src/hooks/home/useDateCourseStates.ts(1 hunks)src/hooks/home/useDatePlaceStates.ts(1 hunks)src/hooks/home/useDateTimes.ts(1 hunks)src/hooks/home/useKeywordStates.ts(1 hunks)src/hooks/home/useUserGrade.ts(1 hunks)src/hooks/home/useUserRegion.ts(1 hunks)src/hooks/home/useWeather.ts(1 hunks)src/hooks/settingAlarm/useAlarms.ts(1 hunks)src/pages/setting/DeleteConfirmPage.tsx(2 hunks)src/providers/deviceTokenProvider.tsx(1 hunks)src/queryKey/queryKey.ts(1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (15)
src/hooks/home/useUserGrade.ts (3)
src/hooks/customQuery.ts (1)
useCoreQuery(8-19)src/queryKey/queryKey.ts (1)
homeKeys(14-24)src/api/home/level.ts (1)
getUserGrade(6-9)
src/hooks/home/useUserRegion.ts (4)
src/hooks/customQuery.ts (2)
useCoreMutation(21-73)useCoreQuery(8-19)src/api/home/region.ts (2)
patchUserRegion(5-8)getUserRegion(10-13)src/queryKey/queryKey.ts (1)
homeKeys(14-24)src/hooks/course/useSearchRegion.tsx (1)
useSearchRegion(6-11)
src/components/settingTab/AlarmSetting.tsx (3)
src/api/queryClient.ts (1)
queryClient(3-9)src/queryKey/queryKey.ts (1)
alarmKeys(8-12)src/hooks/alarm/useGetAlarm.ts (1)
TRequestGetAlarm(8-15)
src/hooks/settingAlarm/useAlarms.ts (4)
src/hooks/customQuery.ts (3)
useCoreQuery(8-19)useCoreMutation(21-73)useCoreMutation(20-28)src/queryKey/queryKey.ts (1)
alarmKeys(8-12)src/api/settingAlarm/alarm.ts (2)
getAlarmSettings(6-9)patchAlarmSettings(12-15)src/hooks/alarm/useGetAlarm.ts (2)
TRequestGetAlarm(8-15)cursor(11-11)
src/hooks/home/useDateTimes.ts (4)
src/hooks/customQuery.ts (1)
useCoreQuery(8-19)src/queryKey/queryKey.ts (1)
homeKeys(14-24)src/api/home/dateTimes.ts (1)
getDateTimeStates(11-14)src/components/home/dateTimes.tsx (1)
DateTimes(7-50)
src/hooks/home/useKeywordStates.ts (3)
src/hooks/customQuery.ts (1)
useCoreQuery(8-19)src/queryKey/queryKey.ts (1)
homeKeys(14-24)src/api/home/keyword.ts (1)
getWeeklyKeywords(6-9)
src/providers/deviceTokenProvider.tsx (5)
src/hooks/alarm/usePostDeviceToken.ts (1)
useFirebase(5-8)src/api/alarm/alarm.ts (1)
postDeviceToken(15-18)src/firebase/firebase.ts (5)
messaging(17-17)registerServiceWorker(51-59)generateToken(29-49)deleteFcmToken(61-70)token(18-31)src/api/queryClient.ts (1)
queryClient(3-9)src/hooks/alarm/useDeviceToken.ts (2)
isSupported(10-26)window(35-37)
src/hooks/home/useDatePlaceStates.ts (3)
src/hooks/customQuery.ts (1)
useCoreQuery(8-19)src/queryKey/queryKey.ts (1)
homeKeys(14-24)src/api/home/dateTimes.ts (1)
getMonthlyDatePlaceStates(6-9)
src/components/modal/regionModal.tsx (3)
src/api/queryClient.ts (1)
queryClient(3-9)src/queryKey/queryKey.ts (1)
homeKeys(14-24)src/api/home/region.ts (1)
TPatchUserRegionRequest(5-8)
src/components/common/PasswordEdit.tsx (2)
src/pages/auth/FindPw.tsx (3)
err(117-119)data(109-116)FindPw(17-207)src/hooks/auth/useAuth.ts (1)
useAuth(5-14)
src/hooks/home/useDateCourseStates.ts (3)
src/hooks/customQuery.ts (1)
useCoreQuery(8-19)src/queryKey/queryKey.ts (1)
homeKeys(14-24)src/api/home/dateCourse.ts (1)
getDateCourseSavedCount(5-8)
src/pages/setting/DeleteConfirmPage.tsx (3)
src/api/queryClient.ts (1)
queryClient(3-9)src/pages/RedirectPage.tsx (1)
LoginRedirect(10-56)src/pages/auth/UserSetting.tsx (1)
User(23-178)
src/components/settingTab/InfoSetting.tsx (5)
src/types/common/common.ts (1)
TResponseError(11-15)src/api/auth/account.ts (1)
changeNickname(13-16)src/api/queryClient.ts (1)
queryClient(3-9)src/queryKey/queryKey.ts (1)
memberKeys(30-34)src/hooks/customQuery.ts (2)
useCoreMutation(20-28)useCoreQuery(7-18)
src/hooks/auth/useAccount.ts (5)
src/hooks/customQuery.ts (3)
useCoreMutation(21-73)useCoreQuery(8-19)useCoreMutation(20-28)src/api/auth/account.ts (5)
changePassword(7-10)changeNickname(13-16)deleteMember(19-21)getMemberInfo(24-27)getMemberGrade(30-33)src/queryKey/queryKey.ts (1)
memberKeys(30-34)src/api/dates/preferences.ts (1)
resetPreferences(6-9)src/hooks/auth/useAuth.ts (1)
useAuth(5-14)
src/hooks/home/useWeather.ts (3)
src/hooks/customQuery.ts (1)
useCoreQuery(8-19)src/queryKey/queryKey.ts (1)
homeKeys(14-24)src/api/home/weather.ts (2)
getWeeklyWeatherRecommendation(11-17)getPrecipitation(19-22)
🔇 Additional comments (18)
src/hooks/home/useUserRegion.ts (1)
4-4: 키 네이밍 변경 반영 LGTMhomeKeys로의 전환이 일관되게 적용되었습니다. queryKey 참조도 정상입니다.
Also applies to: 11-11
src/hooks/home/useDateCourseStates.ts (1)
8-8: LGTM — 키 전환 및 쿼리 구성 정상homeKeys로의 전환이 적절하며, queryFn을 직접 전달하는 방식도 일관적입니다.
src/hooks/home/useDateTimes.ts (1)
4-4: homeKeys로의 전환 반영 적절키 팩토리 전환이 일관되게 반영되었습니다. 다른 훅들과 키 드리프트 위험이 줄었습니다.
src/hooks/home/useKeywordStates.ts (1)
4-4: homeKeys로의 키 소스 통일 👍키 생성부 리팩터링 방향과 일치합니다.
src/hooks/home/useWeather.ts (2)
1-1: useCoreQuery 절대 경로(alias)로 정리된 점 좋습니다모듈 경로 일관성이 올라갑니다.
5-5: homeKeys로 전환 LGTM키 네임스페이스가 통일되었습니다.
src/hooks/home/useDatePlaceStates.ts (1)
4-4: homeKeys 도입 일관성 확보키 제공자 변경이 다른 훅들과 동일하게 반영되었습니다.
src/hooks/settingAlarm/useAlarms.ts (2)
6-13: 쿼리 키 상수화 및 queryKey 활용이 일관성·안정성에 도움이 됩니다하드코딩된 ['alarmSettings'] 대신 alarmKeys.alarmSettings().queryKey 사용으로 캐시 키 일관성과 오타 가능성이 줄었습니다. select/refetch 옵션도 목적에 부합합니다.
17-19: usePatchAlarmSettings 훅 사용처 점검 완료
훅은src/components/settingTab/AlarmSetting.tsx에서만 호출되며, 해당 파일에 이미invalidate처리(onSuccess 무효화)가 구현되어 있습니다.
추가 누락된 호출부가 없어 별도 조치가 필요하지 않습니다.src/pages/setting/DeleteConfirmPage.tsx (1)
50-57: 성공 시 클라이언트 상태 초기화 흐름이 명확합니다스토어 리셋 + React Query 캐시 전체 초기화 + 리다이렉트가 일관되게 수행됩니다.
src/components/common/PasswordEdit.tsx (1)
32-33: 뮤테이션 옵션을 호출부로 이동한 변경이 적절합니다isPending 활용, 입력 비활성화, 성공 후 초기화 흐름이 명확합니다.
Also applies to: 175-183
src/components/settingTab/InfoSetting.tsx (2)
16-18: 공용 에러 타입 기반 헬퍼 도입이 적절합니다401 특례 메시지 처리 포함, 안전한 접근으로 정리된 점 좋습니다.
55-56: 멤버 관련 캐시 무효화 범위가 적절합니다memberKeys.all.queryKey를 통한 광범위 무효화로 주변 데이터 일관성이 유지됩니다.
src/hooks/auth/useAccount.ts (1)
25-26:.queryKey프로퍼티 존재 여부 확인 필요
memberKeys.memberInfo().queryKey와memberKeys.memberGrade().queryKey호출 시 런타임 오류가 발생할 가능성이 있습니다.
createQueryKeys가 반환하는 객체에.queryKey가 실제로 정의돼 있는지 아래 위치에서 확인해 주세요.
- src/hooks/auth/useAccount.ts
- 25–26줄:
useCoreQuery(memberKeys.memberInfo().queryKey, getMemberInfo)- 30–31줄:
useCoreQuery(memberKeys.memberGrade().queryKey, getMemberGrade)만약
.queryKey가 없다면, 배열 자체를 넘기도록 수정해야 합니다.
예시:return useCoreQuery(memberKeys.memberInfo(), getMemberInfo);src/providers/deviceTokenProvider.tsx (4)
33-34: 훅 접두사(use-) 제거 적용 확인 — 린트 경고 해소뮤테이션 별칭을
postDeviceToken으로 변경해 훅 규칙 경고가 사라졌습니다. 적절한 수정입니다.
35-41: Notification 가드 추가 확인 — SSR/비지원 브라우저 안전
'Notification' in window가드가 추가되어 SSR/비지원 환경에서의 ReferenceError 가능성이 제거되었습니다. 좋습니다.
92-101: unregister 시 onMessage 구독 해제 및 invalidate void 처리 적용 확인
messageUnsubRef해제와void queryClient.invalidateQueries처리로 리스너 누수와 floating promise가 방지되었습니다. 적절합니다.
1-1: 파일명·대소문자 일치 확인됨실제 파일명(
src/providers/deviceTokenProvider.tsx)과 모든 import 경로(@/providers/deviceTokenProvider)가 일치하여 CI/운영 환경에서 문제를 일으키지 않습니다.
- 실제 파일: src/providers/deviceTokenProvider.tsx
- App.tsx import:
import { DeviceTokenProvider } from '@/providers/deviceTokenProvider'- HomePage.tsx import:
import { useDeviceTokenContext } from '@/providers/deviceTokenProvider'
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: 7
🔭 Outside diff range comments (3)
src/routes/routes.tsx (2)
64-126: paymentHistory를 메인 레이아웃(children)으로 편입해 레이아웃 일관성과 모달 적용을 보장하세요.두 번째 루트(
/)는 이미<ModalProvider />와<Layout />를 제공하고 있으므로,paymentHistory를 여기 children으로 추가하는 것이 올바릅니다.다음 diff를 children 마지막에 추가해 주세요:
{ path: 'dateTest/result', element: <DateTestResult />, }, + { + path: 'paymentHistory', + element: <PaymentHistory />, + },
28-28: 확장자(.tsx)를 포함한 import는 TS 기본 설정에서 에러가 납니다.
allowImportingTsExtensions를 별도로 켜지 않았다면 컴파일 실패합니다. 다른 import들과도 일관성이 없습니다. 확장자를 제거해 주세요.-import DeleteReasonPage from '@/pages/setting/DeleteReasonPage.tsx'; +import DeleteReasonPage from '@/pages/setting/DeleteReasonPage';src/components/settingTab/InfoSetting.tsx (1)
106-106: readOnly 필드의 불필요한 onChange 제거읽기 전용(그리고 pointer-events-none)이라면 빈 onChange는 불필요합니다. 리렌더에 따른 불필요한 함수 생성만 유발합니다.
- onChange={() => {}}
♻️ Duplicate comments (11)
src/components/settingTab/AlarmSetting.tsx (1)
42-58: 함수형 상태 업데이트와 에러 핸들링이 잘 구현되었습니다이전 리뷰에서 제안했던 함수형 상태 업데이트(
setAlarmSetting((prev) => {...}))가 정확히 적용되어 동시성 문제를 방지할 수 있습니다. 성공 시 정확한 쿼리 키로 캐시 무효화하고, 실패 시 이전 상태로 롤백하는 로직도 적절합니다.src/hooks/home/useDateTimes.ts (1)
7-7: 함수 래핑 제거와 옵션 간소화가 적절합니다이전 리뷰에서 제안했던 불필요한 화살표 함수 래핑 제거와 중복 옵션 제거가 정확히 적용되었습니다.
getDateTimeStates를 직접 전달하고 기본값과 동일한 설정을 제거하여 코드가 더 간결해졌습니다.src/hooks/home/useDatePlaceStates.ts (2)
1-1: import 경로 alias 통일이 완료되었습니다상대 경로(
../customQuery)에서 절대 경로(@/hooks/customQuery)로 변경되어 프로젝트 전반의 import 일관성이 향상되었습니다.
7-7: 함수 래핑 제거와 키 접근 패턴 개선이전 리뷰에서 제안한 불필요한 함수 래핑 제거(
() => getMonthlyDatePlaceStates()→getMonthlyDatePlaceStates)와 키 접근 패턴 변경(monthlyPlaceStates().queryKey→monthlyPlaceStates.queryKey)이 정확히 적용되었습니다.src/hooks/home/useWeather.ts (2)
1-1: import 경로 alias 통일이 완료되었습니다상대 경로에서 절대 경로로 변경되어 프로젝트의 import 일관성이 개선되었습니다. 코드 가독성과 유지보수성이 향상되었습니다.
Also applies to: 3-3
8-8: 불필요한 non-null 단언 제거가 완료되었습니다이전 리뷰에서 제안한
regionId!에서regionId로의 변경이 정확히 적용되었습니다. 타입이 이미number로 선언되어 있고enabled가드도 있어 런타임 안전성이 충분히 확보됩니다.Also applies to: 15-15
src/components/settingTab/InfoSetting.tsx (1)
63-63: onError 콜백의 any 제거 — getApiErrorMessage와 타입 일치err는 TResponseError로 추론 가능하므로 명시 any를 제거하세요. 타입 안정성과 추론 품질이 개선됩니다.
- onError: (err: any) => alert(getApiErrorMessage(err, '닉네임 변경에 실패했습니다.')), + onError: (err) => alert(getApiErrorMessage(err, '닉네임 변경에 실패했습니다.')),src/queryKey/queryKey.ts (4)
26-29: noticeKeys.getAllNotices: 튜플 리터럴 고정(as const)로 타입 정확도 개선리터럴 축소를 위해 as const를 추가해 주세요.
- getAllNotices: (page: number, size: number, noticeCategory: 'SERVICE' | 'SYSTEM') => [page, size, noticeCategory], + getAllNotices: (page: number, size: number, noticeCategory: 'SERVICE' | 'SYSTEM') => + [page, size, noticeCategory] as const,
9-12: alarmKeys: getAlarm/alarmSettings 키 중복 제거메서드명 토큰을 제거하고 파라미터만 반환하세요. 파라미터 없는 키는 빈 튜플을 반환해 일관화할 수 있습니다.
- getAlarm: (size: number, cursor?: number) => ['getAlarm', size, cursor], - alarmSettings: () => ['alarmSettings'], + getAlarm: (size: number, cursor?: number) => [size, cursor] as const, + alarmSettings: () => [] as const,
4-6: regionKeys.search: 메서드명 중복 제거 및 as const 적용createQueryKeys가 루트/메서드명을 자동 접두하므로 'search' 토큰을 파라미터에 다시 넣을 필요가 없습니다. 최종 키 중복을 유발합니다.
- search: (keyword: string) => ['search', keyword], + search: (keyword: string) => [keyword] as const,
14-23: homeKeys: 불필요한 정적 토큰 제거(파라미터만 반환) + as constcreateQueryKeys가 ['home', '메서드명', ...params]를 구성하므로 내부에서 'user','grade','forecast' 등을 중복 포함할 필요가 없습니다. 키 혼선과 캐시 미스의 원인이 됩니다.
- getUserGrade: () => ['user', 'grade'], - dateCourseSave: () => ['date-courses', 'saved-count'], - weather: (startDate: string, regionId: number) => ['weather', 'forecast', startDate, regionId], - rainyInfo: (startDate: string, regionId: number) => ['rainy', 'forecast', startDate, regionId], + getUserGrade: () => [] as const, + dateCourseSave: () => [] as const, + weather: (startDate: string, regionId: number) => [startDate, regionId] as const, + rainyInfo: (startDate: string, regionId: number) => [startDate, regionId] as const, - userRegion: () => ['user', 'region'], + userRegion: () => [] as const,Note: 사용처에서 키 모양이 바뀌므로 invalidate/쿼리 키 참조부 전수 업데이트가 필요합니다.
📜 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 settings in your CodeRabbit configuration.
📒 Files selected for processing (9)
src/components/settingTab/AlarmSetting.tsx(2 hunks)src/components/settingTab/InfoSetting.tsx(3 hunks)src/hooks/home/useDatePlaceStates.ts(1 hunks)src/hooks/home/useDateTimes.ts(1 hunks)src/hooks/home/useKeywordStates.ts(1 hunks)src/hooks/home/useWeather.ts(1 hunks)src/pages/setting/PaymentHistory.tsx(3 hunks)src/queryKey/queryKey.ts(1 hunks)src/routes/routes.tsx(3 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (8)
src/pages/setting/PaymentHistory.tsx (3)
src/components/common/navigator.tsx (2)
Navigator(10-43)page(27-35)src/pages/dateCourse/MakeCourse.tsx (1)
MakeCourse(8-29)src/components/common/graySvgButton.tsx (1)
GraySvgButton(13-22)
src/routes/routes.tsx (3)
src/components/common/modalProvider.tsx (1)
ModalProvider(31-40)src/layout/layout.tsx (1)
Layout(6-14)src/App.tsx (1)
App(7-9)
src/components/settingTab/AlarmSetting.tsx (3)
src/api/queryClient.ts (1)
queryClient(3-9)src/queryKey/queryKey.ts (1)
alarmKeys(8-12)src/hooks/alarm/useGetAlarm.ts (1)
TRequestGetAlarm(8-15)
src/hooks/home/useKeywordStates.ts (5)
src/hooks/customQuery.ts (1)
useCoreQuery(8-19)src/queryKey/queryKey.ts (1)
homeKeys(14-24)src/api/home/keyword.ts (1)
getWeeklyKeywords(6-9)src/hooks/home/useDateCourseStates.ts (1)
useCoreQuery(7-12)src/hooks/home/useUserGrade.ts (1)
useCoreQuery(7-12)
src/components/settingTab/InfoSetting.tsx (5)
src/types/common/common.ts (1)
TResponseError(11-15)src/api/auth/account.ts (1)
changeNickname(13-16)src/api/queryClient.ts (1)
queryClient(3-9)src/queryKey/queryKey.ts (1)
memberKeys(30-34)src/hooks/customQuery.ts (1)
useCoreMutation(20-28)
src/hooks/home/useDateTimes.ts (6)
src/hooks/customQuery.ts (1)
useCoreQuery(8-19)src/queryKey/queryKey.ts (1)
homeKeys(14-24)src/api/home/dateTimes.ts (1)
getDateTimeStates(11-14)src/hooks/home/useDateCourseStates.ts (1)
useCoreQuery(7-12)src/components/home/dateTimes.tsx (1)
DateTimes(7-50)src/hooks/home/useUserGrade.ts (1)
useCoreQuery(7-12)
src/hooks/home/useWeather.ts (4)
src/hooks/customQuery.ts (1)
useCoreQuery(8-19)src/queryKey/queryKey.ts (1)
homeKeys(14-24)src/api/home/weather.ts (2)
getWeeklyWeatherRecommendation(11-17)getPrecipitation(19-22)src/hooks/home/useDateCourseStates.ts (1)
useCoreQuery(7-12)
src/hooks/home/useDatePlaceStates.ts (6)
src/hooks/customQuery.ts (1)
useCoreQuery(8-19)src/queryKey/queryKey.ts (1)
homeKeys(14-24)src/api/home/dateTimes.ts (1)
getMonthlyDatePlaceStates(6-9)src/hooks/home/useDateCourseStates.ts (1)
useCoreQuery(7-12)src/hooks/home/useUserGrade.ts (1)
useCoreQuery(7-12)src/hooks/home/useUserRegion.ts (1)
useGetUserRegion(10-12)
🔇 Additional comments (14)
src/routes/routes.tsx (2)
66-69: ModalProvider를 레이아웃 상단에 배치한 구성은 적절합니다.라우트 진입 시점에 모달 상태를 관리하기에 좋은 위치입니다. 이 구성은 유지하는 것을 권장합니다.
137-147: 인증 게이트 제거 영향 점검 필요(민감 라우트 접근성).
/deleteAccount,/deleteAccount/confirm,/withdraw가 더 이상 ProtectedRoute로 보호되지 않습니다. 페이지 내부 또는 Provider 레벨에서 인증 검증/리다이렉트가 확실히 수행되는지 확인해 주세요. 특히 계정 삭제/탈퇴 라우트는 비로그인 접근 시 진입이 차단되어야 합니다.검증 포인트:
- 각 페이지 마운트 시 토큰 유효성/회원 상태를 검사하고, 미인증 시 로그인으로 리다이렉트하는지
- 브라우저 히스토리 경로 진입(
/deleteAccount직접 URL)에도 동일하게 동작하는지src/pages/setting/PaymentHistory.tsx (2)
4-4: 아이콘 버튼 교체 적합GraySvgButton로 대체한 부분은 재사용성과 일관성 측면에서 적절합니다.
37-43: 헤더 중복 가능성: 레이아웃 하위로 편입 시 Header가 이중 렌더링됩니다.라우팅 수정 제안대로
paymentHistory를 메인 레이아웃 children으로 옮길 경우,<Layout />의<Header />와 본문 내<Header mode="minimal" />가 겹칩니다. 아래 중 하나로 정리해 주세요.
- 페이지 내 Header 제거 후, 레이아웃에서 헤더 모드를 제어할 수 있도록 Layout에 prop 추가(예:
<Layout headerMode="minimal" />).- 혹은 PaymentHistory를 전용 MinimalLayout 하위로 배치.
현 상태 유지 시에는 중복 헤더 노출을 감수해야 합니다.
원하시면 Layout이 헤더 모드를 받도록 리팩터링 패치를 제안드리겠습니다.
src/components/settingTab/AlarmSetting.tsx (1)
7-8: 쿼리 키 통합과 낙관적 업데이트 구현이 올바르게 적용되었습니다queryClient와 alarmKeys import를 추가하고 낙관적 업데이트 패턴을 구현한 부분이 적절합니다. 캐시 무효화를 통한 데이터 동기화도 잘 처리되었습니다.
src/hooks/home/useDateTimes.ts (1)
4-4: 쿼리 키 네임스페이스 통합이 올바르게 적용되었습니다HomeKeys에서 homeKeys로의 마이그레이션이 적절히 수행되었습니다. 중앙화된 키 관리 시스템으로의 전환이 잘 반영되었습니다.
src/hooks/home/useKeywordStates.ts (2)
4-4: 일관된 키 네임스페이스 적용이 완료되었습니다HomeKeys에서 homeKeys로의 변경이 올바르게 적용되어 전체 프로젝트의 쿼리 키 체계 통일에 기여합니다.
8-8: 쿼리 키 접근 패턴 업데이트가 적절합니다
homeKeys.keywords.queryKey로 변경되어 새로운 키 구조에 맞게 잘 적용되었습니다. 함수 호출 방식에서 속성 접근 방식으로의 변경이 일관성 있게 처리되었습니다.src/hooks/home/useDatePlaceStates.ts (1)
4-4: 쿼리 키 네임스페이스 마이그레이션이 적절합니다HomeKeys에서 homeKeys로의 변경이 다른 파일들과 일관되게 적용되었습니다.
src/hooks/home/useWeather.ts (1)
4-4: 쿼리 키 네임스페이스 통합이 적절합니다HomeKeys에서 homeKeys로의 마이그레이션이 일관성 있게 적용되었습니다.
src/components/settingTab/InfoSetting.tsx (4)
16-17: 에러 타입 명확화 적용 좋습니다TResponseError를 활용해 공용 에러 포맷을 고정한 점 좋습니다. 이후 콜백 시그니처에서도 any를 제거해 일관성을 유지하면 더 안전합니다.
45-66: 닉네임 변경 흐름(뮤테이션 옵션 현지화, 캐시 무효화/패치) 구성 적절
- mutate 호출부로 onSuccess/onError를 이동해 가독성과 응집도를 높인 점 좋습니다.
- memberKeys.all 무효화와 memberGrade 캐시 패치로 UX를 보완하려는 접근도 합리적입니다.
75-76: resetPref 호출 시 variables/option 분리 적용 좋습니다mutate(undefined, options) 형태로 올바르게 분리되어 있습니다. 이전 혼동 가능성을 해소합니다.
120-120: CSS 커스텀 유틸 클래스 ‘rounding-32’ 확인 완료
프로젝트 전반에 걸쳐rounding-16,rounding-32클래스가 사용되고 있으며,src/index.css에도 해당 클래스가 정의되어 있습니다. 따라서 Tailwind의rounded-32가 아닌 커스텀 유틸로 의도된 것이 맞으니 변경은 불필요합니다.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.
Actionable comments posted: 8
🔭 Outside diff range comments (2)
src/pages/auth/UserSetting.tsx (1)
82-101: React children 전달 방식 및 버튼 타입 명시
- Biome가 지적한 대로 children prop 사용을 지양하고 JSX children으로 전달해 주세요.
- 폼 내부 버튼들은 의도 명확화를 위해 타입을 명시하는 것이 좋습니다. 성별 선택 버튼은 type="button", 제출 버튼은 type="submit" 권장.
- 제출 버튼은 form onSubmit이 이미 있으므로 onClick={handleSubmit(onSubmit)}는 제거해도 됩니다.
- <Button - onClick={() => { - setValue('gender', Gender.MALE); - setGender(Gender.MALE); - }} - children={'남자'} - size="big-32" - variant={`${gender == Gender.MALE ? 'mint' : 'white'}`} - className="px-[32px] !py-[12px]" - /> + <Button + type="button" + onClick={() => { + setValue('gender', Gender.MALE); + setGender(Gender.MALE); + }} + size="big-32" + variant={`${gender == Gender.MALE ? 'mint' : 'white'}`} + className="px-[32px] !py-[12px]" + > + 남자 + </Button> @@ - <Button - onClick={() => { - setValue('gender', Gender.FEMALE); - setGender(Gender.FEMALE); - }} - children={'여자'} - size="big-32" - variant={`${gender == Gender.FEMALE ? 'mint' : 'white'}`} - className="px-[32px] !py-[12px]" - /> + <Button + type="button" + onClick={() => { + setValue('gender', Gender.FEMALE); + setGender(Gender.FEMALE); + }} + size="big-32" + variant={`${gender == Gender.FEMALE ? 'mint' : 'white'}`} + className="px-[32px] !py-[12px]" + > + 여자 + </Button> @@ - <Button - children={'회원가입 완료'} - size="big-16" - variant="mint" - onClick={handleSubmit(onSubmit)} - disabled={!isValid || !agree1 || !agree2 || isPending} - className="w-full" - /> + <Button + type="submit" + size="big-16" + variant="mint" + disabled={!isValid || !agree1 || !agree2 || isPending} + className="w-full" + > + 회원가입 완료 + </Button>Also applies to: 169-175
src/routes/routes.tsx (1)
28-30: TSX 확장자 포함 임포트는 기본 설정에서 실패합니다
DeleteReasonPage임포트에.tsx확장자를 직접 포함하고 있습니다. TS 기본 설정에서는 허용되지 않으며, 다른 임포트들과도 일관성이 깨집니다. 확장자를 제거해 주세요.-import DeleteReasonPage from '@/pages/setting/DeleteReasonPage.tsx'; +import DeleteReasonPage from '@/pages/setting/DeleteReasonPage';
♻️ Duplicate comments (2)
src/pages/auth/UserSetting.tsx (1)
66-66: 에러 객체 안전 접근: optional chaining 누락
err.response?.data.message는data가 undefined인 경우 런타임 에러가 납니다.data에도 optional chaining을 적용해 주세요. 과거 코멘트와 동일 이슈입니다.- alert(err.response?.data.message ?? '회원가입 중 문제가 발생했습니다.'); + alert(err.response?.data?.message ?? '회원가입 중 문제가 발생했습니다.');src/pages/setting/PaymentHistory.tsx (1)
31-33: h-full만으로는 높이 계산이 보장되지 않습니다 — min-h-screen 사용 권장부모가 명시적 높이를 갖지 않으면
h-full이 효과가 없습니다. 이전 리뷰에서도 지적된 바와 같이(모바일 100vh 이슈),min-h-screen+ flex 레이아웃으로 전환해 주세요.- <div className="flex h-full"> + <div className="min-h-screen flex flex-col">모바일 주소창 변동까지 고려하려면
min-h-[100dvh]를 사용해도 좋습니다.
📜 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 settings in your CodeRabbit configuration.
📒 Files selected for processing (6)
src/components/auth/commonAuthInput.tsx(1 hunks)src/layout/minimalLayout.tsx(1 hunks)src/pages/auth/UserSetting.tsx(7 hunks)src/pages/common/Error.tsx(1 hunks)src/pages/setting/PaymentHistory.tsx(2 hunks)src/routes/routes.tsx(3 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (6)
src/layout/minimalLayout.tsx (3)
src/components/layout/Header.tsx (2)
Header(18-90)IHeaderProps(14-16)src/layout/layout.tsx (1)
Layout(6-14)src/App.tsx (1)
App(7-9)
src/components/auth/commonAuthInput.tsx (1)
src/utils/formatPhoneNumber.tsx (1)
formatInputNumber(1-17)
src/pages/auth/UserSetting.tsx (2)
src/pages/auth/LoginPage.tsx (1)
Login(21-131)src/pages/auth/JoinPage.tsx (1)
Join(19-190)
src/pages/common/Error.tsx (5)
src/components/home/mainCard.tsx (1)
MainCard(6-8)src/components/alarmModal/alarm.tsx (1)
Alarm(5-12)src/layout/authLayout.tsx (1)
AuthLayout(5-16)src/components/dateCourse/info.tsx (1)
Info(11-31)src/components/dateCourse/dateCourseLoading.tsx (1)
DateCourseLoading(4-18)
src/pages/setting/PaymentHistory.tsx (2)
src/components/payment/PaymentRow.tsx (2)
PaymentRow(11-21)IPaymentRowProps(1-9)src/components/common/navigator.tsx (1)
Navigator(10-43)
src/routes/routes.tsx (8)
src/components/common/modalProvider.tsx (1)
ModalProvider(31-40)src/pages/common/Error.tsx (1)
Error(5-21)src/pages/setting/PaymentHistory.tsx (1)
PaymentHistory(14-74)src/pages/setting/DeleteReasonPage.tsx.tsx (1)
DeleteReasonPage(12-57)src/pages/setting/DeleteConfirmPage.tsx (1)
DeleteConfirmPage(28-139)src/pages/auth/WithdrawPage.tsx (1)
Withdraw(1-3)src/layout/layout.tsx (1)
Layout(6-14)src/App.tsx (1)
App(7-9)
🪛 Biome (2.1.2)
src/pages/auth/UserSetting.tsx
[error] 173-173: Avoid passing children using a prop
The canonical way to pass children in React is to use JSX elements
(lint/correctness/noChildrenProp)
🔇 Additional comments (1)
src/routes/routes.tsx (1)
67-71: ModalProvider + Layout 조합 유지, 구성 적절합니다루트 라우트에서 ModalProvider와 Layout을 함께 렌더링하고, 자식 라우트를 Outlet에 주입하는 구조는 합리적입니다. 에러 경로에서도 동일한 패턴을 유지하고 있어 일관성도 좋습니다.
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: 2
🔭 Outside diff range comments (1)
src/components/settingTab/InfoSetting.tsx (1)
98-105: readOnly 입력의 불필요한 onChange 제거읽기 전용이므로 빈 onChange는 제거해도 됩니다. 불필요한 함수 인스턴스 생성을 줄이세요.
<EditableInputBox label="이메일" value={infoLoading ? '불러오는 중' : infoError ? '' : email} readOnly - onChange={() => {}} className="pointer-events-none" placeholder="이메일" />
♻️ Duplicate comments (6)
src/components/settingTab/InfoSetting.tsx (2)
71-71: mutate 첫 번째 인자에 variables(undefined) 전달 수정 반영 완료옵션 객체를 2번째 인자로 분리한 점 좋습니다. 서버로 잘못된 payload가 전달될 가능성이 제거되었습니다.
72-79: 콜백의 any 제거(타입 추론에 맡기기)onSuccess의 res는 useResetPreferences의 제네릭에서 추론 가능합니다. 명시 any는 불필요하므로 제거하세요.
- onSuccess: (res: any) => { + onSuccess: (res) => { if (res?.isSuccess) { alert('취향 데이터가 초기화되었습니다.'); } else { alert(res?.message ?? '초기화에 실패했습니다.'); } },src/hooks/auth/useAccount.ts (1)
9-11: 변이 훅 제네릭 명시로 타입 안정성 강화과거 코멘트와 동일 제안입니다. 제네릭을 명시하면 API 시그니처 변경 시에도 타입 안정성이 높아집니다.
// 비밀번호 변경 function useChangePassword() { - return useCoreMutation(changePassword); + return useCoreMutation<Awaited<ReturnType<typeof changePassword>>, Parameters<typeof changePassword>[0]>(changePassword); } // 닉네임 변경 function useChangeNickname() { - return useCoreMutation(changeNickname); + return useCoreMutation<Awaited<ReturnType<typeof changeNickname>>, Parameters<typeof changeNickname>[0]>(changeNickname); } // 회원 탈퇴 function useDeleteMember() { - return useCoreMutation(deleteMember); + return useCoreMutation<Awaited<ReturnType<typeof deleteMember>>, void>(deleteMember); } // 취향 데이터 초기화 function useResetPreferences() { - return useCoreMutation(resetPreferences); + return useCoreMutation<Awaited<ReturnType<typeof resetPreferences>>, void>(resetPreferences); }Also applies to: 14-16, 19-21, 34-36
src/queryKey/queryKey.ts (3)
9-12: alarm 키도 동일 이슈 — 메서드명/서브키 중복 제거getAlarm/alarmSettings는 메서드명이 자동으로 붙습니다. 파라미터만 반환하고, 파라미터가 없다면 null을 사용하세요.
export const alarmKeys = createQueryKeys('alarm', { - all: null, - getAlarm: (size: number, cursor?: number) => ['getAlarm', size, cursor], - alarmSettings: () => ['alarmSettings'], + all: null, + getAlarm: (size: number, cursor?: number) => [size, cursor] as const, + alarmSettings: null, });
14-24: home 키 스킴 정규화 제안 — 상수 토큰 제거하고 파라미터만 반환메서드명이 자동 접두되므로 ['weather','forecast',…] 같은 정적 토큰은 불필요합니다. 파라미터만 반환하거나(있는 경우), 없는 경우 null을 사용해 최종 키를 간결하게 유지하세요.
export const homeKeys = createQueryKeys('home', { all: null, - getUserGrade: () => ['user', 'grade'], - dateCourseSave: () => ['date-courses', 'saved-count'], - weather: (startDate: string, regionId: number) => ['weather', 'forecast', startDate, regionId], - rainyInfo: (startDate: string, regionId: number) => ['rainy', 'forecast', startDate, regionId], + getUserGrade: null, + dateCourseSave: null, + weather: (startDate: string, regionId: number) => [startDate, regionId] as const, + rainyInfo: (startDate: string, regionId: number) => [startDate, regionId] as const, keywords: null, dateTimes: null, monthlyPlaceStates: null, - userRegion: () => ['user', 'region'], + userRegion: null, });
4-6: createQueryKeys 사용 시 메서드명 중복 포함됨 — 파라미터만 반환하도록 수정 필요search는 메서드명이 자동 접두되므로 배열에 'search'를 넣으면 최종 키가 ['region','search','search',keyword]처럼 중복됩니다. 파라미터만 반환하세요.
export const regionKeys = createQueryKeys('region', { - all: null, - search: (keyword: string) => ['search', keyword], + all: null, + search: (keyword: string) => [keyword] as const, });
📜 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 settings in your CodeRabbit configuration.
📒 Files selected for processing (3)
src/components/settingTab/InfoSetting.tsx(3 hunks)src/hooks/auth/useAccount.ts(1 hunks)src/queryKey/queryKey.ts(1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (3)
src/hooks/auth/useAccount.ts (4)
src/hooks/customQuery.ts (3)
useCoreMutation(21-73)useCoreQuery(8-19)useCoreMutation(20-28)src/api/auth/account.ts (5)
changePassword(7-10)changeNickname(13-16)deleteMember(19-21)getMemberInfo(24-27)getMemberGrade(30-33)src/queryKey/queryKey.ts (1)
memberKeys(30-34)src/api/dates/preferences.ts (1)
resetPreferences(6-9)
src/components/settingTab/InfoSetting.tsx (5)
src/api/auth/account.ts (1)
changeNickname(13-16)src/api/queryClient.ts (1)
queryClient(3-9)src/queryKey/queryKey.ts (1)
memberKeys(30-34)src/hooks/customQuery.ts (1)
useCoreMutation(20-28)src/pages/auth/UserSetting.tsx (1)
User(23-178)
src/queryKey/queryKey.ts (1)
src/hooks/home/useKeywordStates.ts (1)
useCoreQuery(7-12)
🔇 Additional comments (5)
src/components/settingTab/InfoSetting.tsx (2)
51-54: 멤버 등급 캐시 패치 로직 LGTMinvalidate 후 즉시 UI 반영을 위해 memberGrade 캐시를 부분 패치하는 접근이 적절하며, old?.result 가드도 잘 들어갔습니다.
116-116: 클래스명 확인 요청: rounding-32Tailwind 계열 컨벤션에서는 보통 rounded-… 형태를 씁니다. 프로젝트 유틸 클래스라면 OK이지만, 오탈자 가능성이 있어 확인 부탁드립니다.
Would you like me to scan the codebase for "rounding-32" usages to confirm if it's a defined utility class and not a typo?
src/queryKey/queryKey.ts (3)
26-29: notice 키 정의는 일관적이며 문제 없음getAllNotices가 파라미터 튜플만 반환하는 형태로 잘 정리되어 있습니다.
30-34: member 키 정의 LGTMall/memberInfo/memberGrade 모두 createQueryKeys 규칙에 맞습니다. 상위(all) 무효화로 관련 하위 쿼리 일괄 갱신이 가능해집니다.
1-35: 키 스킴 변경 영향 확인 및 사용처 점검 권장
제안된createQueryKeys반환 배열 형태 변경으로 기존invalidateQueries/setQueryData/useCoreQuery(…)호출이 의도한 키와 불일치할 수 있습니다. 아래 사용처를 검토해 주세요.
- src/hooks/course/useSearchRegion.tsx
• regionKeys.search(keyword).queryKey- src/hooks/alarm/useGetAlarm.ts
• alarmKeys.getAlarm(size, cursor).queryKey- src/hooks/settingAlarm/useAlarms.ts
• alarmKeys.alarmSettings().queryKey- src/components/settingTab/AlarmSetting.tsx
• queryClient.invalidateQueries({ queryKey: alarmKeys.alarmSettings().queryKey })- src/components/modal/regionModal.tsx
• queryClient.invalidateQueries({ queryKey: homeKeys.userRegion().queryKey })- src/hooks/home/useDateCourseStates.ts
• homeKeys.dateCourseSave().queryKey- src/hooks/home/useUserGrade.ts
• homeKeys.getUserGrade().queryKey- src/hooks/home/useUserRegion.ts
• homeKeys.userRegion().queryKey- src/hooks/home/useWeather.ts
• homeKeys.weather(startDate, regionId).queryKey
• homeKeys.rainyInfo(startDate, regionId).queryKey- src/hooks/customQuery.ts
• invalidateKeys.map(key ⇒ queryClient.invalidateQueries({ queryKey: key }))- src/components/settingTab/InfoSetting.tsx
• queryClient.invalidateQueries({ queryKey: memberKeys.all.queryKey })
• queryClient.setQueryData(memberKeys.memberGrade.queryKey, …)위 항목들이 변경된 키 스키마와 일치하는지, 필요한 경우 파라미터 순서나 토큰명을 수정해 주세요.
🚨 관련 이슈
#92
✨ 변경사항
✏️ 작업 내용
😅 미완성 작업
N/A
📢 논의 사항 및 참고 사항