Skip to content

Feat/adminpage#41

Open
dasosann wants to merge 2 commits intomainfrom
feat/adminpage
Open

Feat/adminpage#41
dasosann wants to merge 2 commits intomainfrom
feat/adminpage

Conversation

@dasosann
Copy link
Copy Markdown
Contributor

No description provided.

@qodo-code-review
Copy link
Copy Markdown

Review Summary by Qodo

관리자 페이지 전체 기능 구현 (인증, 사용자 관리, 이벤트, 공지사항)

✨ Enhancement

Grey Divider

Walkthroughs

Description
• 관리자 페이지 전체 기능 구현 (인증, 사용자 관리, 이벤트, 공지사항 등)
• 관리자 인증 관련 훅(useAdminAuth) 및 관리 기능 훅(useAdminManagement) 추가
• 로그인, 회원가입, 웹메일 인증 페이지 컴포넌트 구현
• 사용자 검색, 상세정보 조회, 포인트 조정, 경고 메시지 전송 기능 페이지 구현
• 이벤트 관리(매칭 기회, 포인트 충전 할인) 및 공지사항 예약 기능 페이지 구현
• 충전 요청 관리, 결제 내역 조회, 경고 히스토리 조회 페이지 구현
• 역할별(마스터, 오퍼레이터) 메뉴 및 네비게이션 컴포넌트 구현
• 드롭다운, 모달, 페이지네이션, 날짜/시간 선택 등 재사용 가능한 UI 컴포넌트 추가
• 날짜/시간 포맷팅 유틸리티 함수 추가
• 관리자 페이지 레이아웃 및 인증 처리 구현
Diagram
flowchart LR
  Auth["인증<br/>(로그인/회원가입)"]
  UserMgmt["사용자 관리<br/>(검색/상세정보)"]
  PointMgmt["포인트 관리<br/>(조정/결제내역)"]
  WarnMgmt["경고 관리<br/>(메시지/히스토리)"]
  EventMgmt["이벤트 관리<br/>(예약/히스토리)"]
  NoticeMgmt["공지사항 관리<br/>(예약/히스토리)"]
  PayReq["충전 요청<br/>(목록/승인)"]
  
  Hooks["API 훅<br/>(useAdminAuth<br/>useAdminManagement)"]
  Components["UI 컴포넌트<br/>(드롭다운/모달<br/>페이지네이션)"]
  Utils["유틸리티<br/>(dateFormatter)"]
  
  Hooks --> Auth
  Hooks --> UserMgmt
  Hooks --> PointMgmt
  Hooks --> WarnMgmt
  Hooks --> EventMgmt
  Hooks --> NoticeMgmt
  Hooks --> PayReq
  
  Components --> Auth
  Components --> UserMgmt
  Components --> EventMgmt
  Components --> NoticeMgmt
  
  Utils --> EventMgmt
  Utils --> NoticeMgmt
  Utils --> PayReq
Loading

Grey Divider

File Changes

1. hooks/useAdminManagement.ts ✨ Enhancement +318/-0

관리자 페이지 데이터 관리 훅 모음 추가

• 관리자 기능을 위한 API 호출 함수 및 React Query 훅 모음 (사용자 목록, 검색, 포인트 조정, 공지사항, 이벤트, 경고 등)
• getUserList, searchUsers, adjustPoint 등 18개의 비동기 함수 정의
• useUserList, useAdjustPoint, useRegisterNotice 등 18개의 커스텀 훅 export
• TypeScript 타입 정의로 UserListItem, UserListResponse, AdminActionResponse 포함

hooks/useAdminManagement.ts


2. hooks/useAdminAuth.ts ✨ Enhancement +106/-0

관리자 인증 관련 훅 및 타입 정의

• 관리자 인증 관련 API 호출 및 React Query 훅 구현
• 로그인, 회원가입, 웹메일 인증 기능 포함
• useAdminLogin, useAdminInfo, useAdminRegister, useVerifyWebmail 등 5개 훅 export
• TypeScript 타입 정의로 요청/응답 구조 명확화

hooks/useAdminAuth.ts


3. utils/dateFormatter.ts ✨ Enhancement +31/-0

날짜 시간 포맷팅 유틸리티 함수

• 날짜/시간 포맷팅 유틸리티 함수 추가
• 배열 형식, ISO 문자열 형식 등 다양한 입력 형식 지원
• 유효하지 않은 입력에 대해 "N/A" 반환

utils/dateFormatter.ts


View more (58)
4. app/adminpage/user/[uuid]/SendWarnMessage/_components/ScreenSendWarnMessagePage.tsx ✨ Enhancement +225/-0

사용자 경고 메시지 전송 페이지

• 사용자에게 경고 메시지를 전송하는 페이지 컴포넌트
• 15가지 사전 정의된 경고 사유 드롭다운 및 직접 입력 옵션 제공
• 확인 모달을 통한 2단계 전송 프로세스 구현
• 사용자 정보 표시 및 경고 메시지 미리보기 기능

app/adminpage/user/[uuid]/SendWarnMessage/_components/ScreenSendWarnMessagePage.tsx


5. app/adminpage/user/[uuid]/pointManage/_components/ScreenAdminPointManagePage.tsx ✨ Enhancement +220/-0

사용자 포인트 조정 관리 페이지

• 사용자 포인트 수동 조정 페이지 컴포넌트
• ±/- 버튼으로 포인트 증감, 최대 30000P 제한
• 조정 사유 입력 필수 (5자 이상), 확인 및 성공 모달 포함
• 조정 전후 포인트 비교 표시

app/adminpage/user/[uuid]/pointManage/_components/ScreenAdminPointManagePage.tsx


6. app/adminpage/myPage/notice/reservation/_components/ScreenNoticeReservationPage.tsx ✨ Enhancement +224/-0

공지사항 예약 등록 페이지

• 공지사항 예약 등록 페이지 컴포넌트
• 제목, 내용, 날짜, 시간 선택 기능 포함
• 시작 시간이 현재 시각보다 10분 이후여야 하는 유효성 검사
• 에러 메시지 모달을 통한 사용자 피드백

app/adminpage/myPage/notice/reservation/_components/ScreenNoticeReservationPage.tsx


7. app/adminpage/_components/ManagementComponents.tsx ✨ Enhancement +142/-0

관리자 역할별 메뉴 컴포넌트

• 관리자 역할별 메뉴 컴포넌트 모음 (AdminMyPageMain, MasterManageComponent, OperatorManageComponent,
 AdminTeamManage)
• 마스터 관리자용 7개 메뉴 + 1000원 버튼 토글 기능
• 오퍼레이터용 5개 메뉴 (일부 경로 제한)
• 팀 관리 및 오퍼레이터 승인 요청 카드

app/adminpage/_components/ManagementComponents.tsx


8. app/adminpage/register/_components/ScreenAdminRegisterPage.tsx ✨ Enhancement +229/-0

관리자 회원가입 페이지

• 관리자/오퍼레이터 회원가입 페이지 컴포넌트
• 아이디, 비밀번호, 웹메일, 이름, 대학, 권한 입력 필드
• 비밀번호 일치 검증 및 역할별 에러 처리
• 그리드 레이아웃으로 반응형 디자인 구현

app/adminpage/register/_components/ScreenAdminRegisterPage.tsx


9. app/adminpage/myPage/search/_components/ScreenAdminSearchPage.tsx ✨ Enhancement +179/-0

사용자 검색 및 목록 조회 페이지

• 사용자 검색 및 목록 조회 페이지 컴포넌트
• 50명/10명/5명 정렬 옵션 및 닉네임/이메일 검색 기능
• 페이지네이션 지원, 검색 결과 단일 페이지 처리
• 로딩 상태 및 빈 결과 표시

app/adminpage/myPage/search/_components/ScreenAdminSearchPage.tsx


10. app/adminpage/user/[uuid]/_components/ScreenAdminUserDetailPage.tsx ✨ Enhancement +171/-0

사용자 상세 정보 조회 페이지

• 사용자 상세 정보 조회 페이지 컴포넌트
• 경고 히스토리, 경고 메시지 전송, 블랙리스트 관리 버튼
• 결제 내역, 포인트 사용 내역, 포인트 조정 액션 카드
• 블랙리스트 상태 표시 및 모달 구현

app/adminpage/user/[uuid]/_components/ScreenAdminUserDetailPage.tsx


11. app/adminpage/myPage/event/discount/_components/ScreenEventDiscountPage.tsx ✨ Enhancement +167/-0

포인트 충전 할인 이벤트 예약 페이지

• 포인트 충전 할인 이벤트 예약 페이지 컴포넌트
• 날짜, 시간, 할인율(10~40%) 선택 기능
• 이벤트 지속 시간 자동 계산 및 표시
• 시간 유효성 검사 및 에러 모달

app/adminpage/myPage/event/discount/_components/ScreenEventDiscountPage.tsx


12. app/adminpage/webmail-check/_components/ScreenAdminWebmailPage.tsx ✨ Enhancement +165/-0

웹메일 인증 페이지

• 웹메일 인증 페이지 컴포넌트
• 6자리 숫자 코드 입력 필드 (자동 포커스 이동)
• 3분 타이머 및 재발송 기능, 최대 10회 입력 제한
• localStorage를 통한 타이머 상태 유지

app/adminpage/webmail-check/_components/ScreenAdminWebmailPage.tsx


