Skip to content

feat: 인앱 알림 페이지 및 토스트 구현#217

Merged
ff1451 merged 9 commits intodevelopfrom
216-feat-인앱-알림-페이지-및-토스트-구현
Mar 26, 2026

Hidden character warning

The head ref may contain hidden characters: "216-feat-\uc778\uc571-\uc54c\ub9bc-\ud398\uc774\uc9c0-\ubc0f-\ud1a0\uc2a4\ud2b8-\uad6c\ud604"
Merged

feat: 인앱 알림 페이지 및 토스트 구현#217
ff1451 merged 9 commits intodevelopfrom
216-feat-인앱-알림-페이지-및-토스트-구현

Conversation

@ff1451
Copy link
Copy Markdown
Collaborator

@ff1451 ff1451 commented Mar 26, 2026

✨ 요약

- 인앱 알림 SSE 연동, unread count 조회, 읽음 처리 API를 추가했습니다.
- 헤더 알림 , 인앱 토스트, 알림 목록 페이지를 구현했습니다.
- 레이아웃/헤더/하단 네비게이션 구조를 알림 진입 흐름에 맞게 정리했습니다.



😎 해결한 이슈



Summary by CodeRabbit

릴리스 노트

  • 새로운 기능

    • 알림 페이지 추가 - 받은 알림 목록 조회 및 관리 가능
    • 실시간 인앱 알림 토스트 알림 추가
    • 알림 읽음 표시 기능 및 읽지 않은 알림 개수 추적
    • 무한 스크롤을 통한 알림 목록 로드
  • 개선 사항

    • 접근 토큰 새로고침 성능 최적화
    • 반응형 레이아웃 및 하단 네비게이션 겹침 처리 개선
    • UI 애니메이션 추가

@ff1451 ff1451 linked an issue Mar 26, 2026 that may be closed by this pull request
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 26, 2026

Warning

Rate limit exceeded

@ff1451 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 27 minutes and 37 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 808f7166-7577-4a87-adc6-fe33e86ef692

📥 Commits

Reviewing files that changed from the base of the PR and between 8166c14 and e3d2213.

