-
Notifications
You must be signed in to change notification settings - Fork 0
"관리자 회원 상세 조회 페이지" API 연결 ( feat/#348 ) #350
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
""" Walkthrough관리자 회원 상세 조회 기능과 관련된 API, 타입, 훅, UI 컴포넌트, 라우팅 구조가 대폭 확장 및 정비되었습니다. 회원의 프로젝트, 활동 내역, 지원 현황 등 상세 데이터를 조회하고 관리할 수 있는 API와 타입이 추가되었으며, 이를 활용하는 React 훅과 컴포넌트, 그리고 관리자 페이지의 라우트 구조가 새롭게 구성되었습니다. Changes
Sequence Diagram(s)sequenceDiagram
participant Admin as Admin(관리자)
participant UI as AdminUserDetail 컴포넌트
participant Hook as useGetUserProjectData / useGetUserActivity
participant API as user.api.ts / userActivity.api.ts
participant Server as 백엔드 서버
Admin->>UI: 회원 상세 페이지 진입
UI->>Hook: 회원 프로젝트/활동 데이터 요청(userId)
Hook->>API: 회원 상세/프로젝트/활동 API 호출(userId)
API->>Server: HTTP 요청 전송
Server-->>API: 회원 상세/프로젝트/활동 데이터 응답
API-->>Hook: 데이터 반환
Hook-->>UI: 데이터 반환
UI->>Admin: 프로젝트/활동 내역 등 상세 정보 표시
Assessment against linked issues
Assessment against linked issues: Out-of-scope changes해당 변경 내역 중 #348의 "회원 상세 조회 API 연동" 이외의 기능적 코드 변경은 발견되지 않았습니다. Suggested reviewers
Poem
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
npm error Exit handler never called! 📜 Recent review detailsConfiguration used: CodeRabbit UI 📒 Files selected for processing (3)
✅ Files skipped from review due to trivial changes (2)
🚧 Files skipped from review as they are similar to previous changes (1)
⏰ Context from checks skipped due to timeout of 90000ms (1)
✨ Finishing Touches
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 9
🧹 Nitpick comments (10)
src/api/admin/userActivity.api.ts (1)
4-14: 에러 핸들링 개선 제안API 함수의 구조는 좋지만 에러 로깅을 더 구체적으로 만들 수 있습니다.
} catch (e) { - console.error(e); + console.error(`Failed to fetch user activity data for userId: ${userId}`, e); throw e; }이렇게 하면 디버깅 시 어떤 사용자 ID에서 오류가 발생했는지 쉽게 파악할 수 있습니다.
src/components/user/mypage/ContentTab.tsx (1)
25-33: URL 매칭 로직 개선 제안동적 필터링 로직이 잘 작동하지만,
item.url.split('/').pop()을 사용한 URL 매칭이 URL 구조 변경에 취약할 수 있습니다.더 견고한 매칭 방식을 고려해보세요:
const currentFilter = filter.find((item) => - pathname.includes(item.url.split('/').pop() || '') + pathname.endsWith(item.url.split('/').slice(-1)[0]) || + new RegExp(`/${item.url.split('/').pop()}/?$`).test(pathname) );현재 코드도 잘 작동하지만, 더 정확한 매칭을 위해 고려해볼 만합니다.
src/hooks/admin/useGetUserProjectData.ts (2)
10-11: 상태 관리 최적화
useAuthStore.getState().isLoggedIn호출이 컴포넌트 렌더링마다 실행됩니다. 훅 내부에서 상태를 구독하는 것이 더 효율적입니다.- const isLoggedIn = useAuthStore.getState().isLoggedIn; + const isLoggedIn = useAuthStore((state) => state.isLoggedIn);
23-23: 반환값 네이밍 개선 제안
userData라는 이름이 프로젝트 데이터를 반환하는 훅에서는 혼동을 줄 수 있습니다.- return { userData: data, isLoading, isFetching }; + return { projectData: data, isLoading, isFetching };더 명확한 의미 전달을 위해
projectData사용을 권장합니다.src/api/admin/user.api.ts (1)
23-36: API 엔드포인트 일관성 검토
getUserApplicants함수가/admin/project/${projectId}/full엔드포인트를 사용하고 있습니다. 함수명과 엔드포인트 경로가 일치하지 않아 혼동을 줄 수 있습니다.함수명을
getProjectApplicants로 변경하거나 엔드포인트를/admin/users/applicants로 수정하는 것을 고려해보세요.src/hooks/admin/useGetAllUserActivity.ts (2)
17-19: 상태 관리 최적화 제안
useAuthStore.getState().userData?.id를 컴포넌트 렌더링 시마다 호출하고 있습니다. 상태 변경을 구독하지 않으므로 성능상 문제는 없지만, 일관성을 위해useAuthStore훅을 사용하는 것이 좋습니다.- const userLoginId = useAuthStore.getState().userData?.id; + const { userData } = useAuthStore(); + const userLoginId = userData?.id;
42-44: 타입 안전성 개선Union 타입이 복잡하여 타입 안전성이 떨어집니다. 제네릭을 사용하여 더 명확한 타입 정의를 고려해보세요.
- const { data, isLoading, isFetching } = useQuery< - MyComments[] | MyInquiries[] | UserComment[] | UserInquiry[] - >({ + const { data, isLoading, isFetching } = useQuery({그리고 반환 타입을 더 구체적으로 정의할 수 있습니다.
src/models/admin/userDetail/userDetail.applicants.ts (1)
13-13: 타입 안전성 개선문자열 리터럴 타입 대신 enum을 사용하면 더 안전하고 관리하기 쉽습니다.
+ enum ApplicantStatus { + WAITING = 'WAITING', + ACCEPTED = 'ACCEPTED', + REJECTED = 'REJECTED' + } - status: 'WAITING' | 'ACCEPTED' | 'REJECTED'; + status: ApplicantStatus;src/models/admin/userDetail/userProjectData.ts (1)
51-57: 데이터 구조 최적화
UserProjectData인터페이스에서joinedProjects와myJoinedProjects가 유사한 목적을 가지고 있어 보입니다. 네이밍과 용도를 명확히 구분하거나 통합을 고려해보세요.프로젝트 분류 로직을 명확히 정의하고 더 직관적인 네이밍을 제안해드릴까요?
src/routes/AdminRoutes.tsx (1)
9-23: lazy import 구성이 잘 정리되었습니다!모든 lazy import를 파일 상단에 그룹화한 것은 좋은 구조입니다. 코드 분할 전략이 일관되게 적용되었네요.
추가로 lazy 로딩 실패 시 에러 처리를 위해 Error Boundary 컴포넌트 사용을 고려해보세요:
+import { ErrorBoundary } from 'react-error-boundary'; + +const ErrorFallback = ({ error }) => ( + <div>컴포넌트 로딩 중 오류가 발생했습니다: {error.message}</div> +);그리고 라우트에서:
-<Suspense fallback={<Spinner />}> +<ErrorBoundary FallbackComponent={ErrorFallback}> + <Suspense fallback={<Spinner />}>
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (21)
src/api/admin/user.api.ts(1 hunks)src/api/admin/userActivity.api.ts(1 hunks)src/api/mypage.api.ts(1 hunks)src/components/admin/adminUserDetail/AdminUserDetail.tsx(4 hunks)src/components/user/mypage/ContentTab.tsx(2 hunks)src/components/user/mypage/activityLog/commentsActivity/CommentsActivity.tsx(2 hunks)src/components/user/mypage/activityLog/inquiries/Inquiries.tsx(2 hunks)src/components/user/mypage/activityLog/inquiries/inquiry/Inquiry.tsx(1 hunks)src/components/user/mypage/notifications/appliedProjects/AppliedProjects.tsx(3 hunks)src/constants/routes.ts(1 hunks)src/hooks/admin/useGetAllUserActivity.ts(1 hunks)src/hooks/admin/useGetUserProjectData.ts(1 hunks)src/hooks/queries/keys.ts(1 hunks)src/models/admin/userDetail/routing.ts(1 hunks)src/models/admin/userDetail/userActivity.ts(1 hunks)src/models/admin/userDetail/userDetail.applicants.ts(1 hunks)src/models/admin/userDetail/userProjectData.ts(1 hunks)src/models/userProject.ts(1 hunks)src/pages/admin/adminUser/AdminUser.tsx(1 hunks)src/pages/admin/adminUser/AdminUserProjectsLayout.tsx(1 hunks)src/routes/AdminRoutes.tsx(2 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (8)
src/components/user/mypage/activityLog/inquiries/inquiry/Inquiry.tsx (2)
src/models/activityLog.ts (1)
MyInquiries(3-14)src/models/admin/userDetail/userActivity.ts (1)
UserInquiry(3-14)
src/components/user/mypage/activityLog/commentsActivity/CommentsActivity.tsx (3)
src/hooks/admin/useGetAllUserActivity.ts (1)
useGetUserActivity(13-56)src/models/activityLog.ts (1)
MyComments(20-26)src/models/admin/userDetail/userActivity.ts (1)
UserComment(16-22)
src/models/admin/userDetail/userActivity.ts (1)
src/models/apiCommon.ts (1)
ApiCommonType(1-4)
src/components/admin/adminUserDetail/AdminUserDetail.tsx (1)
src/constants/routes.ts (1)
ADMIN_ROUTE(35-58)
src/hooks/admin/useGetUserProjectData.ts (4)
src/models/userProject.ts (1)
AppliedProject(20-24)src/hooks/queries/keys.ts (2)
UserData(68-74)ProjectListKey(22-25)src/api/admin/user.api.ts (1)
getUserProjectData(38-48)src/api/mypage.api.ts (1)
getMyAppliedStatusList(69-80)
src/models/admin/userDetail/userProjectData.ts (3)
src/models/tags.ts (1)
MethodTag(16-20)src/models/userProject.ts (2)
JoinedProject(4-14)AppliedProject(20-24)src/models/apiCommon.ts (1)
ApiCommonType(1-4)
src/models/admin/userDetail/userDetail.applicants.ts (1)
src/models/apiCommon.ts (1)
ApiCommonType(1-4)
src/api/admin/user.api.ts (3)
src/api/http.api.ts (1)
httpClient(80-80)src/models/admin/userDetail/userDetail.applicants.ts (1)
ApiUserApplicantsData(38-40)src/models/admin/userDetail/userProjectData.ts (1)
ApiUserProjectDataResponse(59-61)
⏰ Context from checks skipped due to timeout of 90000ms (1)
- GitHub Check: accessibility-test
🔇 Additional comments (38)
src/api/mypage.api.ts (1)
75-75: API 응답 구조 일관성 개선을 승인합니다.다른 API 함수들과 동일하게
response.data.data를 반환하도록 변경하여 데이터 구조의 일관성을 확보했습니다. 이는putMyInfo와patchMyProfileImg함수들과 동일한 패턴을 따릅니다.src/models/userProject.ts (1)
27-27: 다음 스크립트로 TS/TSX 파일 모두에서data에 대한 null 체크를 재검색해 보세요:#!/bin/bash # tsx 파일 인식 문제 해결을 위해 glob 패턴 사용 rg -A 3 -B 3 "data\s*===\s*null" --glob '*.ts' --glob '*.tsx' rg -A 3 -B 3 "data\s*==\s*null" --glob '*.ts' --glob '*.tsx' rg -A 3 -B 3 "!data" --glob '*.ts' --glob '*.tsx'src/constants/routes.ts (1)
51-51: 라우팅 구조 개선을 승인합니다.
basic라우트를 제거하고projects라우트를 추가하여 관리자 사용자 상세 페이지의 라우팅 구조를 개선했습니다. 이는 프로젝트 관련 탭들을 통합하는 전체 리팩터링과 일치합니다.src/pages/admin/adminUser/AdminUser.tsx (2)
46-46: 라우트 단순화를 승인합니다.사용자 상세 페이지 링크에서
ADMIN_ROUTE.basic참조를 제거하여 라우트 구조를 단순화했습니다. 이는constants/routes.ts의 변경사항과 일치하며 전체 리팩터링 목표에 부합합니다.
40-40: 아래 스크립트로SearchBar컴포넌트 파일을 찾아 props 정의와canWrite사용 여부를 확인해 주세요:#!/bin/bash # 1. SearchBar 컴포넌트 파일 찾기 echo "=== SearchBar 컴포넌트 파일 ===" rg -l "SearchBar" --glob "*.tsx" --glob "*.ts" # 2. 각 파일의 상위 50줄 출력하여 props 인터페이스/타입 확인 echo "=== 파일 상단 50줄 확인 ===" for f in $(rg -l "SearchBar" --glob '*.tsx' --glob '*.ts'); do echo "---- $f ----" sed -n '1,50p' "$f" done # 3. 전체 코드베이스에서 canWrite 키워드 검색 echo "=== canWrite 키워드 검색 ===" rg -n "canWrite" --glob '*.tsx' --glob '*.ts'src/hooks/queries/keys.ts (1)
65-65: 쿼리 키 추가 승인새로운 쿼리 키들이 기존 패턴을 잘 따르고 있으며, 명명 규칙도 일관성 있게 적용되었습니다. 관리자 사용자 상세 조회 기능에 필요한 키들이 적절히 추가되었습니다.
Also applies to: 72-74
src/components/user/mypage/ContentTab.tsx (1)
61-61: Context 전달 개선
Outlet에filterId를 context로 전달하는 것은 좋은 접근입니다. 자식 컴포넌트들이 현재 선택된 필터 ID에 쉽게 접근할 수 있게 됩니다.Also applies to: 68-68
src/components/admin/adminUserDetail/AdminUserDetail.tsx (6)
15-16: 새로운 훅과 타입 임포트가 적절히 추가되었습니다.프로젝트 데이터 조회를 위한
useGetUserProjectData훅과 타입 정의가 올바르게 임포트되었습니다.
21-25: 프로젝트 데이터 훅 통합이 적절합니다.기존 사용자 정보 조회에 추가로 프로젝트 데이터 조회 훅을 통합하여 관리자가 회원의 프로젝트 관련 정보를 종합적으로 볼 수 있도록 구현되었습니다.
27-27: 로딩 상태 처리가 포괄적으로 개선되었습니다.사용자 정보와 프로젝트 데이터 두 가지 데이터 소스의 로딩 상태를 모두 확인하여 데이터가 완전히 준비된 후에 UI를 렌더링하도록 구현되었습니다.
44-44: 기본 정보 탭의 경로가 단순화되었습니다.기존의 복잡한 경로 구조에서 벗어나 더 직관적인 경로로 변경되어 사용자 경험이 개선되었습니다.
54-58: 프로젝트 관련 탭이 효과적으로 통합되었습니다.기존의 3개 분리된 프로젝트 탭(참여, 기획, 지원)을 하나의 통합된 탭으로 재구성하여 UI가 더 깔끔해지고 사용자가 프로젝트 정보에 더 쉽게 접근할 수 있게 되었습니다.
84-84: 컨텍스트 데이터 전달이 적절히 확장되었습니다.하위 컴포넌트에서 사용자 정보와 프로젝트 데이터를 모두 사용할 수 있도록 Outlet 컨텍스트에
projectData가 추가되었습니다.src/pages/admin/adminUser/AdminUserProjectsLayout.tsx (3)
5-21: 프로젝트 탭 구성이 체계적으로 정의되었습니다.지원한 프로젝트, 참여한 프로젝트, 기획한 프로젝트의 3가지 카테고리로 명확하게 분류되어 관리자가 회원의 프로젝트 활동을 종합적으로 파악할 수 있습니다.
26-29: 동적 URL 생성 로직이 적절합니다.URL 파라미터의
:userId플레이스홀더를 실제 사용자 ID로 치환하는 방식이 올바르게 구현되었습니다.userId ?? ''처리로 null/undefined 케이스도 안전하게 처리됩니다.
31-35: ContentTab 컴포넌트 사용이 적절합니다.
$justifyContent='space-evenly'속성을 통해 탭들이 균등하게 배치되도록 구성되어 UI 일관성이 유지됩니다.src/components/user/mypage/notifications/appliedProjects/AppliedProjects.tsx (5)
1-1: URL 파라미터 사용을 위한 임포트가 추가되었습니다.관리자 뷰에서 특정 사용자의 지원 프로젝트를 조회하기 위해
useParams가 적절히 추가되었습니다.
7-7: 새로운 훅으로 적절히 마이그레이션되었습니다.기존의 현재 사용자 전용 훅에서 userId 파라미터를 받는 범용 훅으로 변경되어 관리자가 임의 사용자의 프로젝트를 조회할 수 있게 되었습니다.
10-14: 사용자 ID 기반 데이터 조회가 올바르게 구현되었습니다.URL 파라미터에서 추출한
userId를 활용하여 해당 사용자의 프로젝트 데이터를 조회하는 로직이 적절히 구현되었습니다.
24-30: 빈 데이터 처리 로직이 안전하게 업데이트되었습니다.새로운 데이터 구조에 맞춰 빈 상태 체크 로직이 적절히 수정되어 데이터가 없는 경우를 올바르게 처리합니다.
35-35: 데이터 렌더링이 새로운 데이터 소스로 적절히 변경되었습니다.
userProjectData를 사용하여 프로젝트 목록을 렌더링하도록 수정되어 기능이 올바르게 동작할 것입니다.src/components/user/mypage/activityLog/commentsActivity/CommentsActivity.tsx (5)
6-9: 필요한 임포트가 적절히 추가되었습니다.새로운 훅과 타입 정의, URL 파라미터 사용을 위한 임포트가 올바르게 추가되었습니다.
12-17: 사용자 활동 데이터 조회가 올바르게 구현되었습니다.URL 파라미터에서 userId를 추출하여 해당 사용자의 댓글 활동을 조회하는 로직이 적절히 구현되었습니다. 'comments' 타입 지정으로 명확한 데이터 요청이 이루어집니다.
27-37: 향상된 빈 데이터 검증 로직이 구현되었습니다.데이터 존재 여부, 배열 타입 여부, 배열 길이를 모두 확인하는 포괄적인 검증 로직으로 더 안전한 데이터 처리가 가능해졌습니다.
39-39: 타입 캐스팅이 적절히 처리되었습니다.유니온 타입으로 반환되는 데이터를 적절한 타입으로 캐스팅하여 컴포넌트에서 안전하게 사용할 수 있도록 처리되었습니다.
44-47: 데이터 렌더링과 경계 처리가 올바르게 수정되었습니다.새로운 데이터 소스(
commentsData)를 사용하여 댓글 목록을 렌더링하고, 마지막 항목을 제외한 구분선 표시 로직도 적절히 업데이트되었습니다.src/models/admin/userDetail/userActivity.ts (5)
1-1: 공통 타입 임포트가 적절합니다.API 응답의 공통 구조와 사용자 타입을 올바르게 임포트하여 일관성 있는 타입 정의가 가능합니다.
3-14: UserInquiry 인터페이스가 포괄적으로 정의되었습니다.문의사항에 필요한 모든 필드가 적절한 타입으로 정의되었습니다. 선택적 답변(
answer?), 이미지 URL 배열, 타임스탬프, 사용자 정보 등이 모두 포함되어 있어 완전한 데이터 모델을 제공합니다.
16-22: UserComment 인터페이스가 간결하고 명확합니다.댓글 데이터에 필요한 핵심 정보(내용, 생성일, 연관 프로젝트 정보)가 적절히 정의되어 있습니다.
24-27: UserActivityData 인터페이스가 활동 데이터를 잘 집약합니다.문의사항과 댓글을 하나의 구조체로 묶어 사용자 활동 데이터를 종합적으로 관리할 수 있는 구조입니다.
29-31: API 응답 인터페이스가 일관성 있게 확장되었습니다.공통 API 응답 타입을 확장하여 사용자 활동 데이터를 포함하는 구조로 일관성 있는 API 응답 형태를 유지합니다.
src/api/admin/user.api.ts (3)
14-21: 보안 고려사항: 사용자 경고 API의 권한 검증사용자 경고 기능도 관리자 권한이 필요한 작업입니다. 적절한 권한 검증과 로깅이 구현되어 있는지 확인이 필요합니다.
5-12: 보안 고려사항: 사용자 차단 API의 권한 검증사용자 차단 기능은 민감한 관리자 권한이 필요한 작업입니다. API 엔드포인트에서 적절한 권한 검증이 이루어지는지 확인이 필요합니다.
#!/bin/bash # 관리자 권한 검증 로직 확인 rg -A 10 -B 5 "ban.*user|user.*ban" --type ts
38-48: 더 정확한 구조 확인을 위해 해당 인터페이스 정의와appliedProjects필드를 함께 조회하겠습니다.#!/bin/bash # ApiUserProjectDataResponse 정의와 appliedProjects 필드 확인 rg -n -C3 "export interface ApiUserProjectDataResponse" src/models/admin/userDetail/userProjectData.ts rg -n "appliedProjects" src/models/admin/userDetail/userProjectData.tssrc/models/admin/userDetail/userDetail.applicants.ts (1)
32-36: 데이터 구조 검토
UserApplicantsData인터페이스에서results필드가 배열로 정의되어 있는데, 논리적으로는 단일 객체가 더 적절해 보입니다.#!/bin/bash # API 응답 구조 확인 rg -A 10 -B 5 "results.*ApplicantResult" --type tssrc/routes/AdminRoutes.tsx (3)
228-231: 신고 상세 페이지 라우트가 잘 추가되었습니다!동적 파라미터
:id를 사용한 라우트 구성이 적절합니다.
213-218: ```shell
#!/bin/bashuseGetUserProjectList 훅 구현 확인 (상위 200줄)
echo "=== useGetUserProjectList.ts (first 200 lines) ==="
sed -n '1,200p' src/hooks/user/useGetUserProjectList.ts--- `201-219`: **프로젝트 라우트 구조 개선이 잘 되었습니다.** 중첩 라우트 구조로 변경하여 더 체계적으로 정리되었네요. 다만 몇 가지 확인이 필요합니다: 1. **기존 링크 호환성**: 라우트 경로가 변경되어 기존에 이 경로들을 직접 참조하는 링크가 있다면 업데이트가 필요합니다. 2. **UserProjects 컴포넌트 재사용**: joined와 created 프로젝트 모두에 동일한 컴포넌트를 사용하는데, 각 컨텍스트를 구분할 수 있는지 확인이 필요합니다. 다음 스크립트로 기존 라우트 참조를 확인해보세요: ```shell #!/bin/bash # Description: 변경된 라우트 경로에 대한 기존 참조 확인 # 이전 라우트 패턴 검색 echo "=== 기존 라우트 참조 확인 ===" rg -A 2 "joinedProject|createdProject" --glob "!**/AdminRoutes.tsx" # ADMIN_ROUTE 상수 정의 확인 echo -e "\n=== ADMIN_ROUTE 상수 확인 ===" rg -A 10 "export.*ADMIN_ROUTE|const.*ADMIN_ROUTE" src/constants/
| @@ -0,0 +1 @@ | |||
| export type TabKey = 'basic' | 'log' | 'inquiry' | 'projects'; | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
타입 정의와 라우트 상수 간 불일치 문제가 있습니다.
TabKey 타입에 'basic'이 포함되어 있지만, src/constants/routes.ts에서는 ADMIN_ROUTE의 basic 속성이 제거되었습니다. 이로 인해 타입과 실제 라우트 사용 간에 불일치가 발생할 수 있습니다.
다음과 같이 수정을 제안합니다:
-export type TabKey = 'basic' | 'log' | 'inquiry' | 'projects';
+export type TabKey = 'log' | 'inquiry' | 'projects';또는 basic 탭이 여전히 필요하다면 라우트 구조를 다시 확인해주세요.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| export type TabKey = 'basic' | 'log' | 'inquiry' | 'projects'; | |
| -export type TabKey = 'basic' | 'log' | 'inquiry' | 'projects'; | |
| +export type TabKey = 'log' | 'inquiry' | 'projects'; |
🤖 Prompt for AI Agents
In src/models/admin/userDetail/routing.ts at line 1, the TabKey type includes
'basic' but the corresponding 'basic' route has been removed from ADMIN_ROUTE in
src/constants/routes.ts, causing a mismatch. To fix this, either remove 'basic'
from the TabKey type to align with the current routes or if the 'basic' tab is
still required, restore and verify the 'basic' route in ADMIN_ROUTE accordingly.
| import { My_INQUIRIES_MESSAGE } from '../../../../../../constants/user/customerService'; | ||
| import ContentBorder from '../../../../../common/contentBorder/ContentBorder'; | ||
| import { ChevronRightIcon } from '@heroicons/react/24/outline'; | ||
| import { UserInquiry } from '../../../../../../models/admin/userDetail/userActivity'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
타입 중복 개선 필요
MyInquiries와 UserInquiry 타입이 동일한 구조를 가지고 있습니다. 두 타입 모두 같은 필드들(id, title, content, category, state, answer?, imageUrls, createdAt, updatedAt, user)을 정의하고 있어 중복이 발생합니다.
타입 통합을 고려해보세요:
-import type { MyInquiries } from '../../../../../../models/activityLog';
-import { UserInquiry } from '../../../../../../models/admin/userDetail/userActivity';
+import type { InquiryItem } from '../../../../../../models/shared/inquiry'; // 공통 타입으로 통합
interface InquiryProps {
- list: MyInquiries | UserInquiry;
+ list: InquiryItem;
no: number;
}Also applies to: 10-10
🤖 Prompt for AI Agents
In src/components/user/mypage/activityLog/inquiries/inquiry/Inquiry.tsx at lines
7 and 10, the types MyInquiries and UserInquiry have identical structures
causing redundancy. Refactor by consolidating these two types into a single
shared type definition that includes all common fields, then replace both
MyInquiries and UserInquiry usages with this unified type to eliminate
duplication.
| </S.WrapperNoContentAppliedProjects> | ||
| ); | ||
|
|
||
| const myInquiriesData = userActivityData as MyInquiries[] | UserInquiry[]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
위험한 타입 캐스팅
타입 안전성을 보장하지 않는 캐스팅을 사용하고 있습니다. 런타임에서 타입 불일치로 인한 오류가 발생할 수 있습니다.
- const myInquiriesData = userActivityData as MyInquiries[] | UserInquiry[];
+ const myInquiriesData = userActivityData || [];또는 타입 가드 함수를 만들어 안전하게 처리하세요.
🤖 Prompt for AI Agents
In src/components/user/mypage/activityLog/inquiries/Inquiries.tsx at line 33,
the current type casting of userActivityData to MyInquiries[] | UserInquiry[] is
unsafe and may cause runtime errors due to type mismatches. Replace the direct
casting with a type guard function that checks the structure of userActivityData
at runtime and safely narrows its type before assignment. Implement this type
guard to ensure type safety and prevent potential runtime issues.
| const { userId } = useParams(); | ||
| const { userActivityData, isLoading } = useGetUserActivity( | ||
| Number(userId), | ||
| 'inquiries' | ||
| ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
URL 파라미터 검증 누락
userId 파라미터가 유효한지 검증하지 않고 바로 Number(userId)로 변환하고 있습니다. 잘못된 파라미터가 전달될 경우 문제가 발생할 수 있습니다.
export default function Inquiries() {
const { userId } = useParams();
+
+ if (!userId || isNaN(Number(userId))) {
+ return (
+ <S.WrapperNoContentAppliedProjects data-type='noContent'>
+ <NoContent type='error' />
+ </S.WrapperNoContentAppliedProjects>
+ );
+ }
+
const { userActivityData, isLoading } = useGetUserActivity(
Number(userId),
'inquiries'
);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const { userId } = useParams(); | |
| const { userActivityData, isLoading } = useGetUserActivity( | |
| Number(userId), | |
| 'inquiries' | |
| ); | |
| export default function Inquiries() { | |
| const { userId } = useParams(); | |
| if (!userId || isNaN(Number(userId))) { | |
| return ( | |
| <S.WrapperNoContentAppliedProjects data-type='noContent'> | |
| <NoContent type='error' /> | |
| </S.WrapperNoContentAppliedProjects> | |
| ); | |
| } | |
| const { userActivityData, isLoading } = useGetUserActivity( | |
| Number(userId), | |
| 'inquiries' | |
| ); | |
| // ...rest of the component | |
| } |
🤖 Prompt for AI Agents
In src/components/user/mypage/activityLog/inquiries/Inquiries.tsx around lines
12 to 16, the userId URL parameter is converted to a number without validation,
which can cause issues if the parameter is invalid. Add validation to check if
userId exists and is a valid number before converting it. Handle cases where
userId is missing or invalid by showing an error message or redirecting
appropriately to prevent runtime errors.
| export interface AuthoredProject { | ||
| id: number; | ||
| title: string; | ||
| description: string; | ||
| totalMember: number; | ||
| startDate: string; | ||
| estimatedPeriod: string; | ||
| authorId: number; | ||
| views: number; | ||
| isBeginner: boolean; | ||
| isDone: boolean; | ||
| recruitmentStartDate: string; | ||
| recruitmentEndDate: string; | ||
| createAt: string; | ||
| updateAt: string; | ||
| methodType: MethodTag; | ||
| positions: PositionTag[]; | ||
| skills: SkillTag[]; | ||
| canEvaluate: boolean; | ||
| isAllEvaluated: boolean; | ||
| } | ||
|
|
||
| export interface OwnedProject { | ||
| id: number; | ||
| title: string; | ||
| recruitmentEndDate: string; | ||
| totalMember: number; | ||
| isBeginner: boolean; | ||
| isDone: boolean; | ||
| skills: SkillTag[]; | ||
| canEvaluate: boolean; | ||
| isAllEvaluated: boolean; | ||
| } | ||
|
|
||
| export interface MyJoinedProject { | ||
| id: number; | ||
| title: string; | ||
| recruitmentEndDate: string; | ||
| totalMember: number; | ||
| isBeginner: boolean; | ||
| isDone: boolean; | ||
| skills: SkillTag[]; | ||
| canEvaluate: boolean; | ||
| isAllEvaluated: boolean; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
코드 중복 개선
AuthoredProject, OwnedProject, MyJoinedProject 인터페이스들이 많은 공통 필드를 가지고 있습니다. 기본 인터페이스를 만들어 확장하는 방식으로 중복을 줄일 수 있습니다.
+ interface BaseProject {
+ id: number;
+ title: string;
+ totalMember: number;
+ isBeginner: boolean;
+ isDone: boolean;
+ recruitmentEndDate: string;
+ skills: SkillTag[];
+ canEvaluate: boolean;
+ isAllEvaluated: boolean;
+ }
- export interface AuthoredProject {
+ export interface AuthoredProject extends BaseProject {
- id: number;
- title: string;
description: string;
- totalMember: number;
startDate: string;
estimatedPeriod: string;
authorId: number;
views: number;
- isBeginner: boolean;
- isDone: boolean;
recruitmentStartDate: string;
- recruitmentEndDate: string;
createdAt: string;
updatedAt: string;
methodType: MethodTag;
positions: PositionTag[];
- skills: SkillTag[];
- canEvaluate: boolean;
- isAllEvaluated: boolean;
}동일한 방식으로 다른 인터페이스들도 리팩터링하세요.
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In src/models/admin/userDetail/userProjectData.ts from lines 5 to 49, the
interfaces AuthoredProject, OwnedProject, and MyJoinedProject share many common
fields causing code duplication. Create a base interface containing the shared
properties and have these interfaces extend it, adding only their unique fields.
Apply the same refactoring approach to other similar interfaces in the file to
reduce redundancy.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (2)
src/pages/admin/adminUser/AdminUserProjectsLayout.tsx (1)
4-10: 불필요한 div 래퍼를 제거하세요.최상위 div 래퍼가 불필요합니다. ContentTab을 직접 반환하는 것이 더 깔끔합니다.
export default function AdminUserProjectsLayout() { return ( - <div> - <ContentTab filter={PROJECT_TABS} $justifyContent='space-evenly' /> - </div> + <ContentTab filter={PROJECT_TABS} $justifyContent='space-evenly' /> ); }src/components/common/sidebar/Sidebar.tsx (1)
23-25: useAuthStore 호출을 최적화하세요.관리자 상태 확인 방식 개선은 좋지만, useAuthStore()를 여러 번 호출하는 대신 한 번만 호출하여 필요한 값들을 구조분해할당으로 가져오는 것이 더 효율적입니다.
const Sidebar = ({ menuItems, profileImage, nickname }: SidebarProps) => { - const isLoggedIn = useAuthStore((state) => state.isLoggedIn); + const { isLoggedIn, userData } = useAuthStore(); const location = useLocation(); - const isAdmin = useAuthStore().userData?.admin; + const isAdmin = userData?.admin;
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (17)
src/api/activityLog.api.ts(0 hunks)src/components/admin/adminUserDetail/AdminUserDetail.tsx(4 hunks)src/components/common/sidebar/Sidebar.styled.ts(1 hunks)src/components/common/sidebar/Sidebar.tsx(1 hunks)src/components/user/mypage/ContentTab.tsx(4 hunks)src/components/user/mypage/activityLog/ActivityLog.tsx(1 hunks)src/components/user/mypage/activityLog/inquiries/Inquiries.tsx(2 hunks)src/components/user/mypage/activityLog/inquiries/inquiry/Inquiry.tsx(1 hunks)src/components/user/userPage/userProjectList/UserProjectList.tsx(1 hunks)src/constants/admin/userDetailContentHeader.ts(1 hunks)src/hooks/admin/useGetAllUserActivity.ts(1 hunks)src/hooks/admin/useGetUserProjectData.ts(1 hunks)src/models/admin/userDetail/routing.ts(1 hunks)src/models/admin/userDetail/userActivity.ts(1 hunks)src/models/admin/userDetail/userDetail.applicants.ts(1 hunks)src/models/admin/userDetail/userProjectData.ts(1 hunks)src/pages/admin/adminUser/AdminUserProjectsLayout.tsx(1 hunks)
💤 Files with no reviewable changes (1)
- src/api/activityLog.api.ts
✅ Files skipped from review due to trivial changes (3)
- src/components/user/mypage/activityLog/inquiries/inquiry/Inquiry.tsx
- src/constants/admin/userDetailContentHeader.ts
- src/models/admin/userDetail/userDetail.applicants.ts
🚧 Files skipped from review as they are similar to previous changes (7)
- src/models/admin/userDetail/userActivity.ts
- src/hooks/admin/useGetUserProjectData.ts
- src/components/user/mypage/ContentTab.tsx
- src/components/admin/adminUserDetail/AdminUserDetail.tsx
- src/components/user/mypage/activityLog/inquiries/Inquiries.tsx
- src/hooks/admin/useGetAllUserActivity.ts
- src/models/admin/userDetail/userProjectData.ts
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: YouD0313
PR: devpalsPlus/frontend#334
File: src/components/common/admin/title/AdminTitle.styled.ts:4-10
Timestamp: 2025-06-05T15:59:56.230Z
Learning: YouD0313 prefers using position: fixed for admin headers due to layout requirements, even if it requires viewport-relative width calculations like calc(100vw - 20rem).
🧬 Code Graph Analysis (1)
src/pages/admin/adminUser/AdminUserProjectsLayout.tsx (2)
src/components/user/mypage/ContentTab.tsx (1)
ContentTab(21-88)src/constants/admin/userDetailContentHeader.ts (1)
PROJECT_TABS(3-19)
⏰ Context from checks skipped due to timeout of 90000ms (1)
- GitHub Check: Run Chromatic
🔇 Additional comments (7)
src/models/admin/userDetail/routing.ts (1)
1-1: 이전 타입 불일치 문제가 해결되었습니다.
TabKey타입 정의가 업데이트된 라우팅 구조와 일치하도록 올바르게 수정되었습니다. 이전에 문제가 되었던'basic'이'profile'로 변경되고, 새로운'projects'탭이 추가되어 실제 라우트 구조와 일치합니다.src/components/common/sidebar/Sidebar.styled.ts (1)
3-3: 타입 안전성 개선이 적절합니다.
$isAdmin속성을boolean | undefined로 변경한 것은useAuthStore().userData?.admin의 반환 타입과 일치하여 타입 안전성을 향상시킵니다.src/components/user/mypage/activityLog/ActivityLog.tsx (2)
6-6: useAuthStore import 추가가 적절합니다.사용자 상태 기반 관리자 확인을 위한 필요한 import입니다.
9-9: 관리자 상태 확인 방식 개선이 우수합니다.라우트 기반 확인에서 사용자 상태 기반 확인으로 변경한 것은 더 정확하고 중앙화된 접근 방식입니다. 다른 컴포넌트들과의 일관성도 확보됩니다.
src/components/user/userPage/userProjectList/UserProjectList.tsx (3)
9-9: useAuthStore import 추가가 적절합니다.사용자 상태 기반 관리자 확인을 위한 필요한 import입니다.
12-12: 관리자 상태 확인 방식 개선이 우수합니다.라우트 기반에서 사용자 상태 기반으로 변경한 것은 더 정확하고 일관된 접근 방식입니다.
31-32: 관리자 권한에 따른 링크 동작 분기가 적절합니다.관리자인 경우 새 탭에서 프로젝트 상세 페이지를 여는 것은 관리자 워크플로우에 적합한 UX입니다.
src/api/admin/user.api.ts
Outdated
| @@ -0,0 +1,48 @@ | |||
| import { ApiUserApplicantsData } from '../../models/admin/userDetail/userDetail.applicants'; | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
type
src/api/admin/user.api.ts
Outdated
| @@ -0,0 +1,48 @@ | |||
| import { ApiUserApplicantsData } from '../../models/admin/userDetail/userDetail.applicants'; | |||
| import { ApiUserProjectDataResponse } from '../../models/admin/userDetail/userProjectData'; | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
type
src/api/admin/userActivity.api.ts
Outdated
| @@ -0,0 +1,14 @@ | |||
| import { httpClient } from '../http.api'; | |||
| import { ApiUserActivityResponse } from '../../models/admin/userDetail/userActivity'; | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
type
|
|
||
| type TabKey = 'basic' | 'log' | 'inquiry' | 'joined' | 'created' | 'applied'; | ||
| import useGetUserProjectData from '../../../hooks/admin/useGetUserProjectData'; | ||
| import { TabKey } from '../../../models/admin/userDetail/routing'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
type
| export default function ActivityLog() { | ||
| const { pathname } = useLocation(); | ||
| const isAdmin = pathname.includes('/admin'); | ||
| const isAdmin = useAuthStore().userData?.admin; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이렇게하면 admin일때는 사용자단의 mypage 접근이 안되지 않나요................
| if (!myCommentsData || myCommentsData.length === 0) { | ||
| if ( | ||
| !userActivityData || | ||
| !Array.isArray(userActivityData) || |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이거 데이터 없어도 [] 빈배열로 들어오지않나요? 그러면 이거는 항상 true 조건 아닌가요? 그래서 length로 했던것같은데
| queryFn: () => | ||
| isAdmin ? getUserProjectData(userId) : getMyAppliedStatusList(), | ||
| staleTime: 1 * 60 * 1000, | ||
| enabled: isAdmin ? !!userId : isLoggedIn, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이 조건은 isAdmin 일 때는 무조건 true 인거 아닌가요??
| @@ -0,0 +1,40 @@ | |||
| import { ApiCommonType, User } from '../../apiCommon'; | |||
| import { Career } from '../../applicant'; | |||
| import { SkillTag } from '../../tags'; | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
type을 불러오는건 다 type으로 통일해주세욥
| @@ -0,0 +1,40 @@ | |||
| import { ApiCommonType } from '../../apiCommon'; | |||
| import { MethodTag, PositionTag, SkillTag } from '../../tags'; | |||
| import { AppliedProject, JoinedProject } from '../../userProject'; | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
type
구현내용
연관이슈
close #348
Summary by CodeRabbit
신규 기능
AdminUserProjectsLayout이 추가되어 프로젝트 탭을 통합 관리합니다.버그 수정
리팩터
스타일
문서화 및 타입