13. app/adminpage/myPage/event/free-match/_components/ScreenEventFreeMatchPage.tsx ✨ Enhancement +144/-0

매칭 기회 제공 이벤트 예약 페이지

• 매칭 기회 제공 이벤트 예약 페이지 컴포넌트
• 날짜, 시간 선택 기능 및 이벤트 상태 카드
• 현재 시각 이후 설정 검증, 시간 범위 유효성 검사
• 에러 모달을 통한 사용자 피드백

app/adminpage/myPage/event/free-match/_components/ScreenEventFreeMatchPage.tsx


14. app/adminpage/_components/ScreenAdminLoginPage.tsx ✨ Enhancement +141/-0

관리자 로그인 페이지

• 관리자 로그인 페이지 컴포넌트
• 아이디, 비밀번호 입력 및 비밀번호 표시/숨김 토글
• 로그인 성공 시 관리자 정보 조회 후 대시보드로 이동
• 회원가입, ID/비밀번호 찾기, 문의하기 링크 포함

app/adminpage/_components/ScreenAdminLoginPage.tsx


15. app/adminpage/user/[uuid]/PaymentHistory/_components/ScreenAdminPaymentHistoryPage.tsx ✨ Enhancement +121/-0

사용자 결제 내역 조회 페이지

• 사용자 결제 내역 조회 페이지 컴포넌트
• 결제 상태별 색상 구분 (성공: 초록, 취소: 주황, 관리자 취소: 빨강)
• 결제액, 충전 포인트, 주문번호 표시
• 로딩 상태 및 빈 결과 처리

app/adminpage/user/[uuid]/PaymentHistory/_components/ScreenAdminPaymentHistoryPage.tsx


16. app/adminpage/payrequest/_components/ScreenAdminPayRequestPage.tsx ✨ Enhancement +115/-0

충전 요청 목록 관리 페이지

• 충전 요청 목록 조회 및 관리 페이지 컴포넌트
• 닉네임 검색 기능 및 새로고침 버튼
• 요청 사항별 수락/거절 처리
• 로딩 상태 및 빈 결과 표시

app/adminpage/payrequest/_components/ScreenAdminPayRequestPage.tsx


17. app/adminpage/_components/RequestUserComponent.tsx ✨ Enhancement +120/-0

충전 요청 사용자 카드 컴포넌트

• 충전 요청 사용자 카드 컴포넌트
• 닉네임, 입금자명, 요청 시각, 주문번호 표시
• 상품명, 가격 정보 및 수락/거절 버튼
• ISO 문자열을 KST 시간대로 포맷팅하는 formatDateTime 함수 포함

app/adminpage/_components/RequestUserComponent.tsx


18. app/adminpage/_components/AdminHeader.tsx ✨ Enhancement +107/-0

관리자 페이지 헤더 컴포넌트

• 관리자 페이지 헤더 컴포넌트 (AdminHeader, AdminRegisterHeader)
• Main, 가입자관리, 팀 관리 네비게이션 탭
• 대학, 권한, 닉네임 정보 표시
• 역할별 라벨 변환 로직 (ROLE_ADMIN → "관리자")

app/adminpage/_components/AdminHeader.tsx


19. app/adminpage/myPage/event/_components/ScreenAdminEventPage.tsx ✨ Enhancement +82/-0

이벤트 관리 메인 페이지

• 이벤트 관리 메인 페이지 컴포넌트
• 매칭 기회 제공, 포인트 충전 할인 이벤트 카드
• 이벤트 예약 목록, 히스토리 조회 카드
• 각 카드 클릭 시 해당 페이지로 라우팅

app/adminpage/myPage/event/_components/ScreenAdminEventPage.tsx


20. app/adminpage/_components/SearchUserComponent.tsx ✨ Enhancement +35/-0

사용자 검색 결과 카드 컴포넌트

• 사용자 검색 결과 카드 컴포넌트
• 닉네임, 이메일 정보 표시
• "상세정보 보기" 버튼으로 사용자 상세 페이지 이동

app/adminpage/_components/SearchUserComponent.tsx


21. app/adminpage/myPage/event/registercomplete/_components/ScreenEventRegisterCompletePage.tsx ✨ Enhancement +69/-0

이벤트 등록 완료 페이지 컴포넌트 추가

• 이벤트 등록 완료 페이지 컴포넌트 신규 생성
• AdminHeader와 useAdminInfo 훅을 활용한 관리자 정보 표시
• 이벤트 예약목록 및 공지사항 등록으로 이동하는 네비게이션 카드 구현
• Heart 아이콘과 안내 메시지를 포함한 완료 안내 UI 제공

app/adminpage/myPage/event/registercomplete/_components/ScreenEventRegisterCompletePage.tsx


22. app/adminpage/myPage/notice/complete/_components/ScreenNoticeRegisterCompletePage.tsx ✨ Enhancement +68/-0

공지사항 등록 완료 페이지 컴포넌트 추가

• 공지사항 등록 완료 페이지 컴포넌트 신규 생성
• 완료 안내 메시지와 Heart 아이콘으로 사용자 피드백 제공
• 공지사항 예약목록 및 이벤트 예약으로 이동하는 네비게이션 구현
• 관리자 정보를 헤더에 표시하는 구조

app/adminpage/myPage/notice/complete/_components/ScreenNoticeRegisterCompletePage.tsx


23. app/adminpage/myPage/event/list/_components/ScreenEventListPage.tsx ✨ Enhancement +84/-0

이벤트 예약 목록 페이지 구현

• 이벤트 예약 목록 조회 및 취소 기능 구현
• useEventList 훅으로 RESERVATION 상태의 이벤트 데이터 조회
• useDeleteEvent 훅으로 이벤트 취소 기능 구현
• AdminListItem 컴포넌트로 각 이벤트 항목 렌더링

app/adminpage/myPage/event/list/_components/ScreenEventListPage.tsx


24. app/adminpage/myPage/notice/_components/ScreenAdminNoticeMainPage.tsx ✨ Enhancement +65/-0

공지사항 관리 메인 페이지 추가

• 공지사항 관리 메인 페이지 컴포넌트 신규 생성
• 공지사항 예약, 예약목록, 히스토리로 이동하는 네비게이션 카드 구현
• 각 섹션별 설명 텍스트와 함께 직관적인 UI 제공
• 관리자 정보를 헤더에 표시

app/adminpage/myPage/notice/_components/ScreenAdminNoticeMainPage.tsx


25. app/adminpage/_components/Pagination.tsx ✨ Enhancement +93/-0

페이지네이션 컴포넌트 구현

• 페이지네이션 컴포넌트 신규 생성
• 10개 페이지 단위의 그룹 네비게이션 지원
• 이전/다음 페이지 및 그룹 이동 버튼 구현
• 현재 페이지 하이라이트 및 비활성화 상태 관리

app/adminpage/_components/Pagination.tsx


26. app/adminpage/myPage/notice/list/_components/ScreenNoticeListPage.tsx ✨ Enhancement +79/-0

공지사항 예약 목록 페이지 구현

• 공지사항 예약 목록 조회 및 취소 기능 구현
• useNoticeList 훅으로 RESERVATION 상태의 공지사항 데이터 조회
• useDeleteNotice 훅으로 공지사항 취소 기능 구현
• 빈 상태 메시지와 함께 목록 렌더링

app/adminpage/myPage/notice/list/_components/ScreenNoticeListPage.tsx


27. app/adminpage/myPage/event/history/_components/ScreenEventHistoryPage.tsx ✨ Enhancement +69/-0

이벤트 히스토리 페이지 구현

• 이벤트 히스토리 페이지 컴포넌트 신규 생성
• useEventList 훅으로 HISTORY 상태의 이벤트 데이터 조회
• 완료된 이벤트 목록을 AdminListItem으로 표시
• 이벤트 타입에 따른 제목 분기 처리

app/adminpage/myPage/event/history/_components/ScreenEventHistoryPage.tsx


28. app/adminpage/myPage/_components/ScreenAdminMyPage.tsx ✨ Enhancement +79/-0

관리자 마이페이지 메인 컴포넌트 추가

• 관리자 마이페이지 메인 컴포넌트 신규 생성
• 역할별(ROLE_OPERATOR, ROLE_ADMIN) 다른 관리 컴포넌트 렌더링
• ROLE_SEMI_OPERATOR는 미승인 상태 페이지 표시
• ROLE_SEMI_ADMIN은 웹메일 확인 페이지로 리다이렉트

app/adminpage/myPage/_components/ScreenAdminMyPage.tsx


29. app/adminpage/myPage/notice/history/_components/ScreenNoticeHistoryPage.tsx ✨ Enhancement +64/-0

공지사항 히스토리 페이지 구현

• 공지사항 히스토리 페이지 컴포넌트 신규 생성
• useNoticeList 훅으로 HISTORY 상태의 공지사항 데이터 조회
• 완료된 공지사항 목록을 AdminListItem으로 표시
• 빈 상태 메시지 제공

app/adminpage/myPage/notice/history/_components/ScreenNoticeHistoryPage.tsx


30. app/adminpage/_components/AdminListItem.tsx ✨ Enhancement +59/-0

관리자 목록 항목 컴포넌트 추가

• 관리자 목록 항목 컴포넌트 신규 생성
• 제목, 상태, 날짜, 시간 정보 표시
• 선택적 취소 버튼 구현
• 반응형 레이아웃으로 모바일 대응

