Skip to content

[feat] 페이지 접근 권한 전역 관리 구현#105

Merged
mgYang53 merged 4 commits intodevelopfrom
feat/page-permission-99
Mar 7, 2026
Merged

[feat] 페이지 접근 권한 전역 관리 구현#105
mgYang53 merged 4 commits intodevelopfrom
feat/page-permission-99

Conversation

@mgYang53
Copy link
Contributor

@mgYang53 mgYang53 commented Mar 5, 2026

🚀 풀 리퀘스트 제안

📋 작업 내용

현재 프로젝트는 로그인 여부와 온보딩 완료 여부만 라우트 레벨에서 체크하고 있어, 비멤버가 URL 직접 입력으로 모임 상세/설정/약속 하위 페이지 등에 접근할 수 있는 문제가 있었습니다.

인터셉터에서 권한 에러 코드를 감지하고 커스텀 DOM 이벤트(permission-denied)를 dispatch하면, RootLayout에 등록된 usePermissionRedirect 훅이 에러 토스트와 홈 리다이렉트를 처리하는 방식으로 SPA 내비게이션을 유지하면서 한 곳에서 통합 관리합니다.

🔧 변경 사항

  • errors.ts: PAGE_ACCESS_ERROR_CODES Set 추가 — 리다이렉트 대상 에러 코드 6개 정의 (GA002, GA003, M003, M004, M006, R105)
  • interceptors.ts: 권한 에러 감지 후 permission-denied 커스텀 이벤트 dispatch
  • usePermissionRedirect.ts: 신규 훅 생성 — 이벤트 listen → showErrorToast + navigate(HOME, { replace: true })
  • RootLayout.tsx: usePermissionRedirect() 연결 (모든 페이지에 자동 적용)
  • MeetingRetrospectiveCreatePage.tsx: useEffect에서 권한 에러 스킵 처리 (이중 내비게이션 방지)

📄 기타

관련 이슈: #99

이슈 체크리스트 변경 사항:

  • TopicCreatePageuseMeetingDetail 쿼리 추가는 추후 처리로 미룸. 현재는 mutation 제출 시 권한 에러가 발생하여 인터셉터가 감지하고 홈으로 리다이렉트됨.
  • MeetingRetrospectiveCreatePageuseMeetingDetail 추가 대신, 이미 존재하는 useCollectedAnswers 쿼리가 권한 체크 역할을 하며, 기존 useEffect의 이중 내비게이션 충돌을 수정하는 방식으로 처리.

Summary by CodeRabbit

  • 개선 사항
    • 권한 부족 상황에 대한 전역 처리 추가: 접근 거부 시 사용자에게 명확한 오류 알림을 표시하고 홈으로 자동 리다이렉트됩니다.
    • 반복되는 접근 거부 알림을 묶어 중복 알림을 줄였습니다.
    • 각 페이지의 기존 로컬 오류 처리 로직은 전역 처리로 위임되어 동일한 오류에 대해 중복된 모달/토스트가 표시되지 않습니다.

인터셉터에서 권한 에러 감지 시 커스텀 DOM 이벤트를 dispatch하고,
RootLayout의 usePermissionRedirect 훅에서 토스트와 홈 리다이렉트를
처리하여 한 곳에서 통합 관리.

- errors.ts: PAGE_ACCESS_ERROR_CODES Set 추가 (6개 에러 코드)
- interceptors.ts: permission-denied 커스텀 이벤트 dispatch
- usePermissionRedirect: 이벤트 → 토스트 + navigate(HOME)
- RootLayout: usePermissionRedirect 훅 연결
- MeetingRetrospectiveCreatePage: 이중 내비게이션 방지
@mgYang53 mgYang53 linked an issue Mar 5, 2026 that may be closed by this pull request
13 tasks
@coderabbitai
Copy link

coderabbitai bot commented Mar 5, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 2abaf124-e26e-424e-823e-580574ca3c29

📥 Commits

Reviewing files that changed from the base of the PR and between f79e3eb and 6061c0f.

📒 Files selected for processing (2)
  • src/pages/Retrospectives/meeting/MeetingRetrospectiveCreatePage.tsx
  • src/pages/Topics/TopicCreatePage.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/pages/Retrospectives/meeting/MeetingRetrospectiveCreatePage.tsx

Walkthrough

