Skip to content

feat(web): admin 뷰 전환을 플로팅 메뉴로 이관#497

Merged
manNomi merged 1 commit intomainfrom
codex/admin-server-client-role
Mar 27, 2026
Merged

feat(web): admin 뷰 전환을 플로팅 메뉴로 이관#497
manNomi merged 1 commit intomainfrom
codex/admin-server-client-role

Conversation

@manNomi
Copy link
Copy Markdown
Contributor

@manNomi manNomi commented Mar 27, 2026

관련 이슈

  • resolves: 없음

작업 내용

  • 인증 스토어 역할을 serverRole(권한 판정용)과 clientRole(UI 뷰 판정용)로 분리했습니다.
  • admin 사용자는 하단 좌측 플로팅 버튼에서만 다음 기능을 사용하도록 변경했습니다.
    • 인스펙터 켜기/끄기
    • 멘토 UI 보기 / 멘티 UI 보기 전환
  • 마이페이지, 매칭 페이지, 멘토 페이지 상단에 노출되던 기존 멘토/멘티 전환 버튼 UI를 제거했습니다.
  • 화면 렌더링은 clientRole 기준으로, admin 여부/플로팅 버튼 노출은 serverRole 기준으로 동작하도록 분리했습니다.
  • 멘토 알림 카운트 컴포넌트는 실제 권한(serverRole) 기준으로 유지하고, 카드 확장 읽음 처리 분기는 UI 역할(clientRole) 기준으로 맞췄습니다.

특이 사항

  • setAccessToken/rehydrate 시 admin의 clientRole은 기존 선택값을 유지하고, 최초에는 멘토 뷰를 기본값으로 설정했습니다.
  • 로그아웃/토큰 만료 시에는 serverRole/clientRole을 함께 초기화합니다.

리뷰 요구사항 (선택)

  • admin 계정에서 멘티 UI 보기로 전환해도 좌하단 플로팅 버튼(관리 메뉴)이 계속 노출되는지 확인 부탁드립니다.
  • 마이페이지(/my), 매칭(/my/match), 멘토(/mentor)에서 기존 상단 토글이 완전히 제거되었는지 확인 부탁드립니다.

@manNomi manNomi requested review from enunsnv and wibaek as code owners March 27, 2026 13:30
@vercel
Copy link
Copy Markdown

vercel bot commented Mar 27, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
solid-connect-web-admin Ready Ready Preview, Comment Mar 27, 2026 1:54pm
solid-connection-web Ready Ready Preview, Comment Mar 27, 2026 1:54pm

@github-actions github-actions bot added the web label Mar 27, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 27, 2026

Walkthrough

이 변경 사항은 애플리케이션의 역할(role) 관리 체계를 재설계합니다. 기존의 JWT 파싱과 토큰 기반 역할 검증을 제거하고, 새로운 serverRoleclientRole 개념을 도입했습니다. 주요 내용은 다음과 같습니다:

  1. 인증 저장소 개선: useAuthStore에서 userRoleserverRoleclientRole로 분리하고, 관리자가 멘토/멘티 뷰를 전환할 수 있는 setClientRole 메서드를 추가했습니다.

  2. 컴포넌트 정리: 6개의 컴포넌트에서 로컬 토글 상태와 JWT 파싱 로직을 제거했으며, 역할 기반 렌더링을 useAuthStore에서 제공하는 clientRole에 의존하도록 통일했습니다.

  3. 관리자 메뉴 추가: AIInspectorFab 컴포넌트에 관리자 전용 메뉴를 추가하여 뷰 전환을 중앙화했습니다.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested reviewers

  • wibaek
  • enunsnv
🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목이 주요 변경사항을 명확하고 간결하게 설명합니다: admin 뷰 전환 기능을 플로팅 메뉴로 이관한 점을 잘 나타냅니다.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description check ✅ Passed PR 설명이 필수 섹션(관련 이슈, 작업 내용, 특이 사항, 리뷰 요구사항)을 모두 포함하고 있으며, 각 섹션이 명확하고 구체적으로 작성되어 있습니다.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/admin-server-client-role

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.

Copy link
Copy Markdown

@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

🧹 Nitpick comments (3)
apps/web/src/components/mentor/MentorExpandChatCard/hooks/useExpandCardClickHandler.ts (1)

28-40: 중복된 !isCheckedState 조건문

Lines 30-32와 33-39에서 !isCheckedState 조건을 두 번 체크하고 있습니다. 이를 하나로 합칠 수 있습니다.