app/adminpage/_components/AdminListItem.tsx


31. app/adminpage/user/[uuid]/warnhistory/_components/ScreenAdminUserWarningHistoryPage.tsx ✨ Enhancement +58/-0

사용자 경고 히스토리 페이지 구현

• 사용자 경고 히스토리 페이지 컴포넌트 신규 생성
• useWarnHistory 훅으로 경고 내역 데이터 조회
• AdminWarnItem 컴포넌트로 각 경고 항목 렌더링
• 경고 철회 불가 안내 메시지 포함

app/adminpage/user/[uuid]/warnhistory/_components/ScreenAdminUserWarningHistoryPage.tsx


32. app/adminpage/_components/AdminDropdown.tsx ✨ Enhancement +71/-0

관리자 드롭다운 컴포넌트 추가

• 드롭다운 선택 컴포넌트 신규 생성
• 외부 클릭 감지로 자동 닫기 기능 구현
• 커스텀 높이 및 클래스명 지원
• 부드러운 애니메이션 효과 포함

app/adminpage/_components/AdminDropdown.tsx


33. app/adminpage/_components/AdminWarningModal.tsx ✨ Enhancement +37/-0

관리자 경고 모달 컴포넌트 추가

• 경고 모달 컴포넌트 신규 생성
• TriangleAlert 아이콘으로 경고 상태 시각화
• 메시지 전달 및 확인 버튼 기능 구현
• 고정 위치 오버레이로 사용자 주의 집중

app/adminpage/_components/AdminWarningModal.tsx


34. app/adminpage/myPage/event/free-match/_components/EventStatusCard.tsx ✨ Enhancement +34/-0

이벤트 상태 카드 컴포넌트 추가

• 이벤트 상태 카드 컴포넌트 신규 생성
• 잔여 이벤트 횟수를 Heart 아이콘으로 시각화
• 매칭 기회 제공 이벤트 정보 표시
• 반응형 디자인 적용

app/adminpage/myPage/event/free-match/_components/EventStatusCard.tsx


35. app/adminpage/layout.tsx ✨ Enhancement +29/-0

관리자 페이지 레이아웃 및 인증 처리

• 관리자 페이지 레이아웃 컴포넌트 신규 생성
• useAdminInfo 훅으로 인증 상태 확인
• 미인증 사용자를 로그인 페이지로 리다이렉트
• 로딩 상태 스피너 표시

app/adminpage/layout.tsx


36. app/adminpage/_components/AdminTimeRow.tsx ✨ Enhancement +41/-0

관리자 시간 선택 행 컴포넌트 추가

• 시간 선택 행 컴포넌트 신규 생성
• AdminDropdown을 활용한 시간 및 분 선택
• 접미사 텍스트 커스터마이징 지원
• 반응형 레이아웃 구현

app/adminpage/_components/AdminTimeRow.tsx


37. app/adminpage/_components/AdminDateSelector.tsx ✨ Enhancement +29/-0

관리자 날짜 선택 컴포넌트 추가

• 날짜 선택 컴포넌트 신규 생성
• 오늘, 내일, 모레 3가지 옵션 제공
• 선택된 날짜 하이라이트 표시
• 버튼 기반 직관적인 UI

app/adminpage/_components/AdminDateSelector.tsx


38. app/adminpage/_components/AdminWarnItem.tsx ✨ Enhancement +21/-0

관리자 경고 항목 컴포넌트 추가

• 경고 항목 컴포넌트 신규 생성
• 경고 사유 및 시간 정보 표시
• 일정한 높이의 항목 레이아웃
• 구분선으로 항목 구분

app/adminpage/_components/AdminWarnItem.tsx


39. app/adminpage/_components/AdminNotAllowed.tsx ✨ Enhancement +17/-0

미승인 오퍼레이터 페이지 컴포넌트 추가

• 미승인 오퍼레이터 페이지 컴포넌트 신규 생성
• Ban 아이콘으로 접근 제한 상태 표시
• 관리자 승인 대기 안내 메시지 제공

app/adminpage/_components/AdminNotAllowed.tsx


40. app/adminpage/user/[uuid]/warnhistory/page.tsx ✨ Enhancement +5/-0

경고 히스토리 페이지 라우트 추가

• 경고 히스토리 페이지 라우트 파일 신규 생성
• ScreenAdminUserWarningHistoryPage 컴포넌트 임포트 및 렌더링

app/adminpage/user/[uuid]/warnhistory/page.tsx


41. app/adminpage/user/[uuid]/SendWarnMessage/page.tsx ✨ Enhancement +5/-0

경고 메시지 전송 페이지 라우트 추가

• 경고 메시지 전송 페이지 라우트 파일 신규 생성
• ScreenSendWarnMessagePage 컴포넌트 임포트 및 렌더링

app/adminpage/user/[uuid]/SendWarnMessage/page.tsx


42. app/adminpage/myPage/notice/complete/page.tsx ✨ Enhancement +5/-0

공지사항 등록 완료 페이지 라우트 추가

• 공지사항 등록 완료 페이지 라우트 파일 신규 생성
• ScreenNoticeRegisterCompletePage 컴포넌트 임포트 및 렌더링

app/adminpage/myPage/notice/complete/page.tsx


43. app/adminpage/user/[uuid]/PaymentHistory/page.tsx ✨ Enhancement +5/-0

결제 히스토리 페이지 라우트 추가

• 결제 히스토리 페이지 라우트 파일 신규 생성
• ScreenAdminPaymentHistoryPage 컴포넌트 임포트 및 렌더링

app/adminpage/user/[uuid]/PaymentHistory/page.tsx


44. app/adminpage/user/[uuid]/pointManage/page.tsx ✨ Enhancement +5/-0

포인트 관리 페이지 라우트 추가

• 포인트 관리 페이지 라우트 파일 신규 생성
• ScreenAdminPointManagePage 컴포넌트 임포트 및 렌더링

app/adminpage/user/[uuid]/pointManage/page.tsx


45. app/adminpage/myPage/event/free-match/page.tsx ✨ Enhancement +5/-0

매칭 기회 제공 이벤트 페이지 라우트 추가

• 매칭 기회 제공 이벤트 페이지 라우트 파일 신규 생성
• ScreenEventFreeMatchPage 컴포넌트 임포트 및 렌더링

app/adminpage/myPage/event/free-match/page.tsx


46. app/adminpage/myPage/event/registercomplete/page.tsx 📦 Other +5/-0
• 이벤트 등록 완료 페이지 라우트 파일 신규 생성
• ScreenEventRegisterCompletePage 컴포

app/adminpage/myPage/event/registercomplete/page.tsx


47. app/adminpage/myPage/event/discount/page.tsx Additional files +5/-0

...

app/adminpage/myPage/event/discount/page.tsx


48. app/adminpage/myPage/event/history/page.tsx Additional files +5/-0

...

app/adminpage/myPage/event/history/page.tsx


49. app/adminpage/myPage/event/list/page.tsx Additional files +5/-0

...

app/adminpage/myPage/event/list/page.tsx


50. app/adminpage/myPage/event/page.tsx Additional files +5/-0

...

app/adminpage/myPage/event/page.tsx


51. app/adminpage/myPage/notice/history/page.tsx Additional files +5/-0

...

app/adminpage/myPage/notice/history/page.tsx


52. app/adminpage/myPage/notice/list/page.tsx Additional files +5/-0

...

app/adminpage/myPage/notice/list/page.tsx


53. app/adminpage/myPage/notice/page.tsx Additional files +5/-0

...

app/adminpage/myPage/notice/page.tsx


54. app/adminpage/myPage/notice/reservation/page.tsx Additional files +5/-0

...

app/adminpage/myPage/notice/reservation/page.tsx


55. app/adminpage/myPage/page.tsx Additional files +5/-0

...

app/adminpage/myPage/page.tsx


56. app/adminpage/myPage/search/page.tsx Additional files +5/-0

...

app/adminpage/myPage/search/page.tsx


57. app/adminpage/page.tsx Additional files +5/-0

...

app/adminpage/page.tsx


58. app/adminpage/payrequest/page.tsx Additional files +5/-0

...

app/adminpage/payrequest/page.tsx


59. app/adminpage/register/page.tsx Additional files +5/-0

...

app/adminpage/register/page.tsx


60. app/adminpage/user/[uuid]/page.tsx Additional files +5/-0

...

app/adminpage/user/[uuid]/page.tsx


61. app/adminpage/webmail-check/page.tsx Additional files +5/-0

...

app/adminpage/webmail-check/page.tsx


Grey Divider

Qodo Logo


✨ Describe tool usage guide:

Overview:
The describe tool scans the PR code changes, and generates a description for the PR - title, type, summary, walkthrough and labels. The tool can be triggered automatically every time a new PR is opened, or can be invoked manually by commenting on a PR.

When commenting, to edit configurations related to the describe tool (pr_description section), use the following template:

/describe --pr_description.some_config1=... --pr_description.some_config2=...

With a configuration file, use the following template:

[pr_description]
some_config1=...
some_config2=...
Enabling\disabling automation
  • When you first install the app, the default mode for the describe tool is:
pr_commands = ["/describe", ...]

meaning the describe tool will run automatically on every PR.

  • Markers are an alternative way to control the generated description, to give maximal control to the user. If you set:
