[FE] 어드민 회원관리 페이지 수정, 게시판 익명 기능 추가, 회원가입 버튼 중복 클릭 수정, 로그 표시 수정#301
Hidden character warning
[FE] 어드민 회원관리 페이지 수정, 게시판 익명 기능 추가, 회원가입 버튼 중복 클릭 수정, 로그 표시 수정#301discipline24 merged 2 commits intomainfrom
Conversation
Walkthrough관리자 멤버·대시보드 UI 개선, 신분(grade) 기반 회원 관리 도입, 게시물/댓글 익명 옵션 추가 및 관련 API/페이지/모달·스타일 변경을 포함한 프론트엔드 기능 및 인터페이스 수정 집합입니다. Changes
Sequence DiagramsequenceDiagram
participant User
participant Board as Board.jsx
participant Modal as Modal.jsx
participant API as boardApi.js
participant PostDetail as PostDetail.jsx
participant CommentSection as CommentSection.jsx
User->>Board: 게시물 작성 모달 열기
Board->>Modal: 전달(isAnonymous, setIsAnonymous)
User->>Modal: 익명 체크 후 저장
Modal->>Board: setIsAnonymous(true)
Board->>API: createPost(postData + anonymous)
API-->>Board: 생성 응답
User->>PostDetail: 상세 보기 진입
PostDetail->>CommentSection: 댓글 UI 렌더
User->>CommentSection: 댓글 작성(익명 체크)
CommentSection->>API: createComment({ text, anonymous })
API-->>CommentSection: 댓글 생성 응답
Estimated Code Review Effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly Related PRs
Suggested Labels
Suggested Reviewers
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Pull request overview
어드민 회원관리 UI를 개선하고(grade/액션 메뉴 등), 게시판에 익명 작성 기능을 추가하며, 회원가입(인증 확인/회원가입) 버튼 중복 클릭을 방지하는 PR입니다.
Changes:
- 게시글/댓글 익명 작성 옵션 추가 및 관련 UI 반영
- 어드민 회원관리에서 회원 신분(grade) 변경 기능 및 테이블/액션 메뉴 UI 개선
- 회원가입 플로우에서 인증 확인/회원가입 버튼 중복 요청 방지 및 문구 개선
Reviewed changes
Copilot reviewed 17 out of 17 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| frontend/src/utils/boardApi.js | 게시글/댓글 생성 API에 anonymous 전송 추가 |
| frontend/src/utils/adminUserApi.js | 관리자 사용자 grade 변경 API로 교체 |
| frontend/src/utils/adminMembersData.js | 관리자 회원 grade 변경 API로 교체 |
| frontend/src/utils/adminHomeData.js | 최근 활동 메시지 문구 수정(“님이”) |
| frontend/src/pages/PostDetail.module.css | 댓글/대댓글 작성 영역 레이아웃 및 익명 옵션 스타일 추가 |
| frontend/src/pages/PostDetail.jsx | 현재 사용자 조회 및 게시글 관리 가능 여부 계산, 댓글/대댓글 익명 전송 |
| frontend/src/pages/Board.jsx | 게시글 작성 시 익명 옵션 상태 추가 및 Modal로 전달 |
| frontend/src/components/signup/SignUpForm.jsx | 인증 확인/회원가입 중복 클릭 방지 및 로딩 문구 처리 |
| frontend/src/components/mypage/ProfileCard.jsx | 역할 라벨 일부 수정 및 기본 표시값 변경 |
| frontend/src/components/Board/PostDetail/PostView.jsx | 게시글 수정/삭제 메뉴를 canManagePost일 때만 노출 |
| frontend/src/components/Board/PostDetail/CommentSection.jsx | 댓글/대댓글 작성 시 익명 체크박스 추가 |
| frontend/src/components/Board/Modal.module.css | 모달 z-index 조정 및 익명 옵션/액션 row 스타일 추가 |
| frontend/src/components/Board/Modal.jsx | 게시글 작성 모달에 익명 체크박스 UI 추가 |
| frontend/src/components/AdminMemberManage/AdminMemberManage.module.css | 테이블 스크롤/액션 메뉴(케밥) UI 스타일 추가 |
| frontend/src/components/AdminMemberManage/AdminMemberManage.jsx | grade 변경 기능 추가, 케밥 액션 메뉴 도입, role 라벨 정비 |
| frontend/src/components/AdminHome/MemberList.jsx | 관리자 역할 라벨 및 뱃지 색상 로직 일부 수정 |
| frontend/src/components/AdminDashboard/AdminDashboard.jsx | 대시보드 역할 라벨 normalize 및 활동 로그 문구(“님이”) 수정 |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
frontend/src/components/AdminMemberManage/AdminMemberManage.jsx (1)
184-203:⚠️ Potential issue | 🟠 Major조기 반환 시 로딩 상태 미해제 가능성
Lines 186-189, 192-195, 198-201에서 유효성 검사 실패 시
return하지만,isChangingById가 이미 true로 설정된 상태입니다. 이 경우 로딩 상태가 해제되지 않아 해당 회원의 버튼이 영구적으로 비활성화될 수 있습니다.🐛 수정 제안
try { if (type === 'role') { if (!ROLE_OPTIONS.includes(value)) { window.alert('유효하지 않은 권한입니다.'); + setIsChangingById((prev) => ({ ...prev, [member.id]: false })); return; } await changeAdminMemberRole({ userId: member.id, role: value }); } else if (type === 'status') { if (!STATUS_OPTIONS.includes(value)) { window.alert('유효하지 않은 상태입니다.'); + setIsChangingById((prev) => ({ ...prev, [member.id]: false })); return; } await changeAdminMemberStatus({ userId: member.id, status: value }); } else { if (!GRADE_OPTIONS.includes(value)) { window.alert('유효하지 않은 신분입니다.'); + setIsChangingById((prev) => ({ ...prev, [member.id]: false })); return; } await changeAdminMemberGrade({ userId: member.id, grade: value }); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/src/components/AdminMemberManage/AdminMemberManage.jsx` around lines 184 - 203, Validation returns inside the try block can exit early while isChangingById remains true; move the validation checks (ROLE_OPTIONS/STATUS_OPTIONS/GRADE_OPTIONS) to before setting isChangingById, or ensure isChangingById is cleared on all exits by wrapping the operation in a try/finally and setting isChangingById[member.id] = false in the finally; apply this to the code paths around isChangingById, the validation branches, and the calls to changeAdminMemberRole/changeAdminMemberStatus/changeAdminMemberGrade so the loading state is always unset on validation failure or after the async call.
🧹 Nitpick comments (2)
frontend/src/components/Board/Modal.module.css (1)
11-11: z-index 계층 구조 확인 필요
z-index: 1200으로 변경하여 Header(1001)보다 위에 표시되는 것은 적절합니다. 다만,CreateSubBoardModal.module.css의 overlay는 여전히z-index: 1000입니다. 두 모달이 동시에 열리는 시나리오가 있다면 계층 구조가 의도대로인지 확인해 주세요.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/src/components/Board/Modal.module.css` at line 11, The overlay z-index in CreateSubBoardModal.module.css (currently 1000) is lower than the Modal.module.css z-index (1200) and Header (1001), which can cause incorrect stacking when both modals open; update the overlay rule in CreateSubBoardModal.module.css (the .overlay or equivalent overlay class) to match or exceed 1200 (or refactor both modal overlays to use a shared CSS variable/constant so Modal.module.css and CreateSubBoardModal.module.css use the same stacking value) to ensure the intended stacking order relative to Header (z-index: 1001).frontend/src/components/AdminHome/MemberList.jsx (1)
1-8:ROLE_LABELS상수의 중복 정의 - 중앙화 권장
ROLE_LABELS가MemberList.jsx,AdminMemberManage.jsx,AdminDashboard.jsx세 파일에 독립적으로 정의되어 있습니다. 향후 역할 레이블 변경 시 동기화 누락 위험이 있습니다.공유 상수 파일(예:
src/constants/roleLabels.js)로 추출하는 것을 권장합니다.♻️ 제안된 중앙화 예시
// src/constants/roleLabels.js export const ROLE_LABELS = { SYSTEM_ADMIN: '관리자', PRESIDENT: '회장', VICE_PRESIDENT: '부회장', TEAM_LEADER: '팀장', TEAM_MEMBER: '일반', PENDING_MEMBER: '대기회원', };그 후 각 컴포넌트에서 import하여 사용:
import { ROLE_LABELS } from '../../constants/roleLabels';🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/src/components/AdminHome/MemberList.jsx` around lines 1 - 8, ROLE_LABELS is duplicated across MemberList.jsx, AdminMemberManage.jsx, and AdminDashboard.jsx; extract it into a single shared constant (e.g., create src/constants/roleLabels.js exporting ROLE_LABELS) and replace the local definitions in those components with an import (import { ROLE_LABELS } from '...'). Update any references to ROLE_LABELS in MemberList.jsx, AdminMemberManage.jsx, and AdminDashboard.jsx to use the imported constant and remove the duplicated local definitions so changes to labels are centralized.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@frontend/src/components/signup/SignUpForm.jsx`:
- Around line 309-311: The shared abortRef.current is being reused by multiple
handlers so one request can cancel another; update the handlers that call
abortRef.current.abort() (the signup submission, verification check, and resend
verification handlers) to use separate AbortController instances per request
instead of a single shared abortRef, e.g., create a new AbortController inside
each handler (or maintain distinct refs like
abortRefVerify/abortRefResend/abortRefSignup) and pass that controller.signal to
the fetch/axios call, and only call abort() on that handler's controller so
other in-flight actions are not cancelled.
In `@frontend/src/pages/PostDetail.jsx`:
- Around line 88-104: The ownership check currently falls back to comparing
names which can misgrant access; update the logic in PostDetail.jsx to determine
isPostOwner using only ID comparison: derive postAuthorId (post?.user?.id ||
post?.userId || post?.createdBy?.id) and currentUserId (currentUser?.id ||
currentUser?.userId) and compute isPostOwner = Boolean(post && currentUser &&
postAuthorId && currentUserId && postAuthorId === currentUserId). Remove the
name-based fallbacks (postAuthorName/currentUserName and the postAuthorName ===
currentUserName branch) or gate them behind an explicit, documented fallback
flag if backend sometimes omits IDs. Ensure any UI/logic that relied on
name-based ownership is updated accordingly.
---
Outside diff comments:
In `@frontend/src/components/AdminMemberManage/AdminMemberManage.jsx`:
- Around line 184-203: Validation returns inside the try block can exit early
while isChangingById remains true; move the validation checks
(ROLE_OPTIONS/STATUS_OPTIONS/GRADE_OPTIONS) to before setting isChangingById, or
ensure isChangingById is cleared on all exits by wrapping the operation in a
try/finally and setting isChangingById[member.id] = false in the finally; apply
this to the code paths around isChangingById, the validation branches, and the
calls to changeAdminMemberRole/changeAdminMemberStatus/changeAdminMemberGrade so
the loading state is always unset on validation failure or after the async call.
---
Nitpick comments:
In `@frontend/src/components/AdminHome/MemberList.jsx`:
- Around line 1-8: ROLE_LABELS is duplicated across MemberList.jsx,
AdminMemberManage.jsx, and AdminDashboard.jsx; extract it into a single shared
constant (e.g., create src/constants/roleLabels.js exporting ROLE_LABELS) and
replace the local definitions in those components with an import (import {
ROLE_LABELS } from '...'). Update any references to ROLE_LABELS in
MemberList.jsx, AdminMemberManage.jsx, and AdminDashboard.jsx to use the
imported constant and remove the duplicated local definitions so changes to
labels are centralized.
In `@frontend/src/components/Board/Modal.module.css`:
- Line 11: The overlay z-index in CreateSubBoardModal.module.css (currently
1000) is lower than the Modal.module.css z-index (1200) and Header (1001), which
can cause incorrect stacking when both modals open; update the overlay rule in
CreateSubBoardModal.module.css (the .overlay or equivalent overlay class) to
match or exceed 1200 (or refactor both modal overlays to use a shared CSS
variable/constant so Modal.module.css and CreateSubBoardModal.module.css use the
same stacking value) to ensure the intended stacking order relative to Header
(z-index: 1001).
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 44b9eda2-82d3-4341-9727-e60ae8680fdf
📒 Files selected for processing (17)
frontend/src/components/AdminDashboard/AdminDashboard.jsxfrontend/src/components/AdminHome/MemberList.jsxfrontend/src/components/AdminMemberManage/AdminMemberManage.jsxfrontend/src/components/AdminMemberManage/AdminMemberManage.module.cssfrontend/src/components/Board/Modal.jsxfrontend/src/components/Board/Modal.module.cssfrontend/src/components/Board/PostDetail/CommentSection.jsxfrontend/src/components/Board/PostDetail/PostView.jsxfrontend/src/components/mypage/ProfileCard.jsxfrontend/src/components/signup/SignUpForm.jsxfrontend/src/pages/Board.jsxfrontend/src/pages/PostDetail.jsxfrontend/src/pages/PostDetail.module.cssfrontend/src/utils/adminHomeData.jsfrontend/src/utils/adminMembersData.jsfrontend/src/utils/adminUserApi.jsfrontend/src/utils/boardApi.js
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
frontend/src/components/signup/SignUpForm.jsx (1)
122-140:⚠️ Potential issue | 🟡 Minorabort 발생 시 오류 토스트가 표시되고 로딩 상태가 잘못 초기화됩니다.
사용자가 이전 요청이 진행 중일 때 "재전송"을 클릭하면:
- 이전 요청이 abort되어 axios 인터셉터에서 오류로 처리됨
- 이전 요청의 catch 블록이 "오류가 발생했습니다." 토스트를 표시 (혼란스러운 UX)
- 이전 요청의 finally 블록이
isSending = false로 설정하여 새 요청이 진행 중인데도 버튼이 활성화됨 (기능 버그)
handleCheckVerificationNumber처럼 조기 반환 가드를 추가하거나, 활성 요청 추적 방식을 개선해야 합니다.🛠️ 제안 수정안
const handleSendVerificationNumber = async (e) => { e.preventDefault(); + if (isSending) { + return; + } abortRefSend.current?.abort(); abortRefSend.current = new AbortController(); + const currentController = abortRefSend.current; setIsSending(true); try { - await sendVerificationNumber({ email }, abortRefSend.current.signal); + await sendVerificationNumber({ email }, currentController.signal); setVerificationSent(true); toast.success('인증번호가 발송되었습니다.'); } catch (error) { + if (!error.status) { + return; // abort된 요청은 무시 + } console.log(error); toast.error('오류가 발생했습니다.'); } finally { - setIsSending(false); + if (abortRefSend.current === currentController) { + setIsSending(false); + } } };
error.status가 undefined인 경우는 abort된 요청이므로 에러 메시지 없이 무시할 수 있습니다. 조기 반환 가드(if (isSending) return;)를 추가하면 더욱 명확합니다.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/src/components/signup/SignUpForm.jsx` around lines 122 - 140, The send flow in handleSendVerificationNumber incorrectly treats aborted requests as errors and can reset isSending for a newer request; fix by adding an early guard (if (isSending) return;) at the top of handleSendVerificationNumber, create and capture a local AbortController (e.g., const controller = new AbortController()) before aborting the previous one, assign abortRefSend.current = controller, pass controller.signal to sendVerificationNumber, and in the catch block ignore aborts by checking controller.signal.aborted (or error.name === 'CanceledError') so you don't call toast.error for aborted requests; in finally only call setIsSending(false) if abortRefSend.current === controller to avoid turning off the loading state for a newer request.
🧹 Nitpick comments (2)
frontend/src/components/signup/SignUpForm.jsx (2)
142-165:handleSendVerificationNumber와 동일한 abort 처리 문제가 있습니다.early return guard가 있지만, abort된 요청의 finally 블록이 새 요청 진행 중에
isCheckingVerification을false로 설정하는 문제가 동일하게 존재합니다. 위의 제안과 같은 패턴으로 수정을 권장합니다.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/src/components/signup/SignUpForm.jsx` around lines 142 - 165, The abort handling in handleCheckVerificationNumber has the same race as handleSendVerificationNumber: the finally block unconditionally calls setIsCheckingVerification(false) even if a newer request has replaced abortRefCheck.current; fix by capturing the controller reference at start (const controller = new AbortController(); abortRefCheck.current = controller) and in the finally only call setIsCheckingVerification(false) if abortRefCheck.current === controller (and similarly only call setVerificationChecked(true) when the controller matches), so use the unique symbols handleCheckVerificationNumber, abortRefCheck, isCheckingVerification, setIsCheckingVerification, and setVerificationChecked to implement this guard.
167-204: 동일한 abort 처리 패턴 적용 권장회원가입 성공 시 바로 네비게이션되므로 문제 발생 가능성은 낮지만, 일관성을 위해 동일한 abort 처리 패턴을 적용하는 것이 좋습니다.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/src/components/signup/SignUpForm.jsx` around lines 167 - 204, In handleSignUp, after a successful await signUp(...) and before nav('/login'), clear the controller reference (e.g., set abortRefSignUp.current = null or undefined) so the same abort-handling pattern used elsewhere is applied and the pending controller won't be accidentally reused/aborted; keep abortRefSignUp.current?.abort() at the start and ensure signUp, abortRefSignUp, and nav are the referenced symbols you update.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In `@frontend/src/components/signup/SignUpForm.jsx`:
- Around line 122-140: The send flow in handleSendVerificationNumber incorrectly
treats aborted requests as errors and can reset isSending for a newer request;
fix by adding an early guard (if (isSending) return;) at the top of
handleSendVerificationNumber, create and capture a local AbortController (e.g.,
const controller = new AbortController()) before aborting the previous one,
assign abortRefSend.current = controller, pass controller.signal to
sendVerificationNumber, and in the catch block ignore aborts by checking
controller.signal.aborted (or error.name === 'CanceledError') so you don't call
toast.error for aborted requests; in finally only call setIsSending(false) if
abortRefSend.current === controller to avoid turning off the loading state for a
newer request.
---
Nitpick comments:
In `@frontend/src/components/signup/SignUpForm.jsx`:
- Around line 142-165: The abort handling in handleCheckVerificationNumber has
the same race as handleSendVerificationNumber: the finally block unconditionally
calls setIsCheckingVerification(false) even if a newer request has replaced
abortRefCheck.current; fix by capturing the controller reference at start (const
controller = new AbortController(); abortRefCheck.current = controller) and in
the finally only call setIsCheckingVerification(false) if abortRefCheck.current
=== controller (and similarly only call setVerificationChecked(true) when the
controller matches), so use the unique symbols handleCheckVerificationNumber,
abortRefCheck, isCheckingVerification, setIsCheckingVerification, and
setVerificationChecked to implement this guard.
- Around line 167-204: In handleSignUp, after a successful await signUp(...) and
before nav('/login'), clear the controller reference (e.g., set
abortRefSignUp.current = null or undefined) so the same abort-handling pattern
used elsewhere is applied and the pending controller won't be accidentally
reused/aborted; keep abortRefSignUp.current?.abort() at the start and ensure
signUp, abortRefSignUp, and nav are the referenced symbols you update.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: c7313b9e-b282-474d-9c33-8add1fb0b608
📒 Files selected for processing (2)
frontend/src/components/signup/SignUpForm.jsxfrontend/src/pages/PostDetail.jsx
🚧 Files skipped from review as they are similar to previous changes (1)
- frontend/src/pages/PostDetail.jsx
어드민 회원관리 페이지 grade 추가 및 ui 수정
게시판 익명 기능 추가
회원가입 버튼 중복 클릭수정
Summary by CodeRabbit
New Features
Improvements