♻️ 조건문 통합 제안
 const handleExpandClick = () => {
   setIsExpanded(!isExpanded);
-  if (!isCheckedState) {
-    setIsCheckedState(true);
-  }
-  if (!isCheckedState) {
+  if (!isCheckedState && clientRole) {
+    setIsCheckedState(true);
     if (isMentor) {
       patchCheckMentorings({ checkedMentoringIds: [mentoringId] });
     } else {
       patchMenteeCheckMentorings({ checkedMentoringIds: [mentoringId] });
     }
   }
 };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/web/src/components/mentor/MentorExpandChatCard/hooks/useExpandCardClickHandler.ts`
around lines 28 - 40, The handleExpandClick function currently checks
!isCheckedState twice; refactor by toggling expansion with
setIsExpanded(!isExpanded) then, in a single if (!isCheckedState) block call
setIsCheckedState(true) and invoke the appropriate API: if (isMentor) call
patchCheckMentorings({ checkedMentoringIds: [mentoringId] }) else call
patchMenteeCheckMentorings({ checkedMentoringIds: [mentoringId] }); keep the
same referenced symbols (handleExpandClick, setIsExpanded, setIsCheckedState,
isCheckedState, isMentor, patchCheckMentorings, patchMenteeCheckMentorings,
mentoringId).
apps/web/src/lib/zustand/useAuthStore.ts (1)

92-100: 2. setClientRole 가드 로직 - 빈 객체 반환 시 무음 실패

serverRole !== ADMIN일 때 빈 객체 {}를 반환하여 상태 변경 없이 조용히 무시됩니다. 이는 의도된 동작이지만, 디버깅 시 혼란을 줄 수 있습니다.

개발 환경에서 경고 로그를 추가하는 것을 고려해보세요:

💡 디버깅 편의를 위한 경고 로그 추가 제안
 setClientRole: (role) => {
   set((state) => {
     if (state.serverRole !== UserRole.ADMIN) {
+      if (process.env.NODE_ENV === "development") {
+        console.warn("[useAuthStore] setClientRole called but serverRole is not ADMIN");
+      }
       return {};
     }

     return { clientRole: role };
   });
 },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/lib/zustand/useAuthStore.ts` around lines 92 - 100, The
setClientRole updater silently ignores attempts when state.serverRole !==
UserRole.ADMIN by returning an empty object; change it to emit a dev-only
warning so unauthorized attempts are visible: inside setClientRole (the function
using set and checking state.serverRole), detect the non-ADMIN case and call a
logger (e.g., console.warn or your app logger) with a clear message including
the attempted role and current serverRole, then return the unchanged state as
before; ensure the warning runs only in development (process.env.NODE_ENV !==
'production') to avoid noisy logs in production.
apps/web/src/components/layout/GlobalLayout/ui/AIInspectorFab/index.tsx (1)

22-22: 페이지 이동 시 메뉴 상태 초기화 고려

isMenuOpen 상태가 페이지 이동 후에도 유지될 수 있습니다. SPA 특성상 컴포넌트가 언마운트되지 않으면 메뉴가 열린 채로 남을 수 있습니다.

필요시 라우트 변경 감지하여 메뉴를 닫는 로직을 추가할 수 있습니다.

💡 라우트 변경 시 메뉴 닫기 (선택적)
import { usePathname } from "next/navigation";
import { useEffect } from "react";

// 컴포넌트 내부에서
const pathname = usePathname();

useEffect(() => {
  setIsMenuOpen(false);
}, [pathname]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/components/layout/GlobalLayout/ui/AIInspectorFab/index.tsx` at
line 22, isMenuOpen can persist across SPA route changes; add route-change
detection to automatically close the menu by using Next's usePathname and a
useEffect that calls setIsMenuOpen(false) whenever pathname changes (inside the
AIInspectorFab component where isMenuOpen and setIsMenuOpen are defined). Import
usePathname and useEffect, derive const pathname = usePathname(), and add
useEffect(() => setIsMenuOpen(false), [pathname]) to reset the menu on
navigation.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@apps/web/src/components/mentor/MentorExpandChatCard/hooks/useExpandCardClickHandler.ts`:
- Around line 20-21: The hook useExpandCardClickHandler currently computes
isMentor from clientRole which can be null; add a defensive guard so that when
clientRole is null you do not proceed to mentor-only logic or call
patchMenteeCheckMentorings. Specifically, update the handler in
useExpandCardClickHandler to check if clientRole == null (or undefined) and
short-circuit/return (or skip mentor branch) before evaluating isMentor or
invoking patchMenteeCheckMentorings, ensuring mentor-only API calls run only
when clientRole is a non-null UserRole value.

---

Nitpick comments:
In `@apps/web/src/components/layout/GlobalLayout/ui/AIInspectorFab/index.tsx`:
- Line 22: isMenuOpen can persist across SPA route changes; add route-change
detection to automatically close the menu by using Next's usePathname and a
useEffect that calls setIsMenuOpen(false) whenever pathname changes (inside the
AIInspectorFab component where isMenuOpen and setIsMenuOpen are defined). Import
usePathname and useEffect, derive const pathname = usePathname(), and add
useEffect(() => setIsMenuOpen(false), [pathname]) to reset the menu on
navigation.

In
`@apps/web/src/components/mentor/MentorExpandChatCard/hooks/useExpandCardClickHandler.ts`:
- Around line 28-40: The handleExpandClick function currently checks
!isCheckedState twice; refactor by toggling expansion with
setIsExpanded(!isExpanded) then, in a single if (!isCheckedState) block call
setIsCheckedState(true) and invoke the appropriate API: if (isMentor) call
patchCheckMentorings({ checkedMentoringIds: [mentoringId] }) else call
patchMenteeCheckMentorings({ checkedMentoringIds: [mentoringId] }); keep the
same referenced symbols (handleExpandClick, setIsExpanded, setIsCheckedState,
isCheckedState, isMentor, patchCheckMentorings, patchMenteeCheckMentorings,
mentoringId).

In `@apps/web/src/lib/zustand/useAuthStore.ts`:
- Around line 92-100: The setClientRole updater silently ignores attempts when
state.serverRole !== UserRole.ADMIN by returning an empty object; change it to
emit a dev-only warning so unauthorized attempts are visible: inside
setClientRole (the function using set and checking state.serverRole), detect the
non-ADMIN case and call a logger (e.g., console.warn or your app logger) with a
clear message including the attempted role and current serverRole, then return
the unchanged state as before; ensure the warning runs only in development
(process.env.NODE_ENV !== 'production') to avoid noisy logs in production.
🪄 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: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 7c52b6ca-d8eb-457e-9f8f-f319b986b7c0

📥 Commits

Reviewing files that changed from the base of the PR and between 7c6c220 and b6f2dd6.

📒 Files selected for processing (7)
  • apps/web/src/app/mentor/_ui/MentorClient/index.tsx
  • apps/web/src/app/my/_ui/MyProfileContent/index.tsx
  • apps/web/src/app/my/match/_ui/MatchContent/index.tsx
  • apps/web/src/components/layout/GlobalLayout/ui/AIInspectorFab/index.tsx
  • apps/web/src/components/mentor/MentorApplyCountContent/index.tsx
  • apps/web/src/components/mentor/MentorExpandChatCard/hooks/useExpandCardClickHandler.ts
  • apps/web/src/lib/zustand/useAuthStore.ts

Comment on lines +20 to +21
const clientRole = useAuthStore((state) => state.clientRole);
const isMentor = clientRole === UserRole.MENTOR;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

clientRolenull일 때의 엣지 케이스

clientRolenull인 경우 (초기화 중 또는 토큰 만료 시):

  • isMentor = null === UserRole.MENTORfalse
  • 멘토 사용자가 잘못된 API(patchMenteeCheckMentorings)를 호출할 수 있습니다

이 훅을 사용하는 MentorExpandChatCard는 인증된 페이지 내에서 렌더링되지만, 방어적 코딩으로 clientRolenull일 때 API 호출을 건너뛰는 것이 안전합니다.

🛡️ 방어적 가드 추가 제안
 const handleExpandClick = () => {
   setIsExpanded(!isExpanded);
   if (!isCheckedState) {
     setIsCheckedState(true);
   }
-  if (!isCheckedState) {
+  if (!isCheckedState && clientRole) {
     if (isMentor) {
       patchCheckMentorings({ checkedMentoringIds: [mentoringId] });
     } else {
       patchMenteeCheckMentorings({ checkedMentoringIds: [mentoringId] });
     }
   }
 };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/web/src/components/mentor/MentorExpandChatCard/hooks/useExpandCardClickHandler.ts`
around lines 20 - 21, The hook useExpandCardClickHandler currently computes
isMentor from clientRole which can be null; add a defensive guard so that when
clientRole is null you do not proceed to mentor-only logic or call
patchMenteeCheckMentorings. Specifically, update the handler in
useExpandCardClickHandler to check if clientRole == null (or undefined) and
short-circuit/return (or skip mentor branch) before evaluating isMentor or
invoking patchMenteeCheckMentorings, ensuring mentor-only API calls run only
when clientRole is a non-null UserRole value.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b6f2dd6a04

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

return {
accessToken: token,
serverRole,
clientRole: resolveClientRole(serverRole, state.clientRole),
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Default admin clientRole on token role change

setAccessToken always reuses state.clientRole when the new token is for an admin, so switching accounts from a mentee session to an admin session without an explicit clearAccessToken keeps clientRole as MENTEE. Login mutations call setAccessToken directly, so this path is reachable and makes first admin render open in mentee UI instead of the intended default mentor UI.

Useful? React with 👍 / 👎.

@manNomi manNomi merged commit 545dbe6 into main Mar 27, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant