Skip to content

refactor: 알림 관련 파일 리팩토링 - #42

Merged
youngmis merged 3 commits into
devfrom
feature/estimate-request
Jul 31, 2026
Merged

refactor: 알림 관련 파일 리팩토링#42
youngmis merged 3 commits into
devfrom
feature/estimate-request

Conversation

@youngmis

@youngmis youngmis commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

📋 작업 내용

알림 작업까지 완료하고 전체 검토하여 추가 리팩토링이 필요한 부분 진행


🔥 변경 사항

  1. Header.tsx: 알림 트리거 import 경로를 Header/notification으로 변경
  2. NotificationPanel.tsx (삭제→notification/로 재생성): 목록·페이지네이션을 분리하고 포커스 트랩을 맡도록 옮김
  3. NotificationTrigger.tsxnotification/NotificationTrigger.tsx: 열림/닫힘·포커스 복귀만 담당하도록 옮기고 Escape는 Panel로 넘김
  4. notificationMessages.tsnotification/notificationMessages.ts: 알림 문구 템플릿을 notification 폴더로 이동
  5. NotificationItem.tsx (신규): 알림 한 줄 UI와 메시지/a11y 처리를 분리
  6. NotificationPagination.tsx (신규): 알림 패널 페이지네이션 UI를 분리
  7. notification/index.ts (신규): notification 모듈 barrel export를 추가
  8. useNotifications.tshooks/notifications/: 목록 조회 훅을 notifications 폴더로 이동
  9. useReadNotification.tshooks/notifications/: 읽음 mutation 훅을 notifications 폴더로 이동
  10. useUnreadNotificationCount.tshooks/notifications/: 미읽음 수 훅을 notifications 폴더로 이동
  11. useFocusTrap.ts (신규): 포커스 이동·Tab 트랩·Escape 공통 훅을 추가
  12. focusable.ts (신규): 포커스 가능 요소 조회 유틸을 추가
  13. Modal.tsx: 로컬 포커스 헬퍼 대신 공통 getFocusableElements를 쓰도록 변경
  14. notifications.ts (api): 미사용 readAllNotifications를 제거
  15. apiRoutes.ts: 미사용 READ_ALL 경로를 제거
  16. notification.ts (types): 미사용 ReadAllNotificationsResponse 타입을 제거
  17. notifications.mock.ts (삭제): 더 이상 쓰이지 않는 mock 데이터를 삭제

✅ 체크리스트

  • 로컬에서 정상 동작을 확인했습니다.
  • 기존 기능에 영향을 주지 않는지 확인했습니다.
  • 불필요한 console.log를 제거했습니다.
  • lint를 통과했습니다.
  • README 또는 문서를 수정했습니다. (필요 시)
  • API 명세와 일치하는지 확인했습니다.

📷 스크린샷 (선택)


🔗 관련 이슈

Closes #


💬 To Reviewer

리팩토링 검토 부탁드립니다.

Summary by CodeRabbit

  • 새 기능

    • 알림 패널에서 알림 목록의 로딩·오류·빈 상태를 확인할 수 있습니다.
    • 알림을 개별적으로 읽음 처리하면 목록과 읽지 않은 알림 수가 즉시 갱신됩니다.
    • 이전·다음 및 개별 페이지 버튼으로 알림 목록을 탐색할 수 있습니다.
    • 링크가 있는 알림에서 관련 화면으로 바로 이동할 수 있습니다.
  • 접근성 개선

    • 키보드 포커스를 패널 내부로 제한하고 Escape 키와 닫기 버튼으로 종료할 수 있습니다.
    • 알림 읽음 상태와 현재 페이지 정보가 보조기기에 전달됩니다.

Co-authored-by: Cursor <cursoragent@cursor.com>
@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: f20f420d-c1bd-4c90-9d75-32f8e9375355

📥 Commits

Reviewing files that changed from the base of the PR and between 0712595 and 45eeae9.