권한 거부 관련 에러를 전역으로 처리하도록 변경합니다. 인터셉터가 특정 에러 코드를 감지하면 'permission-denied' 커스텀 이벤트를 방출하고, 전역 훅이 이를 받아 토스트를 띄운 뒤 홈으로 리다이렉트합니다. 일부 페이지의 로컬 에러 처리는 건너뜁니다.

Changes

Cohort / File(s) Summary
에러 코드 정의
src/api/errors.ts
PAGE_ACCESS_ERROR_CODES 상수 추가 (6개 권한 관련 에러 코드). diff에 동일 블록이 중복 삽입된 것으로 보이므로 파일 내 중복 등록 여부 확인 필요.
API 계층 이벤트 발행
src/api/interceptors.ts
응답 에러 처리에서 PAGE_ACCESS_ERROR_CODES에 포함된 코드 발견 시 permission-denied CustomEvent 발행. 이벤트 중복 방지를 위해 1초 디바운스 로직 추가.
전역 권한 거부 훅
src/shared/hooks/usePermissionRedirect.ts, src/shared/hooks/index.ts
usePermissionRedirect 훅 추가 — permission-denied 이벤트 리스닝, 토스트 표시(showErrorToast) 후 ROUTES.HOMEreplace:true 네비게이션. 인덱스에서 export 추가.
RootLayout 통합
src/shared/layout/RootLayout.tsx
RootLayout에서 usePermissionRedirect() 호출 추가로 앱 전역에서 이벤트 처리 활성화.
로컬 처리 건너뜀
src/pages/Retrospectives/...MeetingRetrospectiveCreatePage.tsx, src/pages/Topics/TopicCreatePage.tsx
로컬 에러 핸들러에서 PAGE_ACCESS_ERROR_CODES에 해당하면 기존 토스트/모달/네비게이션 처리를 건너뛰도록 수정 (전역 훅으로 대체).

Sequence Diagram(s)

sequenceDiagram
    participant Client as Client
    participant Interceptor as API 인터셉터
    participant Window as Window
    participant Hook as usePermissionRedirect
    participant Toast as Toast UI
    participant Router as React Router

    Client->>Interceptor: API 요청
    Interceptor-->>Client: 권한 거부 에러 응답

    rect rgba(255, 100, 100, 0.5)
    Interceptor->>Interceptor: PAGE_ACCESS_ERROR_CODES 검사\n(1초 디바운스 로직)
    end

    alt 권한 거부 + 디바운스 통과
        Interceptor->>Window: 'permission-denied' 이벤트 발행 (detail: ErrorMessage)
        Window->>Hook: 이벤트 수신
        Hook->>Toast: showErrorToast(message)
        Hook->>Router: navigate(ROUTES.HOME, {replace:true})
    else 디바운스 차단 또는 기타 에러
        Interceptor->>Client: 기존 ApiError 반환/거부
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related issues

Suggested reviewers

  • choiyoungae
  • haruyam15
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목은 주요 변경 사항인 페이지 접근 권한의 전역 관리 구현을 명확하고 간결하게 요약하고 있습니다.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/page-permission-99

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.