⛔ Files ignored due to path filters (7)
  • src/assets/image/chat-cat-header.png is excluded by !**/*.png, !src/assets/** and included by **
  • src/assets/image/notification-toast-approved.png is excluded by !**/*.png, !src/assets/** and included by **
  • src/assets/image/notification-toast-general.png is excluded by !**/*.png, !src/assets/** and included by **
  • src/assets/svg/chat-icon.svg is excluded by !**/*.svg, !src/assets/** and included by **
  • src/assets/svg/notifications.svg is excluded by !**/*.svg, !src/assets/** and included by **
  • src/assets/svg/person-icon.svg is excluded by !**/*.svg, !src/assets/** and included by **
  • src/assets/svg/unread-notification.svg is excluded by !**/*.svg, !src/assets/** and included by **
📒 Files selected for processing (32)
  • src/App.tsx
  • src/apis/auth/index.ts
  • src/apis/notification/cache.ts
  • src/apis/notification/entity.ts
  • src/apis/notification/index.ts
  • src/apis/notification/queries.ts
  • src/components/common/Portal.tsx
  • src/components/layout/BottomNav/index.tsx
  • src/components/layout/Header/components/InfoHeader.tsx
  • src/components/layout/Header/components/ManagerHeader.tsx
  • src/components/layout/Header/components/NotificationBell.tsx
  • src/components/layout/Header/components/SubpageHeader.tsx
  • src/components/layout/Header/constants.ts
  • src/components/layout/Header/headerConfig.ts
  • src/components/layout/Header/index.tsx
  • src/components/layout/Header/presentation.ts
  • src/components/layout/Header/routeTitles.ts
  • src/components/layout/Header/types.ts
  • src/components/layout/hooks/useLayoutBottomOverlayInset.ts
  • src/components/layout/hooks/useLayoutElements.ts
  • src/components/layout/index.tsx
  • src/components/layout/layoutMetrics.ts
  • src/components/notification/InAppNotificationToast.tsx
  • src/components/notification/InboxNotificationLayer.tsx
  • src/components/notification/hooks/useInboxNotificationQueries.ts
  • src/components/notification/hooks/useInboxNotificationStream.ts
  • src/contexts/useLayoutElementsContext.tsx
  • src/index.css
  • src/pages/Notifications/index.tsx
  • src/stores/authStore.ts
  • src/utils/ts/accessToken.ts
  • src/utils/ts/notification.ts

Walkthrough

인앱 알림 페이지와 토스트 UI를 구현하는 변경입니다. SSE를 통한 실시간 알림 수신, 무한 스크롤 알림 목록 페이지, 알림 토스트 컴포넌트를 추가하고, 관련 API 클라이언트와 React Query 훅을 제공합니다. 동시에 토큰 갱신 동시성 제어, 헤더 및 바텀 네비게이션 구조 개선, 레이아웃 메트릭 기반 오버레이 관리를 포함합니다.

Possibly related PRs

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목 '인앱 알림 페이지 및 토스트 구현'은 변경사항의 주요 내용(인앱 알림 UI 페이지와 토스트 구현)을 명확하게 요약하고 있습니다.
Linked Issues check ✅ Passed PR #216의 목표(인앱 알림 목록 페이지와 토스트 구현)가 완벽하게 충족되었습니다. SSE 스트림, 알림 레이어, 페이지, 헤더 통합이 모두 구현되었습니다.
Out of Scope Changes check ✅ Passed 토큰 갱신 로직(refreshAccessToken) 개선, Portal 타입 확장, 헤더 구조 리팩토링 등은 모두 알림 기능 구현에 필수적인 인프라 개선사항입니다.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 216-feat-인앱-알림-페이지-및-토스트-구현

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@ff1451 ff1451 requested a review from Copilot March 26, 2026 11:45
@ff1451 ff1451 self-assigned this Mar 26, 2026
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a full in-app notification experience (SSE stream + unread count + read API) and integrates it into the app shell (header/bottom-nav/layout) so users can receive toasts and browse a notification inbox page.

Changes:

  • Implemented inbox notification data layer (entities, queries, cache helpers, APIs) and SSE streaming hook.
  • Added UI for notifications: header bell, in-app toast layer, and /notifications list page with infinite scroll + read handling.
  • Refactored layout/header/bottom-nav to support notification entry flow and bottom overlay inset positioning.

Reviewed changes

Copilot reviewed 32 out of 39 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/utils/ts/notification.ts Notification presentation mapping + helpers (message/icon/tone/path normalization).
src/utils/ts/accessToken.ts JWT exp parsing utilities for expiration checks.
src/stores/authStore.ts Reuses shared access token expiry logic.
src/pages/Notifications/index.tsx New notifications inbox page UI + navigation/read behavior.
src/pages/Notifications/hooks/useInboxNotifications.ts React Query hooks for inbox list, unread count, and mark-as-read mutation with cache updates.
src/index.css Adds fade-in-down animation; removes unused chat tooltip anchor support; minor selector formatting.
src/contexts/useLayoutElementsContext.tsx Extends layout context to include layoutElement and bottomOverlayInset.
src/components/notification/hooks/useInboxNotificationStream.ts SSE inbox notification stream hook + token refresh scheduling.
src/components/notification/InboxNotificationLayer.tsx Global layer that receives SSE notifications, updates caches, and shows toast queue.
src/components/notification/InAppNotificationToast.tsx Toast UI rendered via Portal and positioned above bottom overlays.
src/components/layout/layoutMetrics.ts Centralizes bottom overlay inset calculations and toast/list bottom gaps.
src/components/layout/index.tsx Layout refactor: header presentation, bottom inset CSS var, layout root ref, and notification layer mount.
src/components/layout/hooks/useLayoutElements.ts New hook to manage layout element refs + inset measurement.
src/components/layout/hooks/useLayoutBottomOverlayInset.ts Measures bottom overlay inset using bottom nav + viewport changes.
src/components/layout/Header/types.ts Adds notification header type.
src/components/layout/Header/routeTitles.ts Adds /notifications title mapping.
src/components/layout/Header/presentation.ts New header “presentation” resolver (type/title/padding).
src/components/layout/Header/index.tsx Uses presentation resolver; renders new notification header via SubpageHeader.
src/components/layout/Header/headerConfig.ts Routes /notifications to notification header type.
src/components/layout/Header/constants.ts Renames header height constant to SUBPAGE_HEADER_HEIGHT.
src/components/layout/Header/components/SubpageHeader.tsx New reusable fixed subpage header (back button + title + optional right slot).
src/components/layout/Header/components/NotificationBell.tsx Replaces chat bell with notifications bell + unread indicator icon and link to /notifications.
src/components/layout/Header/components/ManagerHeader.tsx Refactors to reuse SubpageHeader + notification bell slot.
src/components/layout/Header/components/InfoHeader.tsx Removes chat-tooltip behavior; uses new notification bell.
src/components/layout/BottomNav/index.tsx Switches to manual selection logic (Link + aria-current), including /notifications mapping to Home tab.
src/components/common/Portal.tsx Allows `DocumentFragment
src/assets/svg/unread-notification.svg New unread notification icon asset.
src/assets/svg/person-icon.svg New person icon asset.
src/assets/svg/notifications.svg New notifications icon asset.
src/assets/svg/chat-icon.svg New chat icon asset.
src/assets/image/notification-toast-approved.png New toast image asset (approved variant).
src/apis/notification/queries.ts Adds react-query key helpers for notification domain.
src/apis/notification/index.ts Adds inbox list/unread-count/read APIs.
src/apis/notification/entity.ts Adds inbox notification entities/types.
src/apis/notification/cache.ts Adds cache helpers (prepend notification, mark read, unread count inc/dec).
src/apis/auth/index.ts Deduplicates concurrent refresh token calls with a shared promise.
src/App.tsx Registers /notifications route under protected layout.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/components/notification/hooks/useInboxNotificationStream.ts Outdated
Comment thread src/components/notification/hooks/useInboxNotificationStream.ts Outdated
Comment thread src/pages/Notifications/index.tsx
Copy link
Copy Markdown
Contributor

@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: 6

🧹 Nitpick comments (9)
src/components/layout/Header/headerConfig.ts (1)

46-46: 기존 코드: == 대신 === 사용 권장

이 PR의 변경사항은 아니지만, Line 46에서 == 대신 ===를 사용하는 것이 TypeScript 컨벤션에 맞습니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/layout/Header/headerConfig.ts` at line 46, The match function
in headerConfig uses loose equality (match: (pathname) => pathname ==
'/schedule') which should be strict; update the arrow function in
headerConfig.ts to use === instead of == (i.e., change pathname == '/schedule'
to pathname === '/schedule') so the match comparison uses strict equality.
src/apis/notification/cache.ts (1)

1-6: import 순서 수정 필요

코딩 가이드라인에 따르면 import 순서는 external → internal(@/**) 순이어야 합니다. @tanstack/react-query@/apis/notification/entity 앞에 와야 합니다.

♻️ 수정 제안
+import type { InfiniteData } from '@tanstack/react-query';
+
 import type {
   InboxNotification,
   InboxNotificationListResponse,
   InboxNotificationUnreadCountResponse,
 } from '@/apis/notification/entity';
-import type { InfiniteData } from '@tanstack/react-query';

As per coding guidelines: "Follow import order: builtin → external → internal (@/**) → parent → sibling → index"

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/apis/notification/cache.ts` around lines 1 - 6, The imports are out of
order: external modules must come before internal alias imports; reorder the two
import blocks so the external import from '@tanstack/react-query' (InfiniteData)
appears above the internal '@/apis/notification/entity' import
(InboxNotification, InboxNotificationListResponse,
InboxNotificationUnreadCountResponse); update the import ordering in
src/apis/notification/cache.ts accordingly to follow builtin → external →
internal convention.
src/apis/notification/index.ts (1)

1-2: import 경로에 path alias 사용 권장

코딩 가이드라인에 따르면 상대 경로 대신 @/* path alias를 사용해야 합니다.

♻️ 수정 제안
 import type { InboxNotificationListResponse, InboxNotificationUnreadCountResponse } from '@/apis/notification/entity';
-import { apiClient } from '../client';
+import { apiClient } from '@/apis/client';

As per coding guidelines: "Use path alias @/* for imports instead of relative paths"

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/apis/notification/index.ts` around lines 1 - 2, The import in
src/apis/notification/index.ts uses a relative path for apiClient; update the
import of apiClient (symbol: apiClient) to use the project path alias (e.g.,
replace "../client" with the corresponding "@/..." alias used across the repo)
so it follows the coding guideline to use `@/`* path aliases; keep the existing
type import from '@/apis/notification/entity' unchanged and ensure the alias
matches your tsconfig/webpack settings.
src/App.tsx (1)

45-45: 신규 import는 alias 경로로 맞춰주세요.

Line 45는 새로 추가된 import라 @/* alias 규칙을 따르는 편이 좋습니다.

수정 예시
-const NotificationsPage = lazy(() => import('./pages/Notifications'));
+const NotificationsPage = lazy(() => import('@/pages/Notifications'));

As per coding guidelines "Use @/* alias for import paths instead of relative paths".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/App.tsx` at line 45, The lazy import using NotificationsPage currently
uses a relative path; update the import to use the project alias by changing the
lazy call for NotificationsPage (const NotificationsPage = lazy(() =>
import('./pages/Notifications'))) to import from the alias path (e.g.
import('@/pages/Notifications')) so it follows the "@/ *" import convention.
src/components/layout/Header/components/NotificationBell.tsx (1)

4-4: 공용 헤더가 페이지 레이어에 의존하고 있습니다.

NotificationBellsrc/pages/Notifications/hooks/useInboxNotifications를 직접 import하면 공용 레이아웃이 페이지 디렉터리에 묶입니다. unread count 훅은 notification 공용 레이어로 올려서 헤더와 페이지가 같이 쓰게 분리하는 편이 안전합니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/layout/Header/components/NotificationBell.tsx` at line 4,
NotificationBell currently imports useUnreadInboxNotificationCount from
src/pages/Notifications/hooks/useInboxNotifications, coupling the shared header
to the page layer; move the unread-count hook into the shared notification layer
and update imports: extract the hook logic from useUnreadInboxNotificationCount
(and any dependent types) into the common notification package/module (e.g.,
notification/hooks or notification/useUnreadInboxNotificationCount), export it
from that module, then change NotificationBell to import
useUnreadInboxNotificationCount from the new shared notification module and
remove any page-layer-only imports so both Header/NotificationBell and the
Notifications page consume the same shared hook.
src/components/layout/index.tsx (1)

9-11: 추가된 layout import는 alias로 맞춰주세요.

이번에 늘어난 import도 상대 경로를 쓰고 있어서 이 계층만 규칙이 다시 섞입니다. @/components/layout/... 형태로 통일하는 편이 좋겠습니다. As per coding guidelines, "Use path alias @/* for imports instead of relative paths".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/layout/index.tsx` around lines 9 - 11, The three imports in
index.tsx (SUBPAGE_HEADER_HEIGHT, getHeaderPresentation, useLayoutElements)
currently use relative paths; change them to use the project path alias format
(e.g. import { SUBPAGE_HEADER_HEIGHT } from
'@/components/layout/Header/constants', import { getHeaderPresentation } from
'@/components/layout/Header/presentation', and import { useLayoutElements } from
'@/components/layout/hooks/useLayoutElements') so all layout imports follow the
'@/...' alias convention.
src/components/layout/hooks/useLayoutElements.ts (1)

2-2: import 경로는 @/ alias로 통일해주세요.

신규 훅 파일이라 지금 상대 경로를 남기면 같은 레이어 안에서도 import 규칙이 섞입니다. As per coding guidelines, "Use path alias @/* for imports instead of relative paths".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/layout/hooks/useLayoutElements.ts` at line 2, The import in
useLayoutElements.ts uses a relative path for the hook; replace the relative
import of useLayoutBottomOverlayInset with the project path-alias form
(useLayoutBottomOverlayInset from
'@/components/layout/hooks/useLayoutBottomOverlayInset') so all imports follow
the "@/..." alias convention and avoid mixing relative paths with aliased
imports.
src/components/layout/BottomNav/index.tsx (1)

68-72: 바텀 네비 라벨은 타이포 토큰으로 맞춰주세요.

여기만 임의 값(text-[12px], leading-[1.6], font-semibold)을 쓰면 다른 라벨과 맞추기 어려워집니다. text-cap* 같은 토큰으로 치환하는 편이 좋겠습니다. As per coding guidelines, "Use semantic typography utilities (text-h1, text-body1, etc.) before custom styles".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/layout/BottomNav/index.tsx` around lines 68 - 72, The span in
the BottomNav component is using hardcoded typography classes (text-[12px],
leading-[1.6], font-semibold) which breaks consistency; replace those with the
project's semantic typography utility token (e.g., text-cap / text-cap-*, or the
equivalent token used across the app) and remove the custom leading/font weight
so the label inherits the token's styles while keeping the existing conditional
color (`isSelected ? 'text-primary-500' : 'text-text-400'`) logic intact; update
the span with the appropriate semantic class name instead of the three inline
tokens to match the app's typography system.
src/components/notification/InAppNotificationToast.tsx (1)

45-60: 새 토스트도 테마 토큰으로 맞춰주세요.

배경/텍스트 색상과 타이포가 rgba(...), hex, text-[..]로 직접 박혀 있어서 다른 화면과 일관성이 깨집니다. theme.css의 색상/타이포 토큰으로 맞춰 두는 편이 유지보수에 낫습니다.

As per coding guidelines, Prioritize color tokens from src/styles/theme.css and Use typography tokens (text-h1 through text-cap2) from src/styles/theme.css.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/notification/InAppNotificationToast.tsx` around lines 45 - 60,
The toast uses hardcoded colors and inline typography classes—replace those with
theme tokens from src/styles/theme.css: update the root container class
(currently using bg-[rgba(231,235,239,0.9)] and shadow) to use the appropriate
background and shadow color tokens; replace the message <p> classes
(text-[16px], text-text-700, leading) with the matching typography token (e.g.,
one of text-h1..text-cap2) and the theme text color token; and replace the
button classes (bg-white, text-[13px], text-[`#5A6B7F`], active:bg-[`#F4F6F9`]) with
the corresponding theme background/text/active color tokens. Modify the
className strings in InAppNotificationToast (root element, the <p> rendering
message, and the action <button>) to reference the theme tokens so styling is
consistent and maintainable.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/components/layout/BottomNav/index.tsx`:
- Line 25: The matchesPath implementation is too broad—matchesPath: (pathname)
=> pathname === '/home' || pathname === '/notifications'—so the Home link is
marked as the active page on /notifications; update matchesPath to only return
true when the route exactly matches the link target (e.g., pathname === to) so
aria-current="page" is only set for the actual page, or alternatively change the
logic to use location-based/section highlighting for non-page emphasis; apply
the same fix to the other occurrence around the second matchesPath block (lines
noted in the review).

In `@src/components/notification/hooks/useInboxNotificationStream.ts`:
- Around line 162-164: The reconnect path in useInboxNotificationStream
currently only waits NOTIFICATION_STREAM_RECONNECT_DELAY and reopens the stream
(check the block using signal.aborted and NOTIFICATION_STREAM_RECONNECT_DELAY
around lines ~162 and the reconnect logic around 220–243) but doesn't
resynchronize inbox caches; after a successful reconnect, call the app's query
invalidation for inbox-related data (e.g., use the existing
queryClient.invalidateQueries for the inbox/unread count keys) or emit a
reconnect event up (e.g., dispatch a "reconnect" through the same event emitter
used by the stream) so inbox list and unread count are refreshed; place this
invalidate/emit right after the stream is successfully re-established and guard
it by signal.aborted to avoid racing during shutdown.
- Around line 77-119: The retry flag hasRetriedAfterUnauthorized is scoped to
the whole function so once a 401 is handled it prevents refresh on later
reconnects; to fix, reset hasRetriedAfterUnauthorized when you successfully
establish a stream (i.e., after receiving an ok response from the fetch in
useInboxNotificationStream or just before you start processing the event
stream), so subsequent reconnect attempts can retry refresh on a fresh 401;
reference hasRetriedAfterUnauthorized, refreshAccessToken, and the
fetch/response.ok handling in useInboxNotificationStream and set the flag back
to false upon successful connection.

In `@src/components/notification/InboxNotificationLayer.tsx`:
- Around line 30-52: Prevent enqueuing while on the /notifications route by
adding a route check at the top of enqueueNotification (use your router or
location.pathname) and early-return when on that route; also, on component
mount/route-enter, clear the toast queue via setNotificationQueue([]) so
previously buffered notifications are dropped. Update enqueueNotification (and
related usages that call setNotificationQueue like the startTransition block) to
skip updating the queue and skip calling prependInboxNotification only when the
current path is "/notifications" while still updating server-side state like
seenNotificationIdsRef or unread counts as needed.

In `@src/pages/Notifications/hooks/useInboxNotifications.ts`:
- Around line 38-47: The unreadCount is always decremented in the onSuccess
handler even when the notification was already read, causing the badge to
underflow; change the onSuccess logic so you first compute the read-state
transition via setInboxNotificationReadState (or a helper) and only call
decrementInboxUnreadCount when that update actually changed a notification from
unread→read. In practice, have setInboxNotificationReadState return whether a
transition happened (or return the updated list and detect the change by
comparing previousData), then call
queryClient.setQueryData(notificationQueryKeys.inbox.unreadCount(), ...) to
decrement only when the transition flag is true.

In `@src/pages/Notifications/index.tsx`:
- Around line 97-110: handleNotificationClick currently returns early when
markAsRead throws, blocking navigation; change the try/catch around markAsRead
so failures are swallowed (optionally log or trigger the existing toast) and do
not return, then continue to compute destinationPath via
normalizeInboxNotificationPath and call navigate(destinationPath) as before;
refer to handleNotificationClick, markAsRead, normalizeInboxNotificationPath,
and navigate when making this change.

---

Nitpick comments:
In `@src/apis/notification/cache.ts`:
- Around line 1-6: The imports are out of order: external modules must come
before internal alias imports; reorder the two import blocks so the external
import from '@tanstack/react-query' (InfiniteData) appears above the internal
'@/apis/notification/entity' import (InboxNotification,
InboxNotificationListResponse, InboxNotificationUnreadCountResponse); update the
import ordering in src/apis/notification/cache.ts accordingly to follow builtin
→ external → internal convention.

In `@src/apis/notification/index.ts`:
- Around line 1-2: The import in src/apis/notification/index.ts uses a relative
path for apiClient; update the import of apiClient (symbol: apiClient) to use
the project path alias (e.g., replace "../client" with the corresponding "@/..."
alias used across the repo) so it follows the coding guideline to use `@/`* path
aliases; keep the existing type import from '@/apis/notification/entity'
unchanged and ensure the alias matches your tsconfig/webpack settings.

In `@src/App.tsx`:
- Line 45: The lazy import using NotificationsPage currently uses a relative
path; update the import to use the project alias by changing the lazy call for
NotificationsPage (const NotificationsPage = lazy(() =>
import('./pages/Notifications'))) to import from the alias path (e.g.
import('@/pages/Notifications')) so it follows the "@/ *" import convention.

In `@src/components/layout/BottomNav/index.tsx`:
- Around line 68-72: The span in the BottomNav component is using hardcoded
typography classes (text-[12px], leading-[1.6], font-semibold) which breaks
consistency; replace those with the project's semantic typography utility token
(e.g., text-cap / text-cap-*, or the equivalent token used across the app) and
remove the custom leading/font weight so the label inherits the token's styles
while keeping the existing conditional color (`isSelected ? 'text-primary-500' :
'text-text-400'`) logic intact; update the span with the appropriate semantic
class name instead of the three inline tokens to match the app's typography
system.

In `@src/components/layout/Header/components/NotificationBell.tsx`:
- Line 4: NotificationBell currently imports useUnreadInboxNotificationCount
from src/pages/Notifications/hooks/useInboxNotifications, coupling the shared
header to the page layer; move the unread-count hook into the shared
notification layer and update imports: extract the hook logic from
useUnreadInboxNotificationCount (and any dependent types) into the common
notification package/module (e.g., notification/hooks or
notification/useUnreadInboxNotificationCount), export it from that module, then
change NotificationBell to import useUnreadInboxNotificationCount from the new
shared notification module and remove any page-layer-only imports so both
Header/NotificationBell and the Notifications page consume the same shared hook.

In `@src/components/layout/Header/headerConfig.ts`:
- Line 46: The match function in headerConfig uses loose equality (match:
(pathname) => pathname == '/schedule') which should be strict; update the arrow
function in headerConfig.ts to use === instead of == (i.e., change pathname ==
'/schedule' to pathname === '/schedule') so the match comparison uses strict
equality.

In `@src/components/layout/hooks/useLayoutElements.ts`:
- Line 2: The import in useLayoutElements.ts uses a relative path for the hook;
replace the relative import of useLayoutBottomOverlayInset with the project
path-alias form (useLayoutBottomOverlayInset from
'@/components/layout/hooks/useLayoutBottomOverlayInset') so all imports follow
the "@/..." alias convention and avoid mixing relative paths with aliased
imports.

In `@src/components/layout/index.tsx`:
- Around line 9-11: The three imports in index.tsx (SUBPAGE_HEADER_HEIGHT,
getHeaderPresentation, useLayoutElements) currently use relative paths; change
them to use the project path alias format (e.g. import { SUBPAGE_HEADER_HEIGHT }
from '@/components/layout/Header/constants', import { getHeaderPresentation }
from '@/components/layout/Header/presentation', and import { useLayoutElements }
from '@/components/layout/hooks/useLayoutElements') so all layout imports follow
the '@/...' alias convention.

In `@src/components/notification/InAppNotificationToast.tsx`:
- Around line 45-60: The toast uses hardcoded colors and inline typography
classes—replace those with theme tokens from src/styles/theme.css: update the
root container class (currently using bg-[rgba(231,235,239,0.9)] and shadow) to
use the appropriate background and shadow color tokens; replace the message <p>
classes (text-[16px], text-text-700, leading) with the matching typography token
(e.g., one of text-h1..text-cap2) and the theme text color token; and replace
the button classes (bg-white, text-[13px], text-[`#5A6B7F`], active:bg-[`#F4F6F9`])
with the corresponding theme background/text/active color tokens. Modify the
className strings in InAppNotificationToast (root element, the <p> rendering
message, and the action <button>) to reference the theme tokens so styling is
consistent and maintainable.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 6133d1e6-5c85-440e-80b6-6b97c4df8b94

📥 Commits

Reviewing files that changed from the base of the PR and between b2f8251 and 8166c14.

⛔ Files ignored due to path filters (7)
  • src/assets/image/chat-cat-header.png is excluded by !**/*.png, !src/assets/** and included by **
  • src/assets/image/notification-toast-approved.png is excluded by !**/*.png, !src/assets/** and included by **
  • src/assets/image/notification-toast-general.png is excluded by !**/*.png, !src/assets/** and included by **
  • src/assets/svg/chat-icon.svg is excluded by !**/*.svg, !src/assets/** and included by **
  • src/assets/svg/notifications.svg is excluded by !**/*.svg, !src/assets/** and included by **
  • src/assets/svg/person-icon.svg is excluded by !**/*.svg, !src/assets/** and included by **
  • src/assets/svg/unread-notification.svg is excluded by !**/*.svg, !src/assets/** and included by **
📒 Files selected for processing (32)
  • src/App.tsx
  • src/apis/auth/index.ts
  • src/apis/notification/cache.ts
  • src/apis/notification/entity.ts
  • src/apis/notification/index.ts
  • src/apis/notification/queries.ts
  • src/components/common/Portal.tsx
  • src/components/layout/BottomNav/index.tsx
  • src/components/layout/Header/components/InfoHeader.tsx
  • src/components/layout/Header/components/ManagerHeader.tsx
  • src/components/layout/Header/components/NotificationBell.tsx
  • src/components/layout/Header/components/SubpageHeader.tsx
  • src/components/layout/Header/constants.ts
  • src/components/layout/Header/headerConfig.ts
  • src/components/layout/Header/index.tsx
  • src/components/layout/Header/presentation.ts
  • src/components/layout/Header/routeTitles.ts
  • src/components/layout/Header/types.ts
  • src/components/layout/hooks/useLayoutBottomOverlayInset.ts
  • src/components/layout/hooks/useLayoutElements.ts
  • src/components/layout/index.tsx
  • src/components/layout/layoutMetrics.ts
  • src/components/notification/InAppNotificationToast.tsx
  • src/components/notification/InboxNotificationLayer.tsx
  • src/components/notification/hooks/useInboxNotificationStream.ts
  • src/contexts/useLayoutElementsContext.tsx
  • src/index.css
  • src/pages/Notifications/hooks/useInboxNotifications.ts
  • src/pages/Notifications/index.tsx
  • src/stores/authStore.ts
  • src/utils/ts/accessToken.ts
  • src/utils/ts/notification.ts

Comment thread src/components/layout/BottomNav/index.tsx
Comment thread src/components/notification/hooks/useInboxNotificationStream.ts Outdated
Comment thread src/components/notification/hooks/useInboxNotificationStream.ts
Comment thread src/components/notification/InboxNotificationLayer.tsx
Comment thread src/components/notification/hooks/useInboxNotificationQueries.ts Outdated
Comment thread src/pages/Notifications/index.tsx
@ff1451 ff1451 force-pushed the 216-feat-인앱-알림-페이지-및-토스트-구현 branch from de441ba to b32ac18 Compare March 26, 2026 12:02
@ff1451 ff1451 merged commit 4540932 into develop Mar 26, 2026
2 checks passed
ff1451 added a commit that referenced this pull request Apr 1, 2026
* 205 feat 이미지 전처리 기능 구현 (#206)

* feat: 전처리 로직 및 WebWorker 구현

* feat: 전처리 적용 및 preview 동시성 제어 로직 추가

* refactor: 리뷰 반영

* [hotfix] 하단바 너비 수정 (#208)

* hotfix: 하단바 너비 수정

* chore: 불필요한 값 제거

* refactor: 고정 gap 제거

* Reapply "[feat] 광고 배너 추가 (#200)"

This reverts commit c51ec85.

* [feat] 하단바 리디자인 (#213)

* chore: asset 추가

* feat: 하단바 리디자인 반영 및 레이아웃 수정

* [refactor] 광고 카드 레이아웃 밀림 수정 (#215)

* refactor: 광고 개수 측정 시기 변경 및 기본값 제거

* feat: 스켈레톤 UI 추가

* feat: 인앱 알림 페이지 및 토스트 구현 (#217)

* feat: 알림 API 및 스트림 기반 추가

* feat: 인앱 알림 레이어 추가

* feat: 알림 페이지 및 헤더 진입 구현

* fix: 알림 스트림 401 재시도 조건 정리

* fix: 알림 목록 이동 차단 제거

* refactor: 알림 공용 훅 위치 정리

* fix: 알림 재연결 캐시 동기화 추가

* fix: 알림 목록 토스트 큐 누적 방지

* fix: 알림 읽음 카운트 감소 조건 보강

* [refactor] 도메인별 TanStack Query 훅 정리 (#219)

* chore: pwa용 이미지 제거

* refactor: auth 도메인 쿼리와 뮤테이션 정리

* refactor: council과 schedule 조회 훅 정리

* refactor: chat과 notification 캐시 처리 정리

* refactor: club 조회와 지원 플로우 정리

* refactor: manager 도메인 캐시 처리 정리

* refactor: studyTime 도메인 쿼리와 뮤테이션 정리

* refactor: 광고와 업로드 도메인 훅 정리

* [refactor] mutaton query 및 hook 추가 수정 (#221)

* refactor: auth와 user myInfo 훅 정리

* refactor: club과 schedule 조회 훅 정리

* refactor: chat과 notification 훅 구조 정리

* refactor: club 지원 뮤테이션 훅 정리

* refactor: manager 뮤테이션 훅 구조 정리

* refactor: mutation 훅 cache 정리

* refactor: 컨벤션 통일

* refactor: isRead 조건 정리

* fix: 채팅 스크롤 문제 수정

* refactor: 불필요한 코드 제거

* [fix] 모바일 환경 입력창과 키보드 간의 간격이 큰 문제 수정 (#223)

* chore: 가공용 safeArea 변수 선언

* refactor: 고정 패딩 값 수정 및 safeArea 적용 변경

* feat: 키보드 활성화 감지 및 safeArea 적용 여부 기능 추가

* refactor: 매직넘버 상수화 및 가로모드 처리

* [fix] 키보드 활성화 시 화면 흔들림 문제 수정 (#225)

* refactor: 채팅 viewport 훅 네이밍 정리

* refactor: viewport 높이 잠금 훅 적용 시점 조정

* [fix] 키보드 활성화 시 채팅 화면 전체가 흔들리는 문제 수정 (#227)

* refactor: 채팅 viewport 훅 네이밍 정리

* refactor: viewport 높이 잠금 훅 적용 시점 조정

* fix: 채팅 화면 스크롤 잠금으로 키보드 흔들림 완화

* fix: 입력 포커스 중 viewport offset 고정 (#229)

* fix: 문서 루트 스크롤 잠금으로 빈 공간 잔류 방지 (#232)

* [fix] 키보드 활성화 시 채팅 화면 상단 고정이 깨지고 빈 공간이 남는 문제 수정 (#234)

* fix: 채팅 화면 상단 고정 깨짐과 빈 공간 잔류 수정

* refactor: 라우트 조건 수정

* fix: 문서 스크롤 위치 감지 보강

* refactor: 입력 요소 판별 유틸과 스크롤 주석 정리

* [fix] 키보드 활성화 시 채팅 화면에서 문서 스크롤이 발생하는 문제 수정 (#236)

* fix: 채팅 문서 스크롤 제스처 차단

* fix: 입력 요소 터치 동작 예외 처리

* [fix] 키보드 활성화 시 채팅방이 마지막 메시지 위치를 유지하지 못하는 문제 수정 (#238)

* fix: 키보드 활성화 시 채팅 하단 정렬 유지

* refactor: 채팅 리사이즈 관찰 안정화

* fix: mypage 연계 약관 페이지 뒤로가기 수정 (#240)

* refactor: alias import 경로 정리

* fix: query 설정과 suspense 분기 정리

* refactor: 관리자 화면 스타일 유틸 정리

* fix: 이미지 전처리 예외 처리 보강

* fix: 헤더와 회비 화면 동작 정리

* fix: 공통 유틸 안정성 개선

* fix: 이미지 전처리 실패 처리를 보정

* fix: 모집 공고 저장 후 설정 반영 순서 조정

* fix: 부원 직책 변경 실패 처리를 보강

* fix: 약관 링크 접근성을 개선

* fix: 공통 쿼리와 유틸 안정성을 보완

* [feat] 동적 버전 정보 표시 구현 (#211)

* feat: 동적 버전 정보 표시 구현

* refactor: 버전 정보 미 존재시 v 표시 제거

* [feat] 메인화면 동아리 카드 디자인 수정 반영 (#242)

* feat: 메인화면 동아리 카드 디자인 수정

* chore: 하단바 아이콘 수정

* refactor: 코드래빗 리뷰 반영

* refactor: and 연산자로 변경

* apiClient 코드 중복 제거 및 네이티브 브릿지 인증 동기화 중앙화 (#244)

* refactor: apiClient 코드 중복 제거 및 네이티브 브릿지 인증 동기화 중앙화

* refactor: body 직렬화 가드를 plain object/array로 한정

* fix: body 읽기 중 AbortError가 ParseError로 오분류되는 문제 수정

* [refactor] 에러 처리 유틸 및 utils 구조 정리 (#246)

* refactor: 에러 처리 유틸 및 공통 토스트 흐름 정리

* refactor: utils 폴더 구조를 역할별로 정리

* refactor: 코드래빗 리뷰 반영

* refactor: 코드래빗 리뷰 반영

* Update src/pages/Home/components/HomeClubSection.tsx

* fix: 인증 세션 복구 흐름 정리

* fix: 홈 동아리 카드 레이아웃 정리

* [feat] 총동아리 페이지 리디자인 및 하단 오버레이 정리 (#249)

* refactor: 하단 오버레이 처리 공통화

* feat: 총동아리 페이지와 헤더 리디자인 반영

* fix: 채팅 하단 여백과 외부 링크 속성 수정

* refactor: 총동아리 헤더 설정 정리

* fix: 총동아리 상세 접근성과 스타일 보완

* [feat] 마이페이지 관리자 카드 분리 및 채팅 미확인 배지 반영 (#251)

* feat: 하단 채팅 배지 표시 및 조회 조건 보완

* refactor: 관리자 정보 카드 컴포넌트 분리

* feat: 채팅 페이지 리디자인 (#252)

* feat: 채팅 페이지 리디자인

* fix:tailwind 문법 수정

* fix: 코드 수정

* fix: 폰트 색상 및 위치 수정

* fix: 채팅방 사람수 정렬

* fix: 오타 수정

* chore: conflict 해결 중 누락된 부분 수정

* [refactor] 광고 렌더링 조건 수정 (#254)

* refactor: 광고 렌더링 조건 수정

* docs: 문서명 변경

---------

Co-authored-by: 박성주 <145267904+ParkSungju01@users.noreply.github.com>
@ff1451 ff1451 deleted the 216-feat-인앱-알림-페이지-및-토스트-구현 branch April 7, 2026 09:33
ff1451 added a commit that referenced this pull request Apr 21, 2026
* [배포] 이미지 전처리, 광고 기능, 하단바 리디자인, 인앱 알림 페이지 및 토스트 프로덕션 배포 (#230)

* 205 feat 이미지 전처리 기능 구현 (#206)

* feat: 전처리 로직 및 WebWorker 구현

* feat: 전처리 적용 및 preview 동시성 제어 로직 추가

* refactor: 리뷰 반영

* [hotfix] 하단바 너비 수정 (#208)

* hotfix: 하단바 너비 수정

* chore: 불필요한 값 제거

* refactor: 고정 gap 제거

* Reapply "[feat] 광고 배너 추가 (#200)"

This reverts commit c51ec85.

* [feat] 하단바 리디자인 (#213)

* chore: asset 추가

* feat: 하단바 리디자인 반영 및 레이아웃 수정

* [refactor] 광고 카드 레이아웃 밀림 수정 (#215)

* refactor: 광고 개수 측정 시기 변경 및 기본값 제거

* feat: 스켈레톤 UI 추가

* feat: 인앱 알림 페이지 및 토스트 구현 (#217)

* feat: 알림 API 및 스트림 기반 추가

* feat: 인앱 알림 레이어 추가

* feat: 알림 페이지 및 헤더 진입 구현

* fix: 알림 스트림 401 재시도 조건 정리

* fix: 알림 목록 이동 차단 제거

* refactor: 알림 공용 훅 위치 정리

* fix: 알림 재연결 캐시 동기화 추가

* fix: 알림 목록 토스트 큐 누적 방지

* fix: 알림 읽음 카운트 감소 조건 보강

* [refactor] 도메인별 TanStack Query 훅 정리 (#219)

* chore: pwa용 이미지 제거

* refactor: auth 도메인 쿼리와 뮤테이션 정리

* refactor: council과 schedule 조회 훅 정리

* refactor: chat과 notification 캐시 처리 정리

* refactor: club 조회와 지원 플로우 정리

* refactor: manager 도메인 캐시 처리 정리

* refactor: studyTime 도메인 쿼리와 뮤테이션 정리

* refactor: 광고와 업로드 도메인 훅 정리

* [refactor] mutaton query 및 hook 추가 수정 (#221)

* refactor: auth와 user myInfo 훅 정리

* refactor: club과 schedule 조회 훅 정리

* refactor: chat과 notification 훅 구조 정리

* refactor: club 지원 뮤테이션 훅 정리

* refactor: manager 뮤테이션 훅 구조 정리

* refactor: mutation 훅 cache 정리

* refactor: 컨벤션 통일

* refactor: isRead 조건 정리

* fix: 채팅 스크롤 문제 수정

* refactor: 불필요한 코드 제거

* [fix] 모바일 환경 입력창과 키보드 간의 간격이 큰 문제 수정 (#223)

* chore: 가공용 safeArea 변수 선언

* refactor: 고정 패딩 값 수정 및 safeArea 적용 변경

* feat: 키보드 활성화 감지 및 safeArea 적용 여부 기능 추가

* refactor: 매직넘버 상수화 및 가로모드 처리

* [fix] 키보드 활성화 시 화면 흔들림 문제 수정 (#225)

* refactor: 채팅 viewport 훅 네이밍 정리

* refactor: viewport 높이 잠금 훅 적용 시점 조정

* [fix] 키보드 활성화 시 채팅 화면 전체가 흔들리는 문제 수정 (#227)

* refactor: 채팅 viewport 훅 네이밍 정리

* refactor: viewport 높이 잠금 훅 적용 시점 조정

* fix: 채팅 화면 스크롤 잠금으로 키보드 흔들림 완화

* fix: 입력 포커스 중 viewport offset 고정 (#229)

* fix: 문서 루트 스크롤 잠금으로 빈 공간 잔류 방지 (#232)

* [fix] 키보드 활성화 시 채팅 화면 상단 고정이 깨지고 빈 공간이 남는 문제 수정 (#234)

* fix: 채팅 화면 상단 고정 깨짐과 빈 공간 잔류 수정

* refactor: 라우트 조건 수정

* fix: 문서 스크롤 위치 감지 보강

* refactor: 입력 요소 판별 유틸과 스크롤 주석 정리

* [fix] 키보드 활성화 시 채팅 화면에서 문서 스크롤이 발생하는 문제 수정 (#236)

* fix: 채팅 문서 스크롤 제스처 차단

* fix: 입력 요소 터치 동작 예외 처리

* [fix] 키보드 활성화 시 채팅방이 마지막 메시지 위치를 유지하지 못하는 문제 수정 (#238)

* fix: 키보드 활성화 시 채팅 하단 정렬 유지

* refactor: 채팅 리사이즈 관찰 안정화

* fix: mypage 연계 약관 페이지 뒤로가기 수정 (#240)

* refactor: alias import 경로 정리

* fix: query 설정과 suspense 분기 정리

* refactor: 관리자 화면 스타일 유틸 정리

* fix: 이미지 전처리 예외 처리 보강

* fix: 헤더와 회비 화면 동작 정리

* fix: 공통 유틸 안정성 개선

* fix: 이미지 전처리 실패 처리를 보정

* fix: 모집 공고 저장 후 설정 반영 순서 조정

* fix: 부원 직책 변경 실패 처리를 보강

* fix: 약관 링크 접근성을 개선

* fix: 공통 쿼리와 유틸 안정성을 보완

* [feat] 동적 버전 정보 표시 구현 (#211)

* feat: 동적 버전 정보 표시 구현

* refactor: 버전 정보 미 존재시 v 표시 제거

* [feat] 메인화면 동아리 카드 디자인 수정 반영 (#242)

* feat: 메인화면 동아리 카드 디자인 수정

* chore: 하단바 아이콘 수정

* refactor: 코드래빗 리뷰 반영

* refactor: and 연산자로 변경

* apiClient 코드 중복 제거 및 네이티브 브릿지 인증 동기화 중앙화 (#244)

* refactor: apiClient 코드 중복 제거 및 네이티브 브릿지 인증 동기화 중앙화

* refactor: body 직렬화 가드를 plain object/array로 한정

* fix: body 읽기 중 AbortError가 ParseError로 오분류되는 문제 수정

* [refactor] 에러 처리 유틸 및 utils 구조 정리 (#246)

* refactor: 에러 처리 유틸 및 공통 토스트 흐름 정리

* refactor: utils 폴더 구조를 역할별로 정리

* refactor: 코드래빗 리뷰 반영

* refactor: 코드래빗 리뷰 반영

* Update src/pages/Home/components/HomeClubSection.tsx

* fix: 인증 세션 복구 흐름 정리

* fix: 홈 동아리 카드 레이아웃 정리

* [feat] 총동아리 페이지 리디자인 및 하단 오버레이 정리 (#249)

* refactor: 하단 오버레이 처리 공통화

* feat: 총동아리 페이지와 헤더 리디자인 반영

* fix: 채팅 하단 여백과 외부 링크 속성 수정

* refactor: 총동아리 헤더 설정 정리

* fix: 총동아리 상세 접근성과 스타일 보완

* [feat] 마이페이지 관리자 카드 분리 및 채팅 미확인 배지 반영 (#251)

* feat: 하단 채팅 배지 표시 및 조회 조건 보완

* refactor: 관리자 정보 카드 컴포넌트 분리

* feat: 채팅 페이지 리디자인 (#252)

* feat: 채팅 페이지 리디자인

* fix:tailwind 문법 수정

* fix: 코드 수정

* fix: 폰트 색상 및 위치 수정

* fix: 채팅방 사람수 정렬

* fix: 오타 수정

* chore: conflict 해결 중 누락된 부분 수정

* [refactor] 광고 렌더링 조건 수정 (#254)

* refactor: 광고 렌더링 조건 수정

* docs: 문서명 변경

---------

Co-authored-by: 박성주 <145267904+ParkSungju01@users.noreply.github.com>

* refactor: 가이드 페이지 이미지 변경 및 구조 개선 (#260)

* hotfix: 가이드 이미지 경로 변경

---------

Co-authored-by: 박성주 <145267904+ParkSungju01@users.noreply.github.com>
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.

[feat] 인앱 알림 페이지 및 토스트 구현

2 participants