pr_commands = ["/describe --pr_description.use_description_markers=true", ...]

the tool will replace every marker of the form pr_agent:marker_name in the PR description with the relevant content, where marker_name is one of the following:

  • type: the PR type.
  • summary: the PR summary.
  • walkthrough: the PR walkthrough.
  • diagram: the PR sequence diagram (if enabled).

Note that when markers are enabled, if the original PR description does not contain any markers, the tool will not alter the description at all.

Custom labels

The default labels of the describe tool are quite generic: [Bug fix, Tests, Enhancement, Documentation, Other].

If you specify custom labels in the repo's labels page or via configuration file, you can get tailored labels for your use cases.
Examples for custom labels:

  • Main topic:performance - pr_agent:The main topic of this PR is performance
  • New endpoint - pr_agent:A new endpoint was added in this PR
  • SQL query - pr_agent:A new SQL query was added in this PR
  • Dockerfile changes - pr_agent:The PR contains changes in the Dockerfile
  • ...

The list above is eclectic, and aims to give an idea of different possibilities. Define custom labels that are relevant for your repo and use cases.
Note that Labels are not mutually exclusive, so you can add multiple label categories.
Make sure to provide proper title, and a detailed and well-phrased description for each label, so the tool will know when to suggest it.

Inline File Walkthrough 💎

For enhanced user experience, the describe tool can add file summaries directly to the "Files changed" tab in the PR page.
This will enable you to quickly understand the changes in each file, while reviewing the code changes (diffs).

To enable inline file summary, set pr_description.inline_file_summary in the configuration file, possible values are:

  • 'table': File changes walkthrough table will be displayed on the top of the "Files changed" tab, in addition to the "Conversation" tab.
  • true: A collapsable file comment with changes title and a changes summary for each file in the PR.
  • false (default): File changes walkthrough will be added only to the "Conversation" tab.
Utilizing extra instructions

The describe tool can be configured with extra instructions, to guide the model to a feedback tailored to the needs of your project.

Be specific, clear, and concise in the instructions. With extra instructions, you are the prompter. Notice that the general structure of the description is fixed, and cannot be changed. Extra instructions can change the content or style of each sub-section of the PR description.

Examples for extra instructions:

[pr_description]
extra_instructions="""- The PR title should be in the format: '<PR type>: <title>'
- The title should be short and concise (up to 10 words)
- ...
"""

Use triple quotes to write multi-line instructions. Use bullet points to make the instructions more readable.

More PR-Agent commands

To invoke the PR-Agent, add a comment using one of the following commands:

  • /review: Request a review of your Pull Request.
  • /describe: Update the PR title and description based on the contents of the PR.
  • /improve [--extended]: Suggest code improvements. Extended mode provides a higher quality feedback.
  • /ask <QUESTION>: Ask a question about the PR.
  • /update_changelog: Update the changelog based on the PR's contents.
  • /help_docs <QUESTION>: Given a path to documentation (either for this repository or for a given one), ask a question.
  • /add_docs 💎: Generate docstring for new components introduced in the PR.
  • /generate_labels 💎: Generate labels for the PR based on the PR's contents.
  • /analyze 💎: Automatically analyzes the PR, and presents changes walkthrough for each component.

See the tools guide for more details.
To list the possible configuration parameters, add a /config comment.

See the describe usage page for a comprehensive guide on using this tool.

@qodo-code-review
Copy link
Copy Markdown

qodo-code-review bot commented Mar 12, 2026

Code Review by Qodo

🐞 Bugs (5) 📘 Rule violations (0) 📎 Requirement gaps (0)

Grey Divider


Action required

1. Admin 401 misredirects login 🐞 Bug ⛯ Reliability
Description
The new admin hooks use the shared axios client, whose 401 interceptor refreshes via /api/auth/login
and redirects to /login on failure; this can send admins to the non-admin login page and break admin
auth flows on any 401 from admin endpoints. This PR introduces the admin endpoints onto that client,
so the issue becomes user-facing for the new admin UI.
Code

hooks/useAdminAuth.ts[R44-54]

+const adminLogin = async (
+  payload: AdminLoginRequest,
+): Promise<AdminLoginResponse> => {
+  const { data } = await api.post<AdminLoginResponse>("/admin/login", payload);
+  return data;
+};
+
+const getAdminInfo = async (): Promise<AdminInfoResponse> => {
+  const { data } = await api.get<AdminInfoResponse>("/auth/any-admin/info");
+  return data;
+};
Evidence
Admin auth calls (e.g., /auth/any-admin/info) use the shared api instance, so any 401 returned by
these endpoints will go through the global 401 interceptor; that interceptor attempts refresh at
/api/auth/login and then redirects to /login (not /adminpage).

hooks/useAdminAuth.ts[44-54]
lib/axios.ts[13-37]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
Admin API calls introduced by this PR use the shared `api` axios instance. That instance has a global 401 interceptor that tries to refresh via `/api/auth/login` and redirects to `/login` on failure, which can incorrectly send admin users to the non-admin login and break admin UX.

### Issue Context
Admin pages call endpoints like `/auth/any-admin/info` using `api`. Any 401 from these endpoints triggers the interceptor.

### Fix Focus Areas
- hooks/useAdminAuth.ts[44-54]
- lib/axios.ts[13-37]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Event link uses wrong path 🐞 Bug ✓ Correctness
Description
The notice completion screen navigates to /adminpage/myPage/event/page, but the event index route is
/adminpage/myPage/event, so the click will 404. Users finishing notice reservation cannot reach the
event reservation screen via this CTA.
Code

app/adminpage/myPage/notice/complete/_components/ScreenNoticeRegisterCompletePage.tsx[R53-56]

+          <div
+            onClick={() => router.push("/adminpage/myPage/event/page")} // or event main
+            className="flex flex-1 cursor-pointer flex-col gap-2 rounded-[24px] border border-white/30 bg-white p-6 pt-[26px] shadow-[1px_1px_20px_rgba(196,196,196,0.3)] transition-shadow hover:shadow-lg"
+          >
Evidence
The click handler pushes to a path that doesn't match the actual Next.js route segment structure
(the route is defined by app/adminpage/myPage/event/page.tsx, i.e., /adminpage/myPage/event).

app/adminpage/myPage/notice/complete/_components/ScreenNoticeRegisterCompletePage.tsx[53-56]
app/adminpage/myPage/event/page.tsx[1-5]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
Notice complete CTA navigates to a non-existent route (`/adminpage/myPage/event/page`), causing a 404.

### Issue Context
The event index route is defined by `app/adminpage/myPage/event/page.tsx`, so the correct URL is `/adminpage/myPage/event`.

### Fix Focus Areas
- app/adminpage/myPage/notice/complete/_components/ScreenNoticeRegisterCompletePage.tsx[53-56]
- app/adminpage/myPage/event/page.tsx[1-5]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Discount event not created 🐞 Bug ✓ Correctness
Description
ScreenEventDiscountPage's confirm handler never calls the discount-event registration API and only
routes to the completion page, so no discount event is actually created. This makes the UI report
success without persisting anything.
Code

app/adminpage/myPage/event/discount/_components/ScreenEventDiscountPage.tsx[R50-80]

+  const handleConfirm = () => {
+    const sH = parseInt(startTime);
+    const sM = parseInt(startMinutes);
+    const eH = parseInt(endTime);
+    const eM = parseInt(endMinutes);
+
+    if (isNaN(sH) || isNaN(sM) || isNaN(eH) || isNaN(eM)) {
+      alert("시간을 올바르게 선택해주세요.");
+      return;
+    }
+    if (selectedDiscount === "선택") {
+      alert("할인율을 선택해주세요.");
+      return;
+    }
+
+    const startTotal = sH * 60 + sM;
+    const endTotal = eH * 60 + eM;
+
+    if (startTotal >= endTotal) {
+      setErrorMessage(
+        <>
+          이벤트 시작 시간이 종료 시간보다 <br /> 같거나 늦을 수 없습니다.
+        </>,
+      );
+      setShowModal(true);
+      return;
+    }
+
+    // API Call logic
+    router.push("/adminpage/myPage/event/registercomplete");
+  };
Evidence
The page has a placeholder comment and unconditionally navigates to the completion page; meanwhile
the repository contains a concrete registerDiscountEvent API hook intended for this action.

app/adminpage/myPage/event/discount/_components/ScreenEventDiscountPage.tsx[50-80]
hooks/useAdminManagement.ts[114-125]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
Discount event reservation currently never calls the backend registration endpoint; it only navigates to the completion page.

### Issue Context
`hooks/useAdminManagement.ts` already defines the discount event registration API (`/auth/admin/event/discount`).

### Fix Focus Areas
- app/adminpage/myPage/event/discount/_components/ScreenEventDiscountPage.tsx[50-80]
- hooks/useAdminManagement.ts[114-125]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View more (1)
4. Unlimited webmail verify attempts 🐞 Bug ⛨ Security
Description
ScreenAdminWebmailPage increments failCount on verification failure but never blocks verification
after 10 attempts, and the remaining-attempts UI can go negative. This enables unlimited brute-force
attempts despite the UI claiming a 10-try limit.
Code

app/adminpage/webmail-check/_components/ScreenAdminWebmailPage.tsx[R77-160]