@mgYang53 mgYang53 changed the title feat: 페이지 접근 권한 전역 관리 구현 (#99) [feat] 페이지 접근 권한 전역 관리 구현 Mar 5, 2026
@mgYang53 mgYang53 self-assigned this Mar 5, 2026
@mgYang53 mgYang53 added feat 새로운 기능 추가 labels Mar 5, 2026
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 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/api/interceptors.ts`:
- Around line 124-129: The permission-denied event is currently dispatched on
every failed response (see PAGE_ACCESS_ERROR_CODES, ErrorMessage lookup and the
CustomEvent('permission-denied') dispatch), causing duplicate toasts/redirects
on concurrent failures; add a short-window duplicate guard (e.g., module-scoped
or window-scoped lastPermissionDeniedAt timestamp) checked before dispatching
and updated after dispatch so that if Date.now() - lastPermissionDeniedAt <
threshold (e.g., 1–2s) you skip emitting the event — keep the existing
errorMessage/detail logic and only suppress duplicate dispatches within that
timeframe.

In `@src/shared/hooks/usePermissionRedirect.ts`:
- Around line 19-21: The event handler in usePermissionRedirect (the handler
function) currently destructures message from (e as CustomEvent).detail without
null checks; change handler to safely extract the payload: verify e is a
CustomEvent and that e.detail exists and that detail.message is a string (or use
optional chaining and a default fallback) before calling showErrorToast, and
pass a default error message when message is missing to avoid runtime crashes in
the global permission hook.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 184af1fc-bd2b-4772-9430-559bb98e39ae

📥 Commits

Reviewing files that changed from the base of the PR and between ff88378 and 15f8d4c.

📒 Files selected for processing (6)
  • src/api/errors.ts
  • src/api/interceptors.ts
  • src/pages/Retrospectives/MeetingRetrospectiveCreatePage.tsx
  • src/shared/hooks/index.ts
  • src/shared/hooks/usePermissionRedirect.ts
  • src/shared/layout/RootLayout.tsx

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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/pages/Retrospectives/meeting/MeetingRetrospectiveCreatePage.tsx`:
- Around line 66-69: Extract a small helper (e.g., isPageAccessError) that
checks PAGE_ACCESS_ERROR_CODES.has(error.code) and use it wherever permission
errors need to be excluded: replace the inline check inside the existing
useEffect that handles useCollectedAnswers errors and add the same early-return
guard at sttMutation.onError so that both flows skip showErrorToast for
PAGE_ACCESS_ERROR_CODES; ensure the helper is imported/defined in the same
module and used by name in both useEffect and sttMutation.onError to avoid
duplicate toasts.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: c84ee131-95b5-4d82-b22e-c6715fc2a82e

📥 Commits

Reviewing files that changed from the base of the PR and between 0737b69 and f79e3eb.

📒 Files selected for processing (1)
  • src/pages/Retrospectives/meeting/MeetingRetrospectiveCreatePage.tsx

@mgYang53 mgYang53 merged commit 2ab89d8 into develop Mar 7, 2026
2 checks passed
@mgYang53 mgYang53 deleted the feat/page-permission-99 branch March 7, 2026 16:10
mgYang53 added a commit that referenced this pull request Mar 7, 2026
* [refactor] 사전의견 폴더 병합 (#92)

* refactor: 사전의견 관련 폴더 병합 (#91)

* fix: SubPageHeader 그림자 prop 추가 (#91)

* refactor: 코드 포매팅 (#91)

* fix: 빌드 에러 수정 (#91)

* fix: 개인 회고 nullable 필드 처리 및 목 모드 조기 반환 추가 (#91)

* chore: 개인회고 관련 파일 삭제 (#91)

* feat: 약속 회고 API 레이어 구축 (#94) (#95)

* feat: 약속 회고 API 레이어 구축 (#94)

STT Job 생성, 요약 조회/수정/발행 API 함수 및 React Query 훅 구현.
AbortController 기반 STT 요청 취소 지원, setQueryData 캐시 전략 적용.

* style: prettier 포맷 수정 (#94)

* refactor: 코드 리뷰 반영 (#94)

- api 함수 api.* 헬퍼로 통일
- useCreateSttJob race condition 수정
- useCreateSttJob 언마운트 시 자동 취소
- KeyPointUpdateRequest 타입 alias로 변경

* feat: 에러 코드 추가 및 사전 의견 버튼 disabled 처리 (#97) (#98)

신규 에러 코드 5개(E000, M016, M017, R106, R108) 추가.
MyMeetingListItem에 preOpinionTemplateConfirmed 필드 추가 후,
메인페이지 예정 약속의 사전 의견 작성 버튼 disabled 처리 반영.

* [feat] 약속회고 생성 전 플로우 UI 및 기능 개발 (#100)

* feat: 약속 완료 시 약속 상세 페이지 UI 변경(#87)

* feat: 약속 회고 상세 페이지 추가 (#87)

* feat:아코디언 컴포넌트 추가(#87)

* design: 아코디언 컴포넌트css 수정(#87)

* feat: 수집된 사전의견 조회 api 개발(#87)

* feat: Dropzone 공통컴포넌트 생성 및 파일 업로드 기능구현(#87)

* design: AI요약 버튼 디자인 수정(#87)

* style: 프리티어(#87)

* refactor: 파일 업로드 검증 강화 및 의존성 정리 (#87)

Dropzone 파일 타입 검증 기능 추가 및 radix-ui 의존성 정리로 코드 품질 개선.

- Dropzone 파일 타입 검증 로직 추가
- 파일 타입 불일치 시 onTypeRejected 콜백 호출
- alert를 toast로 변경하여 일관된 에러 처리
- radix-ui 제거 후 @radix-ui/react-accordion 추가
- Accordion, AlertIcon 컴포넌트 코드 정리
- Import/export 경로 정리 및 불필요한 mock export 제거

* refactor: 에러처리 개선 (#87)

* design: 아코디언css 수정 (#87)

* refactor: GetCollectedAnswersParams 배럴 추가(#87)

* style: 주석제거(#87)

* refactor: AlertIcon 색 커스텀 기능 추가(#87)

* refactor: 회고 아이콘을 SVG 파일로 전환 (#87)

- MeetingRetroIcon, PersonalRetroIcon 컴포넌트를 SVG 파일로 대체
- useCollectedAnswers 훅의 유효성 검사 강화 (정수 체크, pageSize 검증)
- MeetingDetailPage 에러 처리 중복 실행 방지

* refactor: AlertIcon적용 (#87)

* refactor: AlertIcon size prop추가(#87)

* refactor: 주제 목록 에러 처리 개선 (#87)

* fix: 버튼 프로퍼티수정(#87)

* feat: 회고 상태 기반 라우트 분기 처리 추가 (#87)

* feat: 목데이터에 회고상태  추가(#87)

* refactor: params검증로직 페이지에서 처리(#87)

* [feat] 개인회고 작성 및 조회 화면 구현 (#101)

* feat: 개인 회고 작성 화면 구현 (#90)

* feat: 개인 회고 전송 기능 구현 (#90)

* feat: 개인회고 조회 화면 구현 (#90)

* feat: 개인회고 삭제, 수정 구현 (#90)

* chore 미사용 유틸리티 제거 (#90)

* refactor: 코드 포매팅 (#90)

* fix: 코드리뷰 반영 (#90)

* fix: 코드 리뷰 반영 (#90)

* refactor: 코드 포매팅 (#90)

* refactor: 개인회고 폴더 정리 (#90)

* fix: 코드 리뷰 반영 (#90)

* feat: 약속 회고 결과 페이지 UI 및 기능 개발 (#93) (#102)

* feat: 약속 회고 결과 페이지 UI 및 기능 개발 (#93)

- AI 요약 결과 조회, 수정, 발행 기능 구현
- TopicSummaryCard: 구조화된 개별 input으로 주요포인트 편집
- FormPageHeader: children, onBack prop 추가
- 발행 성공 시 DetailPage로 이동
- AiLoadingOverlay SVG 직접 import 방식으로 변경
- summary 관련 목데이터 추가

* fix: 코드 리뷰 반영 (#93)

- TopicSummaryCard 편집 모드 keyPoints map key를 인덱스에서 stable id로 교체
  (EditableKeyPoint 타입 추가, crypto.randomUUID()로 id 생성)
- mock abort 에러를 DOMException에서 Error('canceled')로 변경해 axios 취소 동작 일치
- 회고 요약 목데이터를 전역 단일 객체에서 meetingId별 Record로 리팩터링
- MeetingRetrospectiveResultPage에 isError 처리 및 다시 시도 버튼 추가
- window.history.replaceState를 navigate(location.pathname, replace)로 교체
- FormPageHeader props를 discriminated union으로 타입 강화

* style: prettier 포맷 수정 (#93)

* fix: 코드 리뷰 반영 2차 (#93)

- 상세 항목(details)에도 EditableDetail 타입으로 stable id 적용
  (추가/삭제 시 input 포커스 혼동 방지)
- handleSaveEdit에서 EditableDetail[] → string[]으로 직렬화 처리
- mock STT abort: aborted 즉시 감지, once 옵션으로 리스너 중복 방지, resolve 시 리스너 정리
- meetingId 유효성 검사 강화 (Number.isInteger && > 0)

* fix: 코드 리뷰 반영 3차 (#93)

- guard를 핸들러 정의 이전으로 이동해 gatheringId/meetingId non-null assertion 제거
- topic prop을 항상 SummaryTopic[]으로 유지해 union 타입 불일치 해결
- EditableDetail/EditableKeyPoint를 components/index.ts에서 함께 re-export

* [fix] 책 상세 페이지, 내 책장 페이지 API 스펙 반영 (#104)

* refactor: 감상 기록 액션 메뉴 분리 (#59)

* refactor: Book/Keywords API 목데이터와 엔드포인트 분리 (#59)

* design: 감상 기록 액션 메뉴 관련 z-index 조정 (#59)

* feat: 별점 필터링에 0점 추가 (#59)

* refactor: 내 책장 무한스크롤 시 useInfiniteScroll 활용 (#59)

* feat: 내 책장에 등록된 책이 없을 경우 툴팁 추가 (#59)

* feat: 기록 타임라인의 사전의견에 답변이 없는 주제 처리 추가 (#59)

* refactor: sticky한 헤더에 대해 drop-shadow-bottom 및 useScrollCollapse 활용 (#59)

* fix: 책 타임라인 api 반영 (#59)

* fix: 책 리스트 조회 api 반영 (#59)

* fix: 책 리스트 삭제 api 반영 및 디자인 수정 (#59)

* fix: 도서 API 스펙 변경에 따른 타입 및 컴포넌트 업데이트 (#59)

* fix: 약속 회고 아이템에서 미연동 액션 메뉴 제거 (#59)

* feat: 도서 읽기 상태 토글에 낙관적 업데이트 적용 (#59)

* refactor: 코드 포매팅 (#59)

* fix: 메인페이지 ReadingBooksSection API 파라미터 및 응답 필드 타입 오류 수정 (#59)

* fix: 내책장 편집 모드 진입 시 filteredBookIds 초기화 (#59)

* fix: mock API 파라미터 계약 및 라우트 상수 관련 수정 (#59)

* fix: PRE_OPINION 아이템 React key 중복 가능성 수정 (#59)

* refactor: RootLayout에서 불필요한 overflow-x-clip 제거 (#59)

* refactor: BookLogList 삭제 로직을 useBookLogDeleteActions 훅으로 분리 (#59)

* [feat] 약속회고 조회 및 댓글 기능 구현  (#103)

* feat: 약속 회고상세 UI개발(#96)

* refactor: 회고 관련 파일 구조 meeting 폴더로 재구성 (#96)

* feat: 댓글 Textarea 높이조절 기능구현(#96)

* feat: 약속회고 댓글 기능 구현 (#96)

- 약속회고 상세 페이지에 댓글 작성/삭제 기능 추가.
- 무한 스크롤로 댓글 목록 조회하며, 약속장 및 댓글 작성자만 삭제 가능.

* fix: 약속회고 댓글 에러 처리 및 안정성 개선 (#96)

- 댓글 조회 실패 시 에러 토스트 및 안내 메시지 표시.
- currentUserId null 체크로 삭제 권한 확인 로직 개선.
- 스켈레톤 count 음수값 방어 처리.
- Textarea format별 height 기본값 적용.

* fix: 약속회고 댓글/상세 조회 mock 처리 개선 (#96)

* refactor: 약속회고 댓글 삭제 확인 모달 개선 및 코드 정리 (#96)

- window.confirm을 글로벌 모달로 변경
- Mock 함수에서 불필요한 meetingId 파라미터 제거
- Textarea 컴포넌트에서 사용하지 않는 ref 제거.

---------

Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>

* [feat] 페이지 접근 권한 전역 관리 구현 (#105)

* feat: 페이지 접근 권한 전역 관리 구현 (#99)

인터셉터에서 권한 에러 감지 시 커스텀 DOM 이벤트를 dispatch하고,
RootLayout의 usePermissionRedirect 훅에서 토스트와 홈 리다이렉트를
처리하여 한 곳에서 통합 관리.

- errors.ts: PAGE_ACCESS_ERROR_CODES Set 추가 (6개 에러 코드)
- interceptors.ts: permission-denied 커스텀 이벤트 dispatch
- usePermissionRedirect: 이벤트 → 토스트 + navigate(HOME)
- RootLayout: usePermissionRedirect 훅 연결
- MeetingRetrospectiveCreatePage: 이중 내비게이션 방지

* fix: 권한 에러 중복 이벤트 억제 및 핸들러 방어 코드 추가 (#99)

* fix: 권한 에러 시 중복 토스트/모달 방지 (#99)

* fix: 약속상세 페이지의 사전의견 버튼에 링크 연동(#106) (#107)

---------

Co-authored-by: choiyoungae <109134495+choiyoungae@users.noreply.github.com>
Co-authored-by: haeun <110523397+haruyam15@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feat 새로운 기능 추가

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[feat] 페이지 접근 권한 관리 구현

2 participants