[fix/MAT-920] 앱 내 반영#359
Merged
Merged
Conversation
…deeplink nav
handleNotificationPayload 가 markNotificationAsRead 를 await 한 뒤 deeplink 로
이동하던 구조에서, 콜드스타트 + 미인증 상태에 푸시 탭이 들어오면 다음 문제가 있었다.
- markNotificationAsRead 가 authMiddleware 를 거치는 client.POST 로 나가면서
reissue 실패 -> signOut() 사이드이펙트가 트리거될 수 있었다.
- read POST + 후속 syncNotificationBadgeCount 가 navigation 을 블로킹해
PublishScreen 진입까지 한 박자 더 걸렸다.
waitForRouteRegistered('StudentApp') 게이트를 handleNotificationPayload
진입부에 둬서 학생 세션 hydration 이 완료된 뒤에만 read 처리/딥링크가
진행되도록 하고, markNotificationAsRead 는 fire-and-forget(void) 으로
흘려보내 navigation 즉시 시작.
…ubscriber 기존에는 React Query 캐시와 OS 앱 아이콘 뱃지가 각자 다른 fetch 경로를 통해 갱신되어 (NotificationsScreen / useFcmToken / useDeepLinkHandler / useNotificationBadgeSync 가 각각 syncNotificationBadgeCount 를 직접 호출) 같은 사용자 액션에 같은 /notification/count 가 두 번 fetch 되거나, 잠시 두 값이 다른 round trip 결과를 들고 갈 위험이 있었다. useNotificationBadgeBridge 를 도입해 /api/student/notification/count 캐시 update 이벤트만 구독하고, 그 결과를 OS 뱃지로 push 한다. 호출자들은 이제 invalidate/ setQueryData/refetch 만 신경 쓰면 되고, OS 뱃지는 캐시를 따라간다. - useNotificationBadgeBridge: 새 훅, App.tsx 부트 1회 마운트 - useFcmToken.onMessage: syncNotificationBadgeCount -> invalidate count 캐시 - useDeepLinkHandler.markNotificationAsRead: invalidate 만 유지, sync 호출 제거 - useNotificationBadgeSync: foreground 복귀시 invalidate (refetchType: 'all') - NotificationsScreen: read/readAll onSuccess 의 직접 sync 호출과 handleReadAll 의 직접 setBadge/rollback 제거 (optimistic cache patch 만으로 subscriber 가 OS 뱃지 동기화) - notificationBadge.ts: syncNotificationBadgeCount 제거(dead code), setNotificationBadgeCount/clearNotificationBadgeCount 반환 타입 Promise<void> 로 단순화 (sentinel false/boolean 충돌 제거)
readNotification onSuccess 가 invalidate 만 하던 구조라 사용자가 단건 탭 후 서버 refetch 가 돌 때까지 hasBadge 가 잠시 남았다. handleReadAll 과 동일하게 count cache -1, 리스트 isRead=true 를 즉시 패치하고 onError 에서 rollback. OS 앱 아이콘 뱃지는 useNotificationBadgeBridge 가 count cache update 를 구독하므로 별도 처리 불필요.
서버 사이드(StudentNotificationController + NotificationFacade DEFAULT_DAY_LIMIT=7, MAT-907) 가 list / count / APNs badge 셋을 동일 7일 윈도우로 계산한다는 것을 JSDoc 로 남긴다. 클라이언트에서 dayLimit 을 임의로 바꾸면 alarms list, in-app tab bell, OS app icon badge 의 source 가 어긋날 수 있다.
usePostReadNotification 와 useDeepLinkHandler.markNotificationAsRead 가 각자 같은 SyntaxError swallow 워크어라운드를 들고 있어, 백엔드가 빈 body 응답을 고치면 두 군데를 같이 손봐야 했다. postReadNotification 헬퍼로 단일화해 client.POST + SyntaxError 처리 한 곳에서 관리한다. mutation hook 은 그 헬퍼를 mutationFn 으로 받아 단순화.
여러 위치(useDeepLinkHandler / useFcmToken / useNotificationBadgeSync / useNotificationBadgeBridge / NotificationsScreen / useIncalidateNotificationData) 에서 /api/student/notification 과 /api/student/notification/count 의 queryKey 를 각자 raw array 또는 TanstackQueryClient.queryOptions(...).queryKey 로 만들고 있었다. queryKeys.ts 한 곳에서 path 상수와 queryKey 를 export 해 모든 호출처가 같은 source-of-truth 를 쓰도록 통일. openapi-react-query 내부 표현이 바뀌더라도 한 곳만 손보면 된다.
…cationCount
openapi-react-query useQuery 시그니처는 (method, path, init?, options?) 인데
3번째 자리에 { enabled } 가 들어가 React Query options 가 아닌 init 으로
취급되고 있었다. 결과:
- enabled: false 를 넘겨도 query 가 비활성화되지 않음
- queryKey 가 ['get', path, { enabled }] 로 오염되어 queryKeys.ts 의
notificationCountQueryKey (['get', path, {}]) 와 형식이 어긋남
호출부가 전부 enabled 기본값 true 라 즉시 장애는 없었지만, count queryKey 를
source-of-truth 로 쓰는 이번 작업 컨텍스트에서 source 일치를 보장하도록
init 자리에 {} 를 두고 options 를 4번째 자리로 이동.
Code reviewNo issues found. Checked for bugs and CLAUDE.md compliance. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
앱 내 알림 목록/count/OS 앱 아이콘 뱃지가 모두 서버의 최근 7일 unread count를 기준으로 동작하도록 정리했습니다.
푸시 알림 탭 시 payload의
notificationId로 해당 알림을 읽음 처리하고, 딥링크 이동은 read API에 의해 지연되지 않도록 분리했습니다.React Query count cache를 OS 뱃지의 단일 source-of-truth로 사용하도록 bridge를 추가했습니다.
Linear
Changes
/api/student/notification/countReact Query cache 업데이트를 OS 앱 아이콘 뱃지로 반영하는 badge bridge를 추가했습니다.notificationId기반 개별 읽음 처리를 수행하고, 딥링크 navigation은 read 처리와 분리해 즉시 진행되도록 했습니다.Testing
pnpm --filter native typecheckRisk / Impact
notificationId를 내려주는지,/api/student/notification/count와 APNs badge count가 동일한 최근 7일 unread 기준인지, Android 런처별 badge 반영이 기대 범위인지notificationId추가 및 count/badge 계산 변경이 함께 배포되어야 푸시 탭 읽음 처리와 OS 뱃지 동기화가 완전하게 동작합니다.Screenshots / Video