+  const handleVerify = async () => {
+    const codeString = values.join("");
+    if (codeString.length !== 6) {
+      alert("6자리 숫자를 모두 입력해주세요.");
+      return;
+    }
+    if (timeLeft <= 0) {
+      alert("시간이 만료되었습니다. 재발송 버튼을 눌러주세요.");
+      return;
+    }
+
+    try {
+      await verifyMutation.mutateAsync(codeString);
+      alert("인증에 성공했습니다.");
+      router.push("/adminpage");
+    } catch (error) {
+      setFailCount((prev) => prev + 1);
+      alert("인증코드가 일치하지 않습니다.");
+    }
+  };
+
+  const minutes = Math.floor(timeLeft / 60);
+  const seconds = timeLeft % 60;
+
+  return (
+    <div className="min-h-screen bg-[#f4f4f4] font-sans">
+      <AdminRegisterHeader />
+
+      <main className="mt-10 flex flex-col items-center gap-6 p-6 md:px-[max(5vw,20px)]">
+        <div
+          onClick={handleVerify}
+          className="flex w-full cursor-pointer flex-col gap-4 rounded-[24px] border border-white/30 bg-white p-10 shadow-[1px_1px_20px_rgba(196,196,196,0.3)] transition-shadow hover:shadow-lg"
+        >
+          <div className="border-none text-[32px] font-bold text-black">
+            웹메일 인증하기
+          </div>
+          <div className="text-xl leading-relaxed font-medium text-[#858585]">
+            이전 절차에서 입력한 웹메일로 6자리 숫자코드가 전송되었습니다.
+            <br />
+            이메일 코드는 3분 뒤에 만료되며, 최대 10번의 입력이 가능합니다.
+            이후에는 재발송 버튼을 눌러 인증코드를 다시 받아 주십시오.
+          </div>
+        </div>
+
+        <div className="flex w-full flex-col items-center gap-6 rounded-[24px] border border-white/30 bg-white p-10 shadow-[1px_1px_20px_rgba(196,196,196,0.3)]">
+          <div className="flex gap-2 md:gap-4">
+            {values.map((value, index) => (
+              <input
+                key={index}
+                ref={(el) => {
+                  inputRefs.current[index] = el;
+                }}
+                type="text"
+                inputMode="numeric"
+                maxLength={1}
+                value={value}
+                onChange={(e) => handleChange(e, index)}
+                onKeyDown={(e) => handleKeyDown(e, index)}
+                className="h-12 w-12 rounded-xl border-none bg-[#e5e5e5] text-center text-3xl font-bold text-black transition-all outline-none focus:ring-2 focus:ring-[#ff775e] md:h-16 md:w-16"
+              />
+            ))}
+          </div>
+
+          <div className="mt-4 flex w-full items-center justify-end gap-8">
+            <div className="text-2xl font-medium text-[#858585]">
+              {timeLeft > 0 ? (
+                <>
+                  남은 시간: {minutes}:{seconds.toString().padStart(2, "0")}
+                </>
+              ) : (
+                "유효시간이 만료되었습니다."
+              )}
+            </div>
+            <button
+              onClick={handleResend}
+              className="rounded-lg bg-[#ff775e] px-6 py-3 text-xl font-bold text-white shadow-md transition-opacity hover:opacity-90"
+            >
+              재발송
+            </button>
+          </div>
+
+          <div className="w-full text-right text-2xl font-semibold text-[#666]">
+            인증이 가능한 횟수 {10 - failCount}회
+          </div>
Evidence
The handler only checks code length and timeLeft; it does not check failCount before calling the
verify API, while the UI displays 10 - failCount which will become negative after enough failures.

app/adminpage/webmail-check/_components/ScreenAdminWebmailPage.tsx[77-96]
app/adminpage/webmail-check/_components/ScreenAdminWebmailPage.tsx[158-160]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
Webmail verification is intended to allow only 10 attempts, but the code does not enforce this limit and the remaining-attempts display can go negative.

### Issue Context
`failCount` is incremented on verify failure but never checked before calling `verifyMutation`.

### Fix Focus Areas
- app/adminpage/webmail-check/_components/ScreenAdminWebmailPage.tsx[77-96]
- app/adminpage/webmail-check/_components/ScreenAdminWebmailPage.tsx[158-160]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

5. Router replace during render 🐞 Bug ⛯ Reliability
Description
ScreenAdminMyPage calls router.replace for ROLE_SEMI_ADMIN directly during render, which is a
side-effect that can trigger repeated navigation attempts and React/Next warnings. Navigation should
be performed in an effect after render.
Code

app/adminpage/myPage/_components/ScreenAdminMyPage.tsx[R50-53]

+  if (adminData.role === "ROLE_SEMI_ADMIN") {
+    router.replace("/adminpage/webmail-check");
+    return null;
+  }
Evidence
The component executes router.replace(...) inside a render branch, not inside useEffect, making
it a render-time side effect.

app/adminpage/myPage/_components/ScreenAdminMyPage.tsx[50-53]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`router.replace()` is called during render for `ROLE_SEMI_ADMIN`, which is a side effect and can cause repeated navigation calls and warnings.

### Issue Context
Navigation should happen in `useEffect` after the component renders.

### Fix Focus Areas
- app/adminpage/myPage/_components/ScreenAdminMyPage.tsx[50-53]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

Comment on lines +44 to +54
const adminLogin = async (
payload: AdminLoginRequest,
): Promise<AdminLoginResponse> => {
const { data } = await api.post<AdminLoginResponse>("/admin/login", payload);
return data;
};

const getAdminInfo = async (): Promise<AdminInfoResponse> => {
const { data } = await api.get<AdminInfoResponse>("/auth/any-admin/info");
return data;
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

1. Admin 401 misredirects login 🐞 Bug ⛯ Reliability

The new admin hooks use the shared axios client, whose 401 interceptor refreshes via /api/auth/login
and redirects to /login on failure; this can send admins to the non-admin login page and break admin
auth flows on any 401 from admin endpoints. This PR introduces the admin endpoints onto that client,
so the issue becomes user-facing for the new admin UI.
Agent Prompt
### Issue description
Admin API calls introduced by this PR use the shared `api` axios instance. That instance has a global 401 interceptor that tries to refresh via `/api/auth/login` and redirects to `/login` on failure, which can incorrectly send admin users to the non-admin login and break admin UX.

### Issue Context
Admin pages call endpoints like `/auth/any-admin/info` using `api`. Any 401 from these endpoints triggers the interceptor.

### Fix Focus Areas
- hooks/useAdminAuth.ts[44-54]
- lib/axios.ts[13-37]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +53 to +56
<div
onClick={() => router.push("/adminpage/myPage/event/page")} // or event main
className="flex flex-1 cursor-pointer flex-col gap-2 rounded-[24px] border border-white/30 bg-white p-6 pt-[26px] shadow-[1px_1px_20px_rgba(196,196,196,0.3)] transition-shadow hover:shadow-lg"
>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

2. Event link uses wrong path 🐞 Bug ✓ Correctness

The notice completion screen navigates to /adminpage/myPage/event/page, but the event index route is
/adminpage/myPage/event, so the click will 404. Users finishing notice reservation cannot reach the
event reservation screen via this CTA.
Agent Prompt
### Issue description
Notice complete CTA navigates to a non-existent route (`/adminpage/myPage/event/page`), causing a 404.

### Issue Context
The event index route is defined by `app/adminpage/myPage/event/page.tsx`, so the correct URL is `/adminpage/myPage/event`.

### Fix Focus Areas
- app/adminpage/myPage/notice/complete/_components/ScreenNoticeRegisterCompletePage.tsx[53-56]
- app/adminpage/myPage/event/page.tsx[1-5]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +50 to +80
const handleConfirm = () => {
const sH = parseInt(startTime);
const sM = parseInt(startMinutes);
const eH = parseInt(endTime);
const eM = parseInt(endMinutes);

if (isNaN(sH) || isNaN(sM) || isNaN(eH) || isNaN(eM)) {
alert("시간을 올바르게 선택해주세요.");
return;
}
if (selectedDiscount === "선택") {
alert("할인율을 선택해주세요.");
return;
}

const startTotal = sH * 60 + sM;
const endTotal = eH * 60 + eM;

if (startTotal >= endTotal) {
setErrorMessage(
<>
이벤트 시작 시간이 종료 시간보다 <br /> 같거나 늦을 수 없습니다.
</>,
);
setShowModal(true);
return;
}

// API Call logic
router.push("/adminpage/myPage/event/registercomplete");
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

3. Discount event not created 🐞 Bug ✓ Correctness

ScreenEventDiscountPage's confirm handler never calls the discount-event registration API and only
routes to the completion page, so no discount event is actually created. This makes the UI report
success without persisting anything.
Agent Prompt
### Issue description
Discount event reservation currently never calls the backend registration endpoint; it only navigates to the completion page.

### Issue Context
`hooks/useAdminManagement.ts` already defines the discount event registration API (`/auth/admin/event/discount`).

### Fix Focus Areas
- app/adminpage/myPage/event/discount/_components/ScreenEventDiscountPage.tsx[50-80]
- hooks/useAdminManagement.ts[114-125]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +77 to +160
const handleVerify = async () => {
const codeString = values.join("");
if (codeString.length !== 6) {
alert("6자리 숫자를 모두 입력해주세요.");
return;
}
if (timeLeft <= 0) {
alert("시간이 만료되었습니다. 재발송 버튼을 눌러주세요.");
return;
}

try {
await verifyMutation.mutateAsync(codeString);
alert("인증에 성공했습니다.");
router.push("/adminpage");
} catch (error) {
setFailCount((prev) => prev + 1);
alert("인증코드가 일치하지 않습니다.");
}
};

const minutes = Math.floor(timeLeft / 60);
const seconds = timeLeft % 60;

return (
<div className="min-h-screen bg-[#f4f4f4] font-sans">
<AdminRegisterHeader />

<main className="mt-10 flex flex-col items-center gap-6 p-6 md:px-[max(5vw,20px)]">
<div
onClick={handleVerify}
className="flex w-full cursor-pointer flex-col gap-4 rounded-[24px] border border-white/30 bg-white p-10 shadow-[1px_1px_20px_rgba(196,196,196,0.3)] transition-shadow hover:shadow-lg"
>
<div className="border-none text-[32px] font-bold text-black">
웹메일 인증하기
</div>
<div className="text-xl leading-relaxed font-medium text-[#858585]">
이전 절차에서 입력한 웹메일로 6자리 숫자코드가 전송되었습니다.
<br />
이메일 코드는 3분 뒤에 만료되며, 최대 10번의 입력이 가능합니다.
이후에는 재발송 버튼을 눌러 인증코드를 다시 받아 주십시오.
</div>
</div>

<div className="flex w-full flex-col items-center gap-6 rounded-[24px] border border-white/30 bg-white p-10 shadow-[1px_1px_20px_rgba(196,196,196,0.3)]">
<div className="flex gap-2 md:gap-4">
{values.map((value, index) => (
<input
key={index}
ref={(el) => {
inputRefs.current[index] = el;
}}
type="text"
inputMode="numeric"
maxLength={1}
value={value}
onChange={(e) => handleChange(e, index)}
onKeyDown={(e) => handleKeyDown(e, index)}
className="h-12 w-12 rounded-xl border-none bg-[#e5e5e5] text-center text-3xl font-bold text-black transition-all outline-none focus:ring-2 focus:ring-[#ff775e] md:h-16 md:w-16"
/>
))}
</div>

<div className="mt-4 flex w-full items-center justify-end gap-8">
<div className="text-2xl font-medium text-[#858585]">
{timeLeft > 0 ? (
<>
남은 시간: {minutes}:{seconds.toString().padStart(2, "0")}
</>
) : (
"유효시간이 만료되었습니다."
)}
</div>
<button
onClick={handleResend}
className="rounded-lg bg-[#ff775e] px-6 py-3 text-xl font-bold text-white shadow-md transition-opacity hover:opacity-90"
>
재발송
</button>
</div>

<div className="w-full text-right text-2xl font-semibold text-[#666]">
인증이 가능한 횟수 {10 - failCount}회
</div>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

4. Unlimited webmail verify attempts 🐞 Bug ⛨ Security

ScreenAdminWebmailPage increments failCount on verification failure but never blocks verification
after 10 attempts, and the remaining-attempts UI can go negative. This enables unlimited brute-force
attempts despite the UI claiming a 10-try limit.
Agent Prompt
### Issue description
Webmail verification is intended to allow only 10 attempts, but the code does not enforce this limit and the remaining-attempts display can go negative.

### Issue Context
`failCount` is incremented on verify failure but never checked before calling `verifyMutation`.

### Fix Focus Areas
- app/adminpage/webmail-check/_components/ScreenAdminWebmailPage.tsx[77-96]
- app/adminpage/webmail-check/_components/ScreenAdminWebmailPage.tsx[158-160]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a complete administrative interface, enabling robust management of users, events, and notices. It provides a secure authentication system with webmail verification and role-based access, ensuring that different levels of administrators can efficiently perform their respective tasks through a newly designed set of UI components and integrated API hooks.

Highlights

  • New Admin Panel Components: A comprehensive set of new React components has been introduced to support various administrative functionalities, including date and time selectors, dropdowns, list items, warning modals, and user-specific components.
  • Admin Page Structure and Navigation: The pull request establishes a new /adminpage route with a dedicated layout for authentication and role-based access. It includes main dashboard pages for administrators and operators, along with sub-pages for event management, notice management, user search, payment history, warning messages, and point adjustments.
  • Admin Authentication and Authorization: A new authentication flow is implemented for admin users, featuring login, registration, and webmail verification. Role-based access control is integrated to differentiate functionalities between 'ROLE_ADMIN' and 'ROLE_OPERATOR', including a 'not allowed' screen for unapproved operators.
  • Backend Integration with React Query Hooks: New React Query hooks (useAdminAuth.ts and useAdminManagement.ts) are added to facilitate seamless interaction with backend APIs for tasks such as user listing, searching, point adjustment, event/notice registration and deletion, sending warning messages, and managing payment charge requests.
Changelog
  • app/adminpage/_components/AdminDateSelector.tsx
    • Added a new component for selecting dates (today, tomorrow, day after tomorrow) with dynamic styling based on selection.
  • app/adminpage/_components/AdminDropdown.tsx
    • Added a reusable dropdown component that displays a list of options and handles selection, with optional height and custom class names.
  • app/adminpage/_components/AdminHeader.tsx
    • Added a header component for admin pages, featuring navigation links to main, member management, and team management, along with displaying user university, role, and nickname.
  • app/adminpage/_components/AdminListItem.tsx
    • Added a generic list item component for displaying details like title, subtitle, status, date, and time, with an optional cancel button.
  • app/adminpage/_components/AdminNotAllowed.tsx
    • Added a component to inform users with 'ROLE_SEMI_OPERATOR' that they are not approved and need administrator approval.
  • app/adminpage/_components/AdminTimeRow.tsx
    • Added a component that combines two AdminDropdown instances for selecting hours and minutes, with a customizable suffix.
  • app/adminpage/_components/AdminWarnItem.tsx
    • Added a component to display a single warning reason and its timestamp for a user.
  • app/adminpage/_components/AdminWarningModal.tsx
    • Added a reusable modal component for displaying warning messages with an alert icon and a confirmation button.
  • app/adminpage/_components/ManagementComponents.tsx
    • Added components for AdminMyPageMain to display admin information, MasterManageComponent for master admin functionalities (including a 1000-won button toggle), OperatorManageComponent for operator functionalities, and AdminTeamManage for team-related management.
  • app/adminpage/_components/Pagination.tsx
    • Added a pagination component to navigate through pages of data, supporting previous/next page and group navigation.
  • app/adminpage/_components/RequestUserComponent.tsx
    • Added a component to display individual user charge requests, including user details, product information, and buttons to approve or reject the request.
  • app/adminpage/_components/ScreenAdminLoginPage.tsx
    • Added the login screen component for administrators, featuring input fields for ID and password, a password visibility toggle, and login functionality.
  • app/adminpage/_components/SearchUserComponent.tsx
    • Added a component to display a user's nickname and email from search results, with a button to view detailed user information.
  • app/adminpage/layout.tsx
    • Added the root layout for the admin section, implementing client-side authentication checks and redirection for unauthorized access.
  • app/adminpage/myPage/_components/ScreenAdminMyPage.tsx
    • Added the main admin 'My Page' screen, which dynamically renders different management components based on the admin's role and selected tab.
  • app/adminpage/myPage/event/_components/ScreenAdminEventPage.tsx
    • Added the main event management page, providing navigation cards for free match events, discount events, event reservation lists, and event history.
  • app/adminpage/myPage/event/discount/_components/ScreenEventDiscountPage.tsx
    • Added a screen for scheduling point charge discount events, allowing selection of date, start/end times, and discount percentage, with validation and a warning modal.
  • app/adminpage/myPage/event/discount/page.tsx
    • Added the page entry point for the discount event scheduling screen.
  • app/adminpage/myPage/event/free-match/_components/EventStatusCard.tsx
    • Added a component to display the current status and remaining count of free match events using heart icons.
  • app/adminpage/myPage/event/free-match/_components/ScreenEventFreeMatchPage.tsx
    • Added a screen for scheduling free match events, allowing selection of date and start/end times, with validation and a warning modal.
  • app/adminpage/myPage/event/free-match/page.tsx
    • Added the page entry point for the free match event scheduling screen.
  • app/adminpage/myPage/event/history/_components/ScreenEventHistoryPage.tsx
    • Added a screen to display a list of past events, categorized by type and showing their duration.
  • app/adminpage/myPage/event/history/page.tsx
    • Added the page entry point for the event history screen.
  • app/adminpage/myPage/event/list/_components/ScreenEventListPage.tsx
    • Added a screen to list and manage (cancel) upcoming reserved events, displaying event details and providing a cancellation option.
  • app/adminpage/myPage/event/list/page.tsx
    • Added the page entry point for the event reservation list screen.
  • app/adminpage/myPage/event/page.tsx
    • Added the page entry point for the main admin event page.
  • app/adminpage/myPage/event/registercomplete/_components/ScreenEventRegisterCompletePage.tsx
    • Added a screen to confirm successful event registration, providing navigation to view reserved events or register notices.
  • app/adminpage/myPage/event/registercomplete/page.tsx
    • Added the page entry point for the event registration complete screen.
  • app/adminpage/myPage/notice/_components/ScreenAdminNoticeMainPage.tsx
    • Added the main notice management page, offering navigation to notice reservation, reserved notice list, and notice history.
  • app/adminpage/myPage/notice/complete/_components/ScreenNoticeRegisterCompletePage.tsx
    • Added a screen to confirm successful notice registration, providing navigation to view reserved notices or register events.
  • app/adminpage/myPage/notice/complete/page.tsx
    • Added the page entry point for the notice registration complete screen.
  • app/adminpage/myPage/notice/history/_components/ScreenNoticeHistoryPage.tsx
    • Added a screen to display a list of past notices, showing their title and duration.
  • app/adminpage/myPage/notice/history/page.tsx
    • Added the page entry point for the notice history screen.
  • app/adminpage/myPage/notice/list/_components/ScreenNoticeListPage.tsx
    • Added a screen to list and manage (cancel) upcoming reserved notices, displaying notice details and providing a cancellation option.
  • app/adminpage/myPage/notice/list/page.tsx
    • Added the page entry point for the notice reservation list screen.
  • app/adminpage/myPage/notice/page.tsx
    • Added the page entry point for the main admin notice page.
  • app/adminpage/myPage/notice/reservation/_components/ScreenNoticeReservationPage.tsx
    • Added a screen for scheduling notices, allowing input for title and content, and selection of date and start/end times, with validation and API integration.
  • app/adminpage/myPage/notice/reservation/page.tsx
    • Added the page entry point for the notice reservation screen.
  • app/adminpage/myPage/page.tsx
    • Added the page entry point for the main admin 'My Page' dashboard.
  • app/adminpage/myPage/search/_components/ScreenAdminSearchPage.tsx
    • Added a screen for searching and listing users, including sorting options, search functionality by nickname or email, and pagination.
  • app/adminpage/myPage/search/page.tsx
    • Added the page entry point for the admin user search screen.
  • app/adminpage/page.tsx
    • Added the page entry point for the admin login screen.
  • app/adminpage/payrequest/_components/ScreenAdminPayRequestPage.tsx
    • Added a screen for managing user payment requests, displaying request details, and providing options to approve or reject charges.
  • app/adminpage/payrequest/page.tsx
    • Added the page entry point for the admin payment request screen.
  • app/adminpage/register/_components/ScreenAdminRegisterPage.tsx
    • Added the registration screen component for admin users, allowing input for account details, school email, nickname, university, and role, with validation and API integration.
  • app/adminpage/register/page.tsx
    • Added the page entry point for the admin registration screen.
  • app/adminpage/user/[uuid]/PaymentHistory/_components/ScreenAdminPaymentHistoryPage.tsx
    • Added a screen to display the payment history for a specific user, including transaction status, order ID, payment amount, and points.
  • app/adminpage/user/[uuid]/PaymentHistory/page.tsx
    • Added the page entry point for a specific user's payment history screen.
  • app/adminpage/user/[uuid]/SendWarnMessage/_components/ScreenSendWarnMessagePage.tsx
    • Added a screen for sending warning messages to a specific user, offering predefined warning reasons or a custom input, with a preview and confirmation step.
  • app/adminpage/user/[uuid]/SendWarnMessage/page.tsx
    • Added the page entry point for sending a warning message to a specific user.
  • app/adminpage/user/[uuid]/_components/ScreenAdminUserDetailPage.tsx
    • Added a screen to display detailed information about a specific user, including nickname, email, and options to view warning/payment history, send warnings, and manage blacklist/points.
  • app/adminpage/user/[uuid]/page.tsx
    • Added the page entry point for a specific user's detail screen.
  • app/adminpage/user/[uuid]/pointManage/_components/ScreenAdminPointManagePage.tsx
    • Added a screen for manually adjusting a user's points, allowing increment/decrement, direct input, and requiring a reason for the adjustment.
  • app/adminpage/user/[uuid]/pointManage/page.tsx
    • Added the page entry point for a specific user's point management screen.
  • app/adminpage/user/[uuid]/warnhistory/_components/ScreenAdminUserWarningHistoryPage.tsx
    • Added a screen to display the warning history for a specific user, showing reasons and timestamps for each warning.
  • app/adminpage/user/[uuid]/warnhistory/page.tsx
    • Added the page entry point for a specific user's warning history screen.
  • app/adminpage/webmail-check/_components/ScreenAdminWebmailPage.tsx
    • Added a screen for webmail verification, allowing users to input a 6-digit code, with a timer, resend option, and attempt counter.
  • app/adminpage/webmail-check/page.tsx
    • Added the page entry point for the admin webmail verification screen.
  • hooks/useAdminAuth.ts
    • Added React Query hooks for admin authentication functionalities, including login, fetching admin information, registration, webmail verification, and resending verification emails.
  • hooks/useAdminManagement.ts
    • Added React Query hooks for various admin management operations, such as fetching user lists, searching users, toggling a specific button, retrieving user details, managing payment history, adjusting user points, registering/listing/deleting notices and events, viewing warning history, sending warning messages, and managing charge requests.
  • public/logo/admin_header_logo.svg
    • Added a new SVG asset for the admin header logo.
  • public/logo/admin_page_logo.svg
    • Added a new SVG asset for the admin page logo.
  • public/logo/empty-heart.svg
    • Added a new SVG asset for an empty heart icon.
  • public/logo/event-register-heart.svg
    • Added a new SVG asset for an event registration heart icon.
  • public/logo/female-icon.svg
    • Added a new SVG asset for a female icon.
  • public/logo/full-heart.svg
    • Added a new SVG asset for a full heart icon.
  • public/logo/male-icon.svg
    • Added a new SVG asset for a male icon.
  • public/logo/minus-button.svg
    • Added a new SVG asset for a minus button icon.
  • public/logo/modal-warn.svg
    • Added a new SVG asset for a modal warning icon.
  • public/logo/not-allowed.svg
    • Added a new SVG asset for a 'not allowed' icon.
  • public/logo/plus-button.svg
    • Added a new SVG asset for a plus button icon.
  • public/logo/refresh-button.svg
    • Added a new SVG asset for a refresh button icon.
  • public/logo/search-logo.svg
    • Added a new SVG asset for a search icon.
  • public/logo/under-triangle.svg
    • Added a new SVG asset for an under triangle icon.
  • utils/dateFormatter.ts
    • Added a utility function to format date and time from various input types (string or array) into a consistent 'YYYY-MM-DD' and 'HH:MM' format.
Activity
  • No human activity has been recorded on this pull request yet.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

전반적으로 어드민 페이지의 많은 기능이 추가되었습니다. 컴포넌트, 훅, 페이지가 잘 분리되어 구조가 좋습니다. 다만, 여러 파일에 걸쳐 반복적으로 나타나는 몇 가지 개선점이 보입니다.

  • 접근성(Accessibility): 클릭 가능한 div 요소가 버튼이나 링크 대신 사용되고 있으며, 폼 입력 필드에 <label>이 누락된 경우가 많습니다. 이는 웹 표준 및 접근성 가이드라인(A11y) 위반입니다.
  • 타입 안정성: any 타입이 API 응답 데이터나 컴포넌트 props에 광범위하게 사용되고 있습니다. 이는 타입스크립트의 이점을 살리지 못하므로, 구체적인 타입을 정의하는 것이 좋습니다.
  • 네비게이션: 페이지 이동 시 router.push를 사용하는 div 대신 Next.js의 <Link> 컴포넌트를 사용하는 것이 시맨틱 HTML, SEO, 그리고 성능(프리페칭) 면에서 더 유리합니다.
  • React Keys: map 함수 사용 시 배열의 인덱스를 key로 사용하는 것은 리스트가 동적으로 변경될 때 문제를 일으킬 수 있습니다. 데이터의 고유 ID를 key로 사용해야 합니다.
  • 날짜/시간 처리: 타임존 변환이 수동으로 처리되어 일관성이 없고 오류에 취약합니다. Intl.DateTimeFormat이나 date-fns와 같은 라이브러리를 사용하여 안정적으로 처리하는 것을 권장합니다.

아래에 각 파일별로 구체적인 피드백을 남겼습니다.

Comment on lines +31 to +50
const formatDateTime = (isoString: string) => {
if (!isoString) return "알 수 없음";
try {
const date = new Date(isoString);
if (isNaN(date.getTime())) return "알 수 없음";

// KST 시간대 적용 (UTC+9)
const kstDate = new Date(date.getTime() + 9 * 60 * 60 * 1000);

const year = kstDate.getUTCFullYear();
const month = String(kstDate.getUTCMonth() + 1).padStart(2, "0");
const day = String(kstDate.getUTCDate()).padStart(2, "0");
const hours = String(kstDate.getUTCHours()).padStart(2, "0");
const minutes = String(kstDate.getUTCMinutes()).padStart(2, "0");

return `${year}-${month}-${day} ${hours}시 ${minutes}분`;
} catch (error) {
return "알 수 없음";
}
};
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.

high

KST 시간대를 적용하기 위해 수동으로 9시간을 더하는 방식은 위험합니다. 서버 시간이나 클라이언트의 시간 설정에 따라 예기치 않은 오류를 발생시킬 수 있으며, 서머타임을 고려하지 못합니다. Intl.DateTimeFormat API를 사용하거나 date-fns-tz와 같은 라이브러리를 도입하여 타임존을 안정적으로 처리하는 것을 강력히 권장합니다. 이렇게 하면 코드가 더 견고해지고 유지보수가 용이해집니다.

Comment on lines +194 to +227
return (
<div className="flex min-h-screen w-screen flex-col bg-[#f4f4f4]">
<AdminRegisterHeader />
<main className="flex flex-1 flex-col items-center gap-4 px-[max(5vw,20px)] py-6 font-sans">
<div
onClick={handleSubmit}
className="flex h-[117px] w-full cursor-pointer flex-col justify-center gap-2 rounded-[24px] border border-white/30 bg-white p-6 text-left shadow-[1px_1px_20px_1px_rgba(196,196,196,0.3)] transition-shadow hover:shadow-lg"
>
<span className="text-[32px] font-bold text-black">가입하기</span>
<span className="text-left text-base font-medium text-[#858585]">
관리자의 승인을 받은 이후 오퍼레이터 권한을 사용할 수 있습니다
</span>
</div>

<div className="grid w-full grid-cols-1 gap-10 rounded-[24px] border border-white/30 bg-white p-6 shadow-[1px_1px_20px_1px_rgba(196,196,196,0.3)] md:grid-cols-2 md:gap-x-14 lg:grid-cols-3 lg:pr-[120px]">
{inputFields.map((field, index) =>
field === null ? (
<div key={index} className="hidden lg:block"></div>
) : (
<InputComponent
key={index}
title={field.title}
placeholder={field.placeholder}
type={field.type}
name={field.name}
options={field.options}
onChange={handleChange}
value={(formData as any)[field.name]}
/>
),
)}
</div>
</main>
</div>
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.

high

웹 표준과 접근성을 준수하기 위해 이 컴포넌트 전체를 <form> 태그로 감싸고, '가입하기' 버튼은 <button type="submit">으로 구현하는 것이 좋습니다. 또한, 각 InputComponent에 대해 <label>을 제공해야 합니다. 현재는 시맨틱한 구조가 부족하여 키보드 네비게이션이나 스크린 리더 사용에 어려움이 있을 수 있습니다.

References
  1. 폼 태그 내의 입력 필드는 label과 연결되어야 하며, 제출 버튼은 type="submit"을 명시하는 등 웹 표준을 준수해야 합니다. (link)

Comment on lines +2 to +31
export const formatDateTime = (input: any) => {
if (!input) return { date: "N/A", time: "N/A" };

if (Array.isArray(input) && input.length >= 5) {
const [year, month, day, hour, minute] = input;
return {
date: `${year}-${String(month).padStart(2, "0")}-${String(day).padStart(2, "0")}`,
time: `${String(hour).padStart(2, "0")}:${String(minute).padStart(2, "0")}`,
};
}

if (typeof input === "string") {
try {
const date = new Date(input);
if (isNaN(date.getTime())) return { date: "N/A", time: "N/A" };
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, "0");
const day = String(date.getDate()).padStart(2, "0");
const hours = String(date.getHours()).padStart(2, "0");
const minutes = String(date.getMinutes()).padStart(2, "0");
return {
date: `${year}-${month}-${day}`,
time: `${hours}:${minutes}`,
};
} catch {
return { date: "N/A", time: "N/A" };
}
}
return { date: "N/A", time: "N/A" };
};
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.

high

날짜와 시간을 포맷하는 이 유틸리티 함수는 타임존 처리에 취약합니다. new Date(input)은 브라우저와 입력 문자열 형식에 따라 다르게 동작할 수 있으며, 타임존 정보가 없는 경우 클라이언트의 로컬 타임존을 기준으로 해석하여 예기치 않은 결과를 낳을 수 있습니다. date-fns-tz와 같은 라이브러리를 사용하거나, Intl.DateTimeFormat API를 일관되게 활용하여 명시적으로 타임존('Asia/Seoul' 등)을 지정하고 포맷팅하는 것을 강력히 권장합니다. 이는 애플리케이션 전체에서 날짜/시간 데이터의 일관성과 정확성을 보장하는 데 중요합니다.

Comment on lines +39 to +48
<div
onClick={() => setIsOpen(!isOpen)}
className="w-full h-full bg-[#f4f4f4] rounded-lg border border-[#e5e5e5] px-3 flex items-center justify-between cursor-pointer text-[18px] font-semibold text-black"
>
<span>{selectedValue}</span>
<ChevronDown
size={18}
className={`transition-transform duration-300 text-gray-400 ${isOpen ? "rotate-180" : ""}`}
/>
</div>
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.

medium

웹 접근성을 위해 클릭 가능한 div 대신 <button> 요소를 사용하는 것이 좋습니다. type="button"을 명시하고, aria-haspopup="listbox"aria-expanded={isOpen} 속성을 추가하면 스크린 리더가 이 요소의 역할과 상태를 정확히 인지할 수 있습니다.

References
  1. 아이콘 전용 버튼의 aria-label, 키보드 네비게이션 가능 여부를 확인해야 합니다. 현재 div는 키보드로 포커스 및 활성화가 어렵습니다. (link)

Comment on lines +57 to +62
<div
onClick={goToMainButton}
className={`px-6 py-[29.5px] text-2xl font-semibold cursor-pointer h-full flex items-center transition-all ${adminSelect === "Main" ? "border-b-4 border-black text-black" : "text-[#808080]"}`}
>
Main
</div>
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.

medium

시맨틱 HTML과 Next.js의 성능 최적화 기능을 활용하기 위해 divonClick 조합 대신 next/link 컴포넌트를 사용하시는 것을 권장합니다. <Link> 컴포넌트는 페이지 사전 로딩(prefetching)을 지원하여 사용자 경험을 향상시키고, 검색 엔진 최적화(SEO)에도 더 유리합니다.

References
  1. 의미에 맞는 태그를 사용해야 합니다. 네비게이션 링크는 div가 아닌 a 태그(Next.js에서는 Link 컴포넌트)가 더 적합합니다. (link)

Comment on lines +75 to +82
<div
key={idx}
onClick={() => router.push(item.path)}
className="bg-white rounded-[24px] border border-white/30 shadow-[1px_1px_20px_1px_rgba(196,196,196,0.3)] flex flex-col justify-center text-left p-6 gap-2 cursor-pointer min-w-[317px]"
>
<div className="text-[32px] font-bold text-black">{item.title}</div>
<div className="text-base font-medium text-[#858585]">{item.sub}</div>
</div>
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.

medium

페이지 이동을 위한 UI 요소는 divonClick을 사용하는 것보다 Next.js의 <Link> 컴포넌트를 사용하는 것이 좋습니다. <Link>는 시맨틱한 <a> 태그로 렌더링되어 웹 접근성을 향상시키고, 코드 분할 및 프리페칭(prefetching)과 같은 Next.js의 내장 최적화 기능을 활용할 수 있어 성능에도 이점이 있습니다.

References
  1. 의미에 맞는 태그를 사용해야 합니다. 네비게이션 링크는 div가 아닌 a 태그(Next.js에서는 Link 컴포넌트)가 더 적합합니다. (link)

Comment on lines +63 to +70
<input
type="text"
name="accountId"
placeholder="ID입력"
className="w-[70%] rounded-[0.8em] border border-gray-300 p-[0.9em] text-[18px] shadow-sm outline-none"
value={formData.accountId}
onChange={handleChange}
/>
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.

medium

웹 접근성 표준(WCAG)에 따라 모든 폼 입력 필드는 <label> 요소와 연결되어야 합니다. placeholder는 레이블을 대체할 수 없습니다. 스크린 리더 사용자를 위해 idhtmlFor 속성을 사용하여 inputlabel을 명시적으로 연결하고, 시각적으로 레이블을 숨기고 싶다면 sr-only (screen-reader-only) CSS 클래스를 사용하는 것이 좋습니다.

References
  1. 폼 내의 입력 필드는 label과 연결되어야 웹 표준을 준수하고 접근성이 향상됩니다. (link)

Comment on lines +142 to +149
userData.map((user, idx) => (
<SearchUserComponent
key={idx}
email={user.email}
nickname={user.username}
uuid={user.uuid}
/>
))
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.

medium

React에서 리스트를 렌더링할 때 배열의 인덱스를 key로 사용하는 것은 리스트의 항목이 추가, 삭제, 또는 재정렬될 때 예기치 않은 동작이나 성능 저하를 유발할 수 있습니다. 각 user 객체에 있는 고유한 uuidkey로 사용해야 합니다.

Suggested change
userData.map((user, idx) => (
<SearchUserComponent
key={idx}
email={user.email}
nickname={user.username}
uuid={user.uuid}
/>
))
userData.map((user) => (
<SearchUserComponent
key={user.uuid}
email={user.email}
nickname={user.username}
uuid={user.uuid}
/>
))

export type AdminActionResponse = {
status: number;
message: string;
data: any;
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.

medium

스타일 가이드(53: Strict Typing)에 따라 any 타입 사용은 지양해야 합니다. API 응답 데이터에 대해 구체적인 타입을 정의하면 타입 안정성이 향상되고, 개발 중 실수를 방지하며, 코드의 가독성과 유지보수성이 좋아집니다. AdminActionResponsedata 필드에 any 대신 실제 데이터 구조에 맞는 타입을 선언해주세요.

References
  1. any 타입 사용을 금지하고, unknown이나 never를 적절히 활용하여 타입을 좁히도록 권장합니다. (link)

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.

1 participant