-
Notifications
You must be signed in to change notification settings - Fork 2
[feature] 관리자페이지 기본정보 및 소개정보 수정 api 연동 #971
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
[feature] 관리자페이지 기본정보 및 소개정보 수정 api 연동 #971
Conversation
- 동아리 상세 정보를 구조화된 객체로 관리 - introDescription, activityDescription, awards, idealCandidate, benefits, faqs 필드 포함
- 소개, 활동, 수상, 인재상, 혜택, FAQ 편집 기능 - types/club.ts의 공통 타입 사용으로 중복 제거 - description 객체 구조에 맞춰 API 연동 준비
- ClubCoverEditor 컴포넌트 통합 - description 필드를 DetailedDescription 객체 형태로 전송하도록 수정
- useCoverMutation 훅 연동으로 실제 API 호출 - 파일 크기 검증 및 믹스패널 트래킹 추가 - 타입 안정성을 위한 displayedCover string 타입 명시
- CLUB_COVER_UPLOAD_BUTTON_CLICKED 이벤트 추가 - CLUB_COVER_RESET_BUTTON_CLICKED 이벤트 추가 - ClubCoverEditor에서 잘못된 CLUB_LOGO 이벤트를 CLUB_COVER 이벤트로 수정 - 디버그용 console.log 제거
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning
|
| 코호트 / 파일(s) | 변경 요약 |
|---|---|
이벤트 상수 frontend/src/constants/eventName.ts |
ADMIN_EVENT에 CLUB_COVER_UPLOAD_BUTTON_CLICKED, CLUB_COVER_RESET_BUTTON_CLICKED 키 추가 |
커버 에디터 기능 frontend/src/pages/AdminPage/components/ClubCoverEditor/ClubCoverEditor.tsx |
useUploadCover, useDeleteCover 훅 적용; 파일 선택/크기(≤10MB) 검증, 업로드/삭제 mutation 호출, mixpanel 이벤트명 COVER로 변경 |
기본정보 수정 탭 frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.tsx |
updateClub에 전달하는 payload에서 id 제거, description 필드 추가 |
소개정보 탭 frontend/src/pages/AdminPage/tabs/ClubIntroTab/ClubIntroTab.tsx |
로컬 타입(Award/FAQ/IdealCandidate) 제거 후 @/types/club에서 import; clubDetail.description 기반 초기화; 업데이트 페이로드에 description 객체 네스팅 및 전반적 필드 확장 |
타입 정의 frontend/src/types/club.ts |
ClubDetail.description 타입을 string에서 DetailedDescription으로 변경 |
Sequence Diagram
sequenceDiagram
participant User as 관리자 사용자
participant UI as ClubCoverEditor UI
participant Hook as useUploadCover / useDeleteCover
participant API as 서버 API
rect rgb(200,230,255)
Note over User,API: 커버 업로드 흐름
User->>UI: 파일 선택
UI->>UI: 크기 검증 (≤10MB)
alt 크기 초과
UI-->>User: 경고 표시
else 유효 파일
UI->>Hook: mixpanel 이벤트 + uploadMutation.mutate({clubId, file})
Hook->>API: POST /club/{clubId}/cover
API-->>Hook: 업로드 응답
Hook-->>UI: displayedCover 업데이트
end
end
rect rgb(255,230,230)
Note over User,API: 커버 초기화 흐름
User->>UI: 초기화 버튼 클릭
UI->>User: 확인 대화상자
alt 확인
UI->>Hook: mixpanel 이벤트 + deleteMutation.mutate(clubId)
Hook->>API: DELETE /club/{clubId}/cover
API-->>Hook: 삭제 응답
Hook-->>UI: displayedCover를 default로 설정
else 취소
Note over UI: 작업 중단
end
end
Estimated code review effort
🎯 3 (Moderate) | ⏱️ ~20 minutes
Possibly related issues
- Moadong/moadong#969: Admin 페이지의 정보/소개 API 연동 변경과 페이로드 타입 수정이 본 PR과 목적이 유사함 — ClubIntro/ClubInfo 페이로드 변경 관련.
- MOA-460: 관리자 페이지 기본정보 및 소개정보 수정 API 연결 작업 목표와 일치 — 해당 API 연결 변경을 반영함.
Possibly related PRs
- [feature] 동아리 커버 이미지 업로드 및 삭제 hook 추가 #957: ClubCoverEditor에서 사용하는
useUploadCover/useDeleteCover훅 초기 구현과 직접적인 연관. - [feature] 동아리 상세페이지 > 공통 레이아웃(UI 틀) 구현 #964:
ClubDetail.description타입 변경(또는 관련 타입 정리)을 함께 수정한 이력과 중복 영향 가능. - [feature] 관리자페이지 믹스패널 로깅을 적용한다 #874: ADMIN_EVENT 상수 및 mixpanel 로깅 확장과 관련된 이전 변경과 연계됨.
Suggested labels
📬 API
Suggested reviewers
- oesnuj
- lepitaaar
Pre-merge checks and finishing touches
✅ Passed checks (5 passed)
| Check name | Status | Explanation |
|---|---|---|
| Description Check | ✅ Passed | Check skipped - CodeRabbit’s high-level summary is enabled. |
| Title check | ✅ Passed | PR 제목은 관리자 페이지의 기본정보 및 소개정보 수정 API 연동이라는 주요 변경사항을 명확하게 설명하고 있습니다. |
| Linked Issues check | ✅ Passed | PR의 모든 코드 변경사항이 MOA-460 이슈의 목표인 '관리자 페이지 기본정보 및 소개정보 수정 API 연동'을 충족합니다. |
| Out of Scope Changes check | ✅ Passed | 모든 변경사항이 기본정보 및 소개정보 수정 API 연동, 타입 시스템 개선, 커버 이미지 관리와 관련된 범위 내의 변경입니다. |
| Docstring Coverage | ✅ Passed | No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. |
✨ Finishing touches
- 📝 Generate docstrings
🧪 Generate unit tests (beta)
- Create PR with unit tests
- Post copyable unit tests in a comment
- Commit unit tests in branch
feature/#970-admin-edit-api-fix-MOA-460
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.
Comment @coderabbitai help to get the list of available commands and usage tips.
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: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
frontend/src/pages/AdminPage/components/ClubCoverEditor/ClubCoverEditor.tsx (1)
17-25: [긴급] Hooks 규칙 위반: 조건부 hook 호출을 수정해주세요.Lines 24-25에서
useUploadCover()와useDeleteCover()hook이 early return(line 22) 이후에 호출되고 있습니다. 이는 React의 Hooks 규칙을 위반하며, 다음과 같은 심각한 문제를 야기할 수 있습니다:
- Hook 호출 순서 불일치:
clubId의 존재 여부에 따라 hook 호출 여부가 달라져 React의 내부 상태 관리가 깨질 수 있습니다.- 상태 손실: 리렌더링 시 hook 상태가 예기치 않게 초기화되거나 잘못된 상태와 연결될 수 있습니다.
- 프로덕션 크래시 가능성: 런타임에 예측 불가능한 에러가 발생할 수 있습니다.
🔎 수정 방안
Hook 호출을 early return 이전으로 이동하고, 조건부 렌더링을 컴포넌트 하단에서 처리하도록 변경:
const ClubCoverEditor = ({ coverImage }: ClubCoverEditorProps) => { const trackEvent = useMixpanelTrack(); const { clubId } = useAdminClubContext(); const fileInputRef = useRef<HTMLInputElement>(null); - - if (!clubId) return null; const uploadMutation = useUploadCover(); const deleteMutation = useDeleteCover(); + + if (!clubId) return null; const isCoverImageEmpty = !coverImage?.trim();이렇게 하면 hook이 항상 동일한 순서로 호출되어 React의 규칙을 준수하게 됩니다.
Based on learnings, static analysis hints from Biome correctly identified this critical issue.
🧹 Nitpick comments (1)
frontend/src/pages/AdminPage/components/ClubCoverEditor/ClubCoverEditor.tsx (1)
27-30: displayedCover 로직을 간소화할 수 있습니다.현재 로직은 올바르게 동작하지만, 다음과 같이 더 간결하게 표현할 수 있습니다.
🔎 간소화 제안
const isCoverImageEmpty = !coverImage?.trim(); -const displayedCover: string = isCoverImageEmpty - ? defaultCover - : (coverImage ?? defaultCover); +const displayedCover: string = coverImage?.trim() || defaultCover;이렇게 하면 중간 변수 없이 더 읽기 쉬운 코드가 됩니다.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Jira integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (5)
frontend/src/constants/eventName.tsfrontend/src/pages/AdminPage/components/ClubCoverEditor/ClubCoverEditor.tsxfrontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.tsxfrontend/src/pages/AdminPage/tabs/ClubIntroTab/ClubIntroTab.tsxfrontend/src/types/club.ts
🧰 Additional context used
📓 Path-based instructions (3)
frontend/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (frontend/.cursorrules)
frontend/**/*.{ts,tsx,js,jsx}: Replace magic numbers with named constants for clarity
Replace complex/nested ternaries withif/elseor IIFEs for readability
Assign complex boolean conditions to named variables for explicit meaning
Avoid hidden side effects; functions should only perform actions implied by their signature (Single Responsibility Principle)
Use unique and descriptive names for custom wrappers/functions to avoid ambiguity
Define constants near related logic or ensure names link them clearly to avoid silent failures
Break down broad state management into smaller, focused hooks/contexts to reduce coupling
Files:
frontend/src/types/club.tsfrontend/src/constants/eventName.tsfrontend/src/pages/AdminPage/components/ClubCoverEditor/ClubCoverEditor.tsxfrontend/src/pages/AdminPage/tabs/ClubIntroTab/ClubIntroTab.tsxfrontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.tsx
frontend/**/*.{ts,tsx}
📄 CodeRabbit inference engine (frontend/.cursorrules)
Use consistent return types for similar functions/hooks
Files:
frontend/src/types/club.tsfrontend/src/constants/eventName.tsfrontend/src/pages/AdminPage/components/ClubCoverEditor/ClubCoverEditor.tsxfrontend/src/pages/AdminPage/tabs/ClubIntroTab/ClubIntroTab.tsxfrontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.tsx
frontend/**/*.{tsx,jsx}
📄 CodeRabbit inference engine (frontend/.cursorrules)
frontend/**/*.{tsx,jsx}: Abstract complex logic/interactions into dedicated components/HOCs
Separate significantly different conditional UI/logic into distinct components
Colocate simple, localized logic or use inline definitions to reduce context switching
Choose field-level or form-level cohesion based on form requirements when using form libraries like react-hook-form
Use Component Composition instead of Props Drilling to reduce coupling
Files:
frontend/src/pages/AdminPage/components/ClubCoverEditor/ClubCoverEditor.tsxfrontend/src/pages/AdminPage/tabs/ClubIntroTab/ClubIntroTab.tsxfrontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.tsx
🧠 Learnings (3)
📓 Common learnings
Learnt from: seongwon030
Repo: Moadong/moadong PR: 195
File: frontend/src/pages/AdminPage/AdminPage.tsx:7-7
Timestamp: 2025-03-19T05:18:07.818Z
Learning: AdminPage.tsx에서 현재 하드코딩된 클럽 ID('67d2e3b9b15c136c6acbf20b')는 로그인 기능 구현 후 동적으로 가져오는 방식으로 수정될 예정입니다.
📚 Learning: 2025-03-19T05:18:07.818Z
Learnt from: seongwon030
Repo: Moadong/moadong PR: 195
File: frontend/src/pages/AdminPage/AdminPage.tsx:7-7
Timestamp: 2025-03-19T05:18:07.818Z
Learning: AdminPage.tsx에서 현재 하드코딩된 클럽 ID('67d2e3b9b15c136c6acbf20b')는 로그인 기능 구현 후 동적으로 가져오는 방식으로 수정될 예정입니다.
Applied to files:
frontend/src/constants/eventName.tsfrontend/src/pages/AdminPage/tabs/ClubIntroTab/ClubIntroTab.tsxfrontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.tsx
📚 Learning: 2025-11-25T14:08:23.253Z
Learnt from: CR
Repo: Moadong/moadong PR: 0
File: frontend/.cursorrules:0-0
Timestamp: 2025-11-25T14:08:23.253Z
Learning: Applies to frontend/**/*.{tsx,jsx} : Abstract complex logic/interactions into dedicated components/HOCs
Applied to files:
frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.tsx
🧬 Code graph analysis (1)
frontend/src/pages/AdminPage/components/ClubCoverEditor/ClubCoverEditor.tsx (2)
frontend/src/hooks/queries/club/cover/useCoverMutation.ts (2)
useUploadCover(10-36)useDeleteCover(38-55)frontend/src/constants/eventName.ts (1)
ADMIN_EVENT(40-82)
🪛 Biome (2.1.2)
frontend/src/pages/AdminPage/components/ClubCoverEditor/ClubCoverEditor.tsx
[error] 24-24: This hook is being called conditionally, but all hooks must be called in the exact same order in every component render.
Hooks should not be called after an early return.
For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level
(lint/correctness/useHookAtTopLevel)
[error] 25-25: This hook is being called conditionally, but all hooks must be called in the exact same order in every component render.
Hooks should not be called after an early return.
For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level
(lint/correctness/useHookAtTopLevel)
🔇 Additional comments (5)
frontend/src/constants/eventName.ts (1)
48-49: LGTM! 커버 이미지 이벤트 추가가 적절합니다.동아리 커버 업로드 및 초기화 버튼 클릭 이벤트가 기존 네이밍 컨벤션을 잘 따르고 있으며, 로고 관련 이벤트들과 논리적으로 그룹화되어 있습니다.
frontend/src/types/club.ts (1)
18-18: LGTM! ClubDetail 타입 확장이 적절합니다.
description을string에서DetailedDescription객체로 변경하여 소개, 활동, 수상, 인재상, 혜택, FAQ 등 풍부한 설명 데이터를 구조화할 수 있게 되었습니다. 이는ClubApiResponse와의 일관성을 확보하며,ClubIntroTab과ClubInfoEditTab에서 올바르게 사용되고 있습니다.Also applies to: 22-22, 25-25, 30-30
frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.tsx (1)
121-131: LGTM! 페이로드 구조가 적절하게 개선되었습니다.기본 정보 수정 시
description객체를 그대로 전달하여 기존 소개 정보가 유지되도록 했습니다. 이는ClubIntroTab에서 별도로 상세 정보를 수정하는 관심사 분리 원칙을 잘 따르고 있습니다.frontend/src/pages/AdminPage/tabs/ClubIntroTab/ClubIntroTab.tsx (1)
36-45: LGTM! description 필드 초기화가 올바르게 구현되었습니다.
clubDetail?.description의 각 필드를 안전하게 읽어와 state를 초기화하고 있으며, 옵셔널 체이닝과 적절한 기본값 처리가 되어 있습니다.frontend/src/pages/AdminPage/components/ClubCoverEditor/ClubCoverEditor.tsx (1)
32-63: LGTM! 커버 이미지 업로드 및 삭제 로직이 잘 구현되었습니다.파일 선택 및 업로드 처리가 올바르게 구현되었습니다:
- 파일 크기 검증 (10MB 제한)
- 적절한 이벤트 추적 (
CLUB_COVER_UPLOAD_BUTTON_CLICKED,CLUB_COVER_RESET_BUTTON_CLICKED)- Mutation을 통한 API 호출
- 초기화 전 사용자 확인 요청으로 좋은 UX 제공
frontend/src/pages/AdminPage/tabs/ClubIntroTab/ClubIntroTab.tsx
Outdated
Show resolved
Hide resolved
- updateClub 호출 시 타입 안정성 개선
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: 1
🧹 Nitpick comments (1)
frontend/src/pages/AdminPage/tabs/ClubIntroTab/ClubIntroTab.tsx (1)
56-73: updatedData에 명시적 타입 지정 권장updatedData 객체에 명시적인 타입을 지정하면 타입 안전성이 향상되고 의도를 명확히 할 수 있습니다.
🔎 제안: 명시적 타입 지정
-const updatedData = { +const updatedData: Partial<ClubDetail> = { name: clubDetail.name, category: clubDetail.category, // ... rest of fields };또는 description만 전송하는 경우:
+const updatedData: Pick<ClubDetail, 'description'> = { description: { introDescription, activityDescription, awards, idealCandidate, benefits, faqs, }, };As per coding guidelines, "Use consistent return types for similar functions/hooks"를 위해 명시적 타입 사용을 권장합니다.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Jira integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
frontend/src/pages/AdminPage/tabs/ClubIntroTab/ClubIntroTab.tsx
🧰 Additional context used
📓 Path-based instructions (3)
frontend/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (frontend/.cursorrules)
frontend/**/*.{ts,tsx,js,jsx}: Replace magic numbers with named constants for clarity
Replace complex/nested ternaries withif/elseor IIFEs for readability
Assign complex boolean conditions to named variables for explicit meaning
Avoid hidden side effects; functions should only perform actions implied by their signature (Single Responsibility Principle)
Use unique and descriptive names for custom wrappers/functions to avoid ambiguity
Define constants near related logic or ensure names link them clearly to avoid silent failures
Break down broad state management into smaller, focused hooks/contexts to reduce coupling
Files:
frontend/src/pages/AdminPage/tabs/ClubIntroTab/ClubIntroTab.tsx
frontend/**/*.{tsx,jsx}
📄 CodeRabbit inference engine (frontend/.cursorrules)
frontend/**/*.{tsx,jsx}: Abstract complex logic/interactions into dedicated components/HOCs
Separate significantly different conditional UI/logic into distinct components
Colocate simple, localized logic or use inline definitions to reduce context switching
Choose field-level or form-level cohesion based on form requirements when using form libraries like react-hook-form
Use Component Composition instead of Props Drilling to reduce coupling
Files:
frontend/src/pages/AdminPage/tabs/ClubIntroTab/ClubIntroTab.tsx
frontend/**/*.{ts,tsx}
📄 CodeRabbit inference engine (frontend/.cursorrules)
Use consistent return types for similar functions/hooks
Files:
frontend/src/pages/AdminPage/tabs/ClubIntroTab/ClubIntroTab.tsx
🧠 Learnings (4)
📓 Common learnings
Learnt from: seongwon030
Repo: Moadong/moadong PR: 195
File: frontend/src/pages/AdminPage/AdminPage.tsx:7-7
Timestamp: 2025-03-19T05:18:07.818Z
Learning: AdminPage.tsx에서 현재 하드코딩된 클럽 ID('67d2e3b9b15c136c6acbf20b')는 로그인 기능 구현 후 동적으로 가져오는 방식으로 수정될 예정입니다.
📚 Learning: 2025-03-19T05:18:07.818Z
Learnt from: seongwon030
Repo: Moadong/moadong PR: 195
File: frontend/src/pages/AdminPage/AdminPage.tsx:7-7
Timestamp: 2025-03-19T05:18:07.818Z
Learning: AdminPage.tsx에서 현재 하드코딩된 클럽 ID('67d2e3b9b15c136c6acbf20b')는 로그인 기능 구현 후 동적으로 가져오는 방식으로 수정될 예정입니다.
Applied to files:
frontend/src/pages/AdminPage/tabs/ClubIntroTab/ClubIntroTab.tsx
📚 Learning: 2025-07-19T05:05:10.196Z
Learnt from: seongwon030
Repo: Moadong/moadong PR: 548
File: frontend/src/pages/ClubDetailPage/ClubDetailPage.tsx:17-57
Timestamp: 2025-07-19T05:05:10.196Z
Learning: ClubDetailPage.tsx에서 notJoinedClubNames 배열의 하드코딩은 의도적인 설계 결정입니다. 개발자가 명시적으로 하드코딩을 선택했으므로 이에 대한 리팩토링 제안을 하지 않아야 합니다.
Applied to files:
frontend/src/pages/AdminPage/tabs/ClubIntroTab/ClubIntroTab.tsx
📚 Learning: 2025-11-25T14:08:23.253Z
Learnt from: CR
Repo: Moadong/moadong PR: 0
File: frontend/.cursorrules:0-0
Timestamp: 2025-11-25T14:08:23.253Z
Learning: Applies to frontend/**/*.{ts,tsx} : Use consistent return types for similar functions/hooks
Applied to files:
frontend/src/pages/AdminPage/tabs/ClubIntroTab/ClubIntroTab.tsx
oesnuj
left a comment
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.
수고하셨습니다~
#️⃣연관된 이슈
📝작업 내용
1. 타입 시스템 개선 (types/club.ts)ClubDetail.description 구조 변경
2. 동아리 기본 정보 수정 (ClubInfoEditTab)API 연동 완료
3. 동아리 상세 정보 수정 (ClubIntroTab)새로운 편집 탭 구현
DetailedDescription 구조에 맞춰 데이터 전송 형식 구현
4. 커버 이미지 관리 기능 (ClubCoverEditor)
TanStack Query 기반 API 연동
useUploadCover: S3 presigned URL 방식 업로드useDeleteCover: 커버 이미지 삭제중점적으로 리뷰받고 싶은 부분(선택)
논의하고 싶은 부분(선택)
🫡 참고사항
Summary by CodeRabbit
릴리스 노트
새 기능
개선사항
✏️ Tip: You can customize this high-level summary in your review settings.