📒 Files selected for processing (3)
  • src/components/common/Header/Header.tsx
  • src/components/common/Header/notification/NotificationTrigger.tsx
  • src/lib/constants/apiRoutes.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/components/common/Header/Header.tsx
  • src/lib/constants/apiRoutes.ts
  • src/components/common/Header/notification/NotificationTrigger.tsx

📝 Walkthrough

Walkthrough

알림 패널을 notification/ 구조로 재구성했습니다. 알림 조회, 개별 읽음 처리, 페이지네이션, 포커스 트랩, 접근성 처리를 추가했습니다. 전체 읽음 처리 API와 관련 타입 및 목 데이터를 제거했습니다.

Changes

알림 기능

Layer / File(s) Summary
알림 조회와 읽음 처리
src/hooks/notifications/*, src/lib/constants/apiRoutes.ts, src/types/notification.ts
알림 목록과 미읽음 수 조회 훅을 추가했습니다. 개별 읽음 처리에 낙관적 갱신, 실패 복구, 완료 후 무효화를 적용했습니다. 전체 읽음 처리 경로와 응답 타입을 제거했습니다.
공유 포커스 제어
src/hooks/useFocusTrap.ts, src/lib/utils/focusable.ts, src/components/common/Modal.tsx
포커스 가능 요소 조회 유틸리티와 포커스 트랩 훅을 추가했습니다. Modal은 공유 유틸리티를 사용합니다.
알림 패널 UI와 헤더 연결
src/components/common/Header/notification/*, src/components/common/Header/Header.tsx
알림 항목, 페이지네이션, 새 패널을 추가했습니다. 로딩·오류·빈 상태, 읽음 처리, Escape 종료, 접근성 속성을 처리합니다. 기존 패널 파일을 제거하고 새 배럴 모듈을 연결했습니다.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Header
  participant NotificationTrigger
  participant NotificationPanel
  participant NotificationsAPI
  Header->>NotificationTrigger: 알림 버튼 표시
  NotificationTrigger->>NotificationPanel: 패널 열기
  NotificationPanel->>NotificationsAPI: 페이지별 알림 조회
  NotificationsAPI-->>NotificationPanel: 알림 목록과 페이지 정보 반환
  NotificationPanel->>NotificationsAPI: 개별 알림 읽음 처리
  NotificationsAPI-->>NotificationPanel: 갱신된 알림 반환
  NotificationPanel-->>NotificationTrigger: 닫기 처리
Loading

Possibly related PRs

Suggested reviewers: wndnjs2037, 9g-g9

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 62.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목은 알림 모듈의 파일 구조와 책임을 리팩토링하는 PR의 주요 변경 사항을 명확하게 요약합니다.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/estimate-request

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (2)
src/components/common/Header/notification/NotificationPagination.tsx (1)

28-67: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

페이지 수가 많아지면 페이지네이션이 넘칠 수 있습니다.

Array.from({ length: pageCount }, ...)는 페이지 번호 버튼을 모두 렌더링합니다. <ul>에는 flex-wrap이나 overflow-x-auto가 없습니다. NotificationPanel의 폭은 w-[359px]로 고정되어 있습니다. 페이지 수가 늘어나면 버튼이 패널 폭을 초과해 잘리거나 넘칠 수 있습니다.

가로 스크롤 처리나 페이지 윈도잉(예: 현재 페이지 주변만 표시하고 생략 부호 사용)을 추가하는 것을 권장합니다.

♻️ 최소 수정 예시: 가로 스크롤 허용
-      <ul className="flex items-center gap-4">
+      <ul className="flex items-center gap-4 overflow-x-auto">
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/common/Header/notification/NotificationPagination.tsx` around
lines 28 - 67, Update the pagination list in NotificationPagination to prevent
page buttons from exceeding the fixed panel width: add horizontal overflow
handling to the <ul> or replace the full pageCount rendering with a windowed
page list that includes ellipses. Preserve current-page selection, navigation
callbacks, and disabled states.
src/components/common/Header/notification/NotificationPanel.tsx (1)

65-78: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

aria-modal="true"인데 배경 스크롤 잠금이 없습니다.

경로 지침은 모달에 대해 role="dialog", aria-modal, 포커스 트랩, ESC 닫기, 배경 스크롤 잠금을 요구하며 Modal.tsx를 참고 사례로 지정합니다. 이 패널은 포커스 트랩과 ESC 닫기는 구현했지만 배경 스크롤 잠금은 없습니다. aria-modal="true"로 선언하면 스크린 리더 사용자는 배경이 비활성화된 것으로 인식하지만, 실제로는 배경 스크롤이 가능해 선언된 상태와 동작이 불일치합니다.

Modal.tsx와 동일한 스크롤 잠금을 추가하거나, 드롭다운형 패널로 의도했다면 aria-modal 값을 재검토하십시오.

As per path instructions, "모달은 role="dialog", aria-modal, 포커스 트랩, ESC 닫기, 배경 스크롤 잠금이 필요합니다. Modal.tsx가 참고 사례입니다."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/common/Header/notification/NotificationPanel.tsx` around lines
65 - 78, Update the notification panel component around its dialog state and the
existing panelRef handling to add background scroll locking while the panel is
open, matching the established behavior in Modal.tsx. Ensure the lock is applied
on open and reliably removed on close or unmount, while preserving the existing
focus-trap and ESC-dismiss behavior.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/common/Header/notification/NotificationPanel.tsx`:
- Around line 48-63: Update handleNotificationActivate to catch and absorb
errors from await markAsRead(notification.id), while retaining the existing
finally cleanup of pendingReadIds. Do not rethrow the mutation failure, since
the existing onError handler already restores the cache.
- Around line 24-46: Update the NotificationPanel pagination flow around
currentPage, pageCount, and safePage so that when the fetched pagination
metadata shows currentPage exceeds pageCount, the current page state is
automatically reset to pageCount. Ensure the correction occurs after pageCount
is derived and avoids unnecessary state updates, while preserving the existing
bounded goToPage behavior.

In `@src/hooks/useFocusTrap.ts`:
- Around line 22-76: Update the focus-trapping effect in useFocusTrap to capture
document.activeElement before moving focus into the container, then restore that
element during cleanup when it is still connected to the document. Preserve the
existing keydown handling and focus behavior, including the container fallback
when no focusable elements exist.

In `@src/lib/utils/focusable.ts`:
- Around line 1-11: Update getFocusableElements to return only elements eligible
for Tab traversal by requiring element.tabIndex >= 0, which excludes
tabindex="-1" buttons and links. Also exclude descendants of disabled fieldsets
using element.matches(":disabled"), while preserving the existing hidden,
aria-hidden, and disabled-element filtering.

---

Nitpick comments:
In `@src/components/common/Header/notification/NotificationPagination.tsx`:
- Around line 28-67: Update the pagination list in NotificationPagination to
prevent page buttons from exceeding the fixed panel width: add horizontal
overflow handling to the <ul> or replace the full pageCount rendering with a
windowed page list that includes ellipses. Preserve current-page selection,
navigation callbacks, and disabled states.

In `@src/components/common/Header/notification/NotificationPanel.tsx`:
- Around line 65-78: Update the notification panel component around its dialog
state and the existing panelRef handling to add background scroll locking while
the panel is open, matching the established behavior in Modal.tsx. Ensure the
lock is applied on open and reliably removed on close or unmount, while
preserving the existing focus-trap and ESC-dismiss behavior.
🪄 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 Plus

Run ID: 6ee7265b-e638-4e40-afad-66aa0a7358e3

📥 Commits

Reviewing files that changed from the base of the PR and between d5e2fe8 and f835c16.

📒 Files selected for processing (18)
  • src/components/common/Header/Header.tsx
  • src/components/common/Header/NotificationPanel.tsx
  • src/components/common/Header/notification/NotificationItem.tsx
  • src/components/common/Header/notification/NotificationPagination.tsx
  • src/components/common/Header/notification/NotificationPanel.tsx
  • src/components/common/Header/notification/NotificationTrigger.tsx
  • src/components/common/Header/notification/index.ts
  • src/components/common/Header/notification/notificationMessages.ts
  • src/components/common/Modal.tsx
  • src/hooks/notifications/useNotifications.ts
  • src/hooks/notifications/useReadNotification.ts
  • src/hooks/notifications/useUnreadNotificationCount.ts
  • src/hooks/useFocusTrap.ts
  • src/lib/api/notifications.ts
  • src/lib/constants/apiRoutes.ts
  • src/lib/mocks/notifications.mock.ts
  • src/lib/utils/focusable.ts
  • src/types/notification.ts
💤 Files with no reviewable changes (5)
  • src/types/notification.ts
  • src/lib/constants/apiRoutes.ts
  • src/lib/api/notifications.ts
  • src/components/common/Header/NotificationPanel.tsx
  • src/lib/mocks/notifications.mock.ts

Comment thread src/components/common/Header/notification/NotificationPanel.tsx
Comment thread src/components/common/Header/notification/NotificationPanel.tsx
Comment thread src/hooks/useFocusTrap.ts
Comment thread src/lib/utils/focusable.ts

@juengseulki juengseulki left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📋 PR 리뷰

👍 좋았던 점

  • Notification 관련 컴포넌트와 훅을 notification 모듈로 정리해 역할이 훨씬 명확해졌습니다.
  • NotificationItem, NotificationPagination으로 UI를 분리하면서 NotificationPanel이 조합 역할만 담당하도록 개선했습니다.
  • 알림 조회, 읽음 처리, 미읽음 개수 훅을 hooks/notifications로 모아 관련 로직을 한 곳에서 관리할 수 있도록 정리했습니다.
  • useFocusTrapgetFocusableElements를 공통화해 Modal과 NotificationPanel이 동일한 포커스 정책을 사용할 수 있도록 개선한 점이 좋았습니다.
  • 사용하지 않는 API, 타입, mock 데이터를 함께 제거해 죽은 코드까지 정리한 점도 좋았습니다.
    접근성 측면에서 Escape 종료, 포커스 복귀, 페이지 상태 안내 등을 함께 반영해 사용자 경험을 개선했습니다.

🔍 확인 및 제안

  • useFocusTrap을 공통 훅으로 분리한 만큼 이후 Drawer, Dropdown 등 포커스 트랩이 필요한 컴포넌트에서도 동일한 훅을 사용할 계획인지 궁금합니다.
  • NotificationPanel의 역할이 렌더링 조합에 집중되면서 구조가 훨씬 명확해졌습니다.
  • Modal도 동일한 getFocusableElements 유틸을 사용하도록 변경되어 포커스 정책을 한 곳에서 관리할 수 있게 된 점이 좋았습니다.

전체적으로 기능 변경보다는 Notification 모듈의 구조 개선과 접근성 공통화에 초점을 맞춘 리팩토링이었습니다. 역할 분리가 잘 이루어졌고, 불필요한 코드까지 함께 정리되어 유지보수성이 좋아진 PR이라고 생각합니다. 수고하셨습니다! 😊

@juengseulki
juengseulki requested a review from wndnjs2037 July 31, 2026 08:29
신영미 and others added 2 commits July 31, 2026 17:39
Co-authored-by: Cursor <cursoragent@cursor.com>
@youngmis
youngmis merged commit 88df8c6 into dev Jul 31, 2026
1 check passed
@wndnjs2037

Copy link
Copy Markdown
Collaborator

리팩토링하느라 고생 많으셨습니다!
NotificationPanel을 조합 역할만 남기고 NotificationItem, NotificationPagination으로 분리해주신 부분과 getFocusableElements를 공통화하면서 tabIndex, disabled 필터까지 보강해서 Modal과 포커스 정책이 한 곳으로 모아주신 부분도 읽기 좋게 리팩토링 된 것 같아요 👍
앞으로도 이렇게 역할 단위로 컴포넌트를 나누는 방향으로 작업해주시면 좋을 것 같습니다!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants