[feature] 관리자 페이지 UI 변경에 따른 사이드바 및 레이아웃 수정#942
Conversation
- ClubLogoEditor, 클럽 이름 표시 제거 - 사이드바 스타일 개선 (padding, gap, border-radius) - SideBar props 제거 (clubLogo, clubName)
- PageContainer 제거하고 Background/Layout 구조로 분리 - 전체 배경색(#f2f2f2) 적용 - 컴포넌트 네이밍 개선 (Content → MainContent) - Divider 제거하고 gap으로 간격 조정
- 모든 탭에서 동일한 헤더 및 레이아웃 스타일 적용 - UI 구조 통일로 유지보수 편의성 향상
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning
|
| Cohort / File(s) | 변경 요약 |
|---|---|
AdminPage 레이아웃 frontend/src/pages/AdminPage/AdminPage.styles.ts, frontend/src/pages/AdminPage/AdminPage.tsx |
AdminPageContainer/Divider 제거 → Background/Layout/MainContent 도입. JSX 구조 대체 및 SideBar 호출에서 props 제거. |
ContentSection 신규 컴포넌트 frontend/src/pages/AdminPage/components/ContentSection/ContentSection.styles.ts, frontend/src/pages/AdminPage/components/ContentSection/ContentSection.tsx |
Root/Header/Body 컴포넌트 추가 및 Object.assign으로 서브컴포넌트 노출. |
SideBar 리팩토링 frontend/src/pages/AdminPage/components/SideBar/SideBar.styles.ts, frontend/src/pages/AdminPage/components/SideBar/SideBar.tsx |
SideBarProps 제거(클럽 로고/이름 props 제거), 스타일명(예: Divider→SidebarDivider) 변경, 헤더 간소화, 로그아웃 확인 프롬프트 추가. |
ClubLogoEditor 변경 frontend/src/pages/AdminPage/components/ClubLogoEditor/ClubLogoEditor.styles.ts, frontend/src/pages/AdminPage/components/ClubLogoEditor/ClubLogoEditor.tsx |
로고 크기/스타일 축소(168→100), 드롭다운 편집 메뉴 제거 → 인라인 버튼(업로드/초기화)으로 단순화, 스타일/컴포넌트명 대체(UploadButton/ResetButton 등), 이벤트 트래킹 추가. |
탭 스타일·구조 전환 (여러 탭) frontend/src/pages/AdminPage/tabs/.../*.styles.ts, frontend/src/pages/AdminPage/tabs/.../*.tsx |
다수 탭에서 컨테이너명(예: *EditorContainer → Container) 및 스타일 엔티티 제거/리네임. 기존 헤더/타이틀을 ContentSection.Header/ContentSection.Body로 전환, 일부 레이블·플레이스홀더·버튼 텍스트 변경. |
PhotoEdit 및 파일 업로드 변경 frontend/src/pages/AdminPage/tabs/PhotoEditTab/* |
ContentSection 적용, 파일 입력에 크기 검사 및 입력 리셋 로직 추가, 업로드 흐름 유지. |
MakeTags 접근성 개선 frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/components/MakeTags/* |
삭제 아이콘을 background-image → inline img로 변경, placeholder/aria-label 추가, Remove 버튼에 type·접근성 레이블 추가. |
공통 스타일 미세조정 frontend/src/components/common/InputField/InputField.styles.ts, 기타 스타일 파일들 |
레이블 여백(12px→8px) 등 소규모 CSS 조정 및 포맷 정리. |
Estimated code review effort
🎯 3 (Moderate) | ⏱️ ~25 minutes
- 집중 검토가 필요한 부분:
- ContentSection의 타입·서브컴포넌트 노출 방식 및 기존 사용처 호환성
- SideBar에서 clubLogo/clubName 제거로 인한 호출부 영향
- ClubLogoEditor / PhotoEditTab의 파일 업로드·초기화 흐름(파일 크기 검사, hidden input 리셋)
- 여러 styles.ts에서의 export 명 변경으로 인한 import 누락 위험
Possibly related issues
- [feature] MOA-429 관리자 페이지 UI 변경에 따른 사이드바 및 레이아웃 수정 #941 — AdminPage 레이아웃 및 SideBar 수정 목표와 직접적으로 일치합니다.
- Moadong/moadong#583 — ClubLogoEditor 및 RecruitEditTab의 UI·레이블 변경과 연관됩니다.
- MOA-429 — 관리자 페이지 UI 변경에 따른 사이드바 및 레이아웃 수정 요구사항과 일치합니다.
Possibly related PRs
- [feature] 관리자 페이지 클럽 로고 수정 및 초기화 UI/UX 수정 #342 — ClubLogoEditor 관련 스타일·컴포넌트 변경과 오버랩(로고 편집 UI).
- [release] FE v1.1.6 릴리즈 #842 — AdminPage 구조(레이아웃/ContentSection/SideBar) 변경과 중복 지점 다수.
- 관리자 페이지 UI/UX 개선 및 버그 수정 (모집대상 텍스트, 탭 경고, 로고 업로드) #587 — 로고 업로드 입력 리셋/동작 관련 변경과 연관성(파일 입력 처리).
Suggested labels
🔨 Refactor
Suggested reviewers
- seongje973
- lepitaaar
- suhyun113
- seongwon030
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 제목이 주요 변경사항인 사이드바 및 레이아웃 수정을 명확하게 설명하고 있으며, 변경 내용과 일치합니다. |
| Linked Issues check | ✅ Passed | PR의 모든 변경사항이 MOA-429 이슈의 목표(사이드바 컴포넌트 수정, 레이아웃 스타일 조정, ContentSection 도입)와 일치합니다. |
| Out of Scope Changes check | ✅ Passed | 모든 변경사항이 관리자 페이지 UI 개선 범위 내에 있으며, MOA-429 목표와 직접적으로 연관되어 있습니다. |
| 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/#941-admin-ui-sidebar-layout-MOA-429
📜 Recent 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/PhotoEditTab/PhotoEditTab.tsx(3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- frontend/src/pages/AdminPage/tabs/PhotoEditTab/PhotoEditTab.tsx
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.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
frontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.tsx (1)
67-75: 수동 시간대 오프셋 계산 대신 라이브러리 사용 권장정적 9시간 오프셋 대신
date-fns-tz또는 서버 측 UTC/ISO 형식을 사용하면 유지보수성과 확장성이 향상됩니다. 현재는 한국 시간대에서 정상 작동하지만, 코드의 재사용성과 국제화를 고려하면 타이머존 라이브러리 사용이 권장됩니다.
🧹 Nitpick comments (6)
frontend/src/pages/AdminPage/tabs/ApplicationListTab/ApplicationListTab.tsx (1)
121-123: 인라인 스타일 대신 styled component를 사용하세요.마진 값(24px)을 인라인 스타일로 직접 지정하는 것보다 named constant나 styled component를 사용하는 것이 좋습니다.
🔎 제안하는 리팩토링
+const HeaderWrapper = styled.div` + margin-bottom: 24px; +`; + return ( <Styled.Container> - <div style={{ marginBottom: '24px' }}> + <HeaderWrapper> <ContentSection.Header title='지원서 목록' /> - </div> + </HeaderWrapper>코딩 가이드라인에 따라 magic number를 named constant로 대체하고 인라인 스타일을 피하는 것이 권장됩니다.
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/ApplicantsListTab.tsx (1)
120-122: 인라인 스타일 대신 styled component를 사용하세요.ApplicationListTab과 동일하게 인라인 스타일을 사용하고 있습니다. styled component로 추출하여 일관성을 유지하세요.
🔎 제안하는 리팩토링
+const HeaderWrapper = styled.div` + margin-bottom: 24px; +`; + return ( <Styled.Container> - <div style={{ marginBottom: '24px' }}> + <HeaderWrapper> <ContentSection.Header title='지원서 목록' /> - </div> + </HeaderWrapper>코딩 가이드라인에 따라 magic number와 인라인 스타일을 피하는 것이 권장됩니다.
frontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.styles.ts (2)
3-7: 중복 스타일 컴포넌트 추출을 고려해보세요.
Container스타일이AccountEditTab,ClubInfoEditTab,PhotoEditTab,RecruitEditTab4개 파일에서 동일하게 정의되어 있습니다. 공통 스타일 파일로 추출하면 유지보수성이 향상됩니다.🔎 공통 스타일 추출 예시
공통 스타일 파일 (예:
AdminPage/components/common.styles.ts):export const TabContainer = styled.div` display: flex; flex-direction: column; gap: 60px; `;
24-29:transition속성과 실제 사용되는 속성 불일치.Line 28에서
background-color전환을 정의하지만, Line 25에서는background단축 속성을 사용합니다. 일관성을 위해 동일한 속성명을 사용하세요.🔎 수정 제안
transition: - background-color 0.12s ease, + background 0.12s ease, transform 0.06s ease;frontend/src/pages/AdminPage/AdminPage.styles.ts (1)
2-5: 배경색에 대한 상수화를 고려해보세요.
#f2f2f2와 같은 색상 값을 테마 변수나 상수로 관리하면 일관성 유지에 도움이 됩니다.frontend/src/pages/AdminPage/components/ContentSection/ContentSection.tsx (1)
21-31: action을 감싸는 div의 필요성 검토Line 28에서
action을<div>로 감싸고 있습니다.ContentSectionHeader스타일링이 이 래퍼를 필요로 하지 않는다면, 직접 렌더링하는 것이 더 간결합니다.🔎 제안하는 리팩토링
const ContentSectionHeader = ({ title, action, }: ContentSectionHeaderProps) => { return ( <Styled.ContentSectionHeader> <Styled.ContentSectionTitle>{title}</Styled.ContentSectionTitle> - {action && <div>{action}</div>} + {action} </Styled.ContentSectionHeader> ); };
📜 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 (17)
frontend/src/pages/AdminPage/AdminPage.styles.ts(1 hunks)frontend/src/pages/AdminPage/AdminPage.tsx(2 hunks)frontend/src/pages/AdminPage/components/ContentSection/ContentSection.styles.ts(1 hunks)frontend/src/pages/AdminPage/components/ContentSection/ContentSection.tsx(1 hunks)frontend/src/pages/AdminPage/components/SideBar/SideBar.styles.ts(2 hunks)frontend/src/pages/AdminPage/components/SideBar/SideBar.tsx(5 hunks)frontend/src/pages/AdminPage/tabs/AccountEditTab/AccountEditTab.styles.ts(2 hunks)frontend/src/pages/AdminPage/tabs/AccountEditTab/AccountEditTab.tsx(2 hunks)frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/ApplicantsListTab.tsx(2 hunks)frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.tsx(2 hunks)frontend/src/pages/AdminPage/tabs/ApplicationListTab/ApplicationListTab.tsx(2 hunks)frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.styles.ts(2 hunks)frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.tsx(3 hunks)frontend/src/pages/AdminPage/tabs/PhotoEditTab/PhotoEditTab.styles.ts(1 hunks)frontend/src/pages/AdminPage/tabs/PhotoEditTab/PhotoEditTab.tsx(3 hunks)frontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.styles.ts(2 hunks)frontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.tsx(3 hunks)
🧰 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/ApplicantsTab/ApplicantsTab.tsxfrontend/src/pages/AdminPage/components/ContentSection/ContentSection.tsxfrontend/src/pages/AdminPage/components/SideBar/SideBar.styles.tsfrontend/src/pages/AdminPage/tabs/AccountEditTab/AccountEditTab.tsxfrontend/src/pages/AdminPage/components/ContentSection/ContentSection.styles.tsfrontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/ApplicantsListTab.tsxfrontend/src/pages/AdminPage/components/SideBar/SideBar.tsxfrontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.styles.tsfrontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.styles.tsfrontend/src/pages/AdminPage/tabs/AccountEditTab/AccountEditTab.styles.tsfrontend/src/pages/AdminPage/tabs/ApplicationListTab/ApplicationListTab.tsxfrontend/src/pages/AdminPage/tabs/PhotoEditTab/PhotoEditTab.tsxfrontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.tsxfrontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.tsxfrontend/src/pages/AdminPage/AdminPage.styles.tsfrontend/src/pages/AdminPage/AdminPage.tsxfrontend/src/pages/AdminPage/tabs/PhotoEditTab/PhotoEditTab.styles.ts
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/ApplicantsTab/ApplicantsTab.tsxfrontend/src/pages/AdminPage/components/ContentSection/ContentSection.tsxfrontend/src/pages/AdminPage/tabs/AccountEditTab/AccountEditTab.tsxfrontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/ApplicantsListTab.tsxfrontend/src/pages/AdminPage/components/SideBar/SideBar.tsxfrontend/src/pages/AdminPage/tabs/ApplicationListTab/ApplicationListTab.tsxfrontend/src/pages/AdminPage/tabs/PhotoEditTab/PhotoEditTab.tsxfrontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.tsxfrontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.tsxfrontend/src/pages/AdminPage/AdminPage.tsx
frontend/**/*.{ts,tsx}
📄 CodeRabbit inference engine (frontend/.cursorrules)
Use consistent return types for similar functions/hooks
Files:
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.tsxfrontend/src/pages/AdminPage/components/ContentSection/ContentSection.tsxfrontend/src/pages/AdminPage/components/SideBar/SideBar.styles.tsfrontend/src/pages/AdminPage/tabs/AccountEditTab/AccountEditTab.tsxfrontend/src/pages/AdminPage/components/ContentSection/ContentSection.styles.tsfrontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/ApplicantsListTab.tsxfrontend/src/pages/AdminPage/components/SideBar/SideBar.tsxfrontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.styles.tsfrontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.styles.tsfrontend/src/pages/AdminPage/tabs/AccountEditTab/AccountEditTab.styles.tsfrontend/src/pages/AdminPage/tabs/ApplicationListTab/ApplicationListTab.tsxfrontend/src/pages/AdminPage/tabs/PhotoEditTab/PhotoEditTab.tsxfrontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.tsxfrontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.tsxfrontend/src/pages/AdminPage/AdminPage.styles.tsfrontend/src/pages/AdminPage/AdminPage.tsxfrontend/src/pages/AdminPage/tabs/PhotoEditTab/PhotoEditTab.styles.ts
🧠 Learnings (8)
📚 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} : Separate significantly different conditional UI/logic into distinct components
Applied to files:
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.tsxfrontend/src/pages/AdminPage/components/ContentSection/ContentSection.tsxfrontend/src/pages/AdminPage/tabs/AccountEditTab/AccountEditTab.tsxfrontend/src/pages/AdminPage/components/ContentSection/ContentSection.styles.tsfrontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/ApplicantsListTab.tsxfrontend/src/pages/AdminPage/tabs/PhotoEditTab/PhotoEditTab.tsxfrontend/src/pages/AdminPage/AdminPage.styles.ts
📚 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/components/ContentSection/ContentSection.tsxfrontend/src/pages/AdminPage/tabs/AccountEditTab/AccountEditTab.tsxfrontend/src/pages/AdminPage/components/ContentSection/ContentSection.styles.tsfrontend/src/pages/AdminPage/tabs/PhotoEditTab/PhotoEditTab.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} : Use Component Composition instead of Props Drilling to reduce coupling
Applied to files:
frontend/src/pages/AdminPage/components/ContentSection/ContentSection.tsxfrontend/src/pages/AdminPage/tabs/PhotoEditTab/PhotoEditTab.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,js,jsx} : Break down broad state management into smaller, focused hooks/contexts to reduce coupling
Applied to files:
frontend/src/pages/AdminPage/components/ContentSection/ContentSection.tsxfrontend/src/pages/AdminPage/tabs/AccountEditTab/AccountEditTab.tsxfrontend/src/pages/AdminPage/tabs/PhotoEditTab/PhotoEditTab.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/**/*.{ts,tsx,js,jsx} : Avoid hidden side effects; functions should only perform actions implied by their signature (Single Responsibility Principle)
Applied to files:
frontend/src/pages/AdminPage/components/SideBar/SideBar.tsx
📚 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/components/SideBar/SideBar.tsxfrontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.tsxfrontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.tsxfrontend/src/pages/AdminPage/AdminPage.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} : Colocate simple, localized logic or use inline definitions to reduce context switching
Applied to files:
frontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.tsxfrontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.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/ClubInfoEditTab/ClubInfoEditTab.tsx
🧬 Code graph analysis (10)
frontend/src/pages/AdminPage/components/ContentSection/ContentSection.tsx (1)
frontend/src/pages/AdminPage/components/ContentSection/ContentSection.styles.ts (3)
ContentSectionHeader(9-15)ContentSectionBody(24-28)ContentSection(3-7)
frontend/src/pages/AdminPage/tabs/AccountEditTab/AccountEditTab.tsx (1)
frontend/src/constants/eventName.ts (1)
ADMIN_EVENT(38-78)
frontend/src/pages/AdminPage/components/ContentSection/ContentSection.styles.ts (1)
frontend/src/pages/AdminPage/components/ContentSection/ContentSection.tsx (1)
ContentSection(37-40)
frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.styles.ts (3)
frontend/src/pages/AdminPage/tabs/AccountEditTab/AccountEditTab.styles.ts (1)
Container(3-7)frontend/src/pages/AdminPage/tabs/PhotoEditTab/PhotoEditTab.styles.ts (1)
Container(3-7)frontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.styles.ts (1)
Container(3-7)
frontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.styles.ts (3)
frontend/src/pages/AdminPage/tabs/AccountEditTab/AccountEditTab.styles.ts (1)
Container(3-7)frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.styles.ts (1)
Container(3-7)frontend/src/pages/AdminPage/tabs/PhotoEditTab/PhotoEditTab.styles.ts (1)
Container(3-7)
frontend/src/pages/AdminPage/tabs/AccountEditTab/AccountEditTab.styles.ts (3)
frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.styles.ts (1)
Container(3-7)frontend/src/pages/AdminPage/tabs/PhotoEditTab/PhotoEditTab.styles.ts (1)
Container(3-7)frontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.styles.ts (1)
Container(3-7)
frontend/src/pages/AdminPage/tabs/PhotoEditTab/PhotoEditTab.tsx (3)
frontend/src/pages/AdminPage/tabs/PhotoEditTab/components/ImageUpload/ImageUpload.tsx (1)
ImageUpload(15-68)frontend/src/pages/AdminPage/tabs/PhotoEditTab/components/ImagePreview/ImagePreview.tsx (1)
ImagePreview(9-18)frontend/src/constants/eventName.ts (1)
ADMIN_EVENT(38-78)
frontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.tsx (1)
frontend/src/constants/eventName.ts (1)
ADMIN_EVENT(38-78)
frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.tsx (4)
frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/components/SelectTags/SelectTags.styles.ts (1)
Button(7-21)frontend/src/constants/eventName.ts (1)
ADMIN_EVENT(38-78)frontend/src/constants/snsConfig.ts (1)
SNS_CONFIG(5-26)frontend/src/types/club.ts (1)
SNSPlatform(14-14)
frontend/src/pages/AdminPage/tabs/PhotoEditTab/PhotoEditTab.styles.ts (3)
frontend/src/pages/AdminPage/tabs/AccountEditTab/AccountEditTab.styles.ts (1)
Container(3-7)frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.styles.ts (1)
Container(3-7)frontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.styles.ts (1)
Container(3-7)
🔇 Additional comments (19)
frontend/src/pages/AdminPage/components/SideBar/SideBar.styles.ts (1)
1-60: LGTM! 사이드바 스타일 개선이 잘 적용되었습니다.스타일 업데이트가 PR 목표와 일치하며, 일관된 레이아웃을 제공합니다.
Divider를SidebarDivider로 이름 변경한 것은 명확성을 높입니다.frontend/src/pages/AdminPage/tabs/AccountEditTab/AccountEditTab.tsx (1)
69-125: LGTM! ContentSection 패턴이 올바르게 적용되었습니다.헤더와 본문이 명확하게 분리되어 있고, 기존 로직을 유지하면서 레이아웃만 개선했습니다. 다른 탭들의 좋은 참고 예시가 될 것 같습니다.
frontend/src/pages/AdminPage/tabs/AccountEditTab/AccountEditTab.styles.ts (1)
25-30: LGTM! GuidanceBox 스타일 정리가 적절합니다.하단 마진을 제거하고 spacing을 부모 Container의
gap속성으로 관리하는 것이 더 일관된 레이아웃을 제공합니다.frontend/src/pages/AdminPage/AdminPage.tsx (1)
22-29: LGTM! 레이아웃 구조 개선이 잘 적용되었습니다.새로운
Background > Layout > MainContent구조가 명확하고,SideBar가 독립 컴포넌트로 단순화되어 재사용성이 향상되었습니다. PR 목표와 완벽하게 일치합니다.frontend/src/pages/AdminPage/tabs/PhotoEditTab/PhotoEditTab.tsx (1)
82-114: LGTM! ContentSection 구조가 명확하게 적용되었습니다."활동사진 추가"와 "활동사진 수정"이 별도 블록으로 깔끔하게 분리되어 있고, 기존 기능을 모두 유지하면서 레이아웃만 개선했습니다.
frontend/src/pages/AdminPage/tabs/PhotoEditTab/PhotoEditTab.styles.ts (2)
3-7: LGTM!
Container스타일이 다른 탭들과 일관되게 적용되었습니다. 앞서 언급한 대로 공통 스타일 추출을 고려해볼 수 있습니다.
9-21: LGTM!
ImageGrid의 가로 스크롤 처리와Label스타일이 적절하게 구현되었습니다.frontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.tsx (2)
138-191: ContentSection 컴파운드 패턴이 적절히 적용되었습니다.
ContentSection,ContentSection.Header,ContentSection.Body구조가 일관되게 사용되었고, 폼 요소들이 논리적으로 그룹화되어 있습니다. 전반적인 레이아웃 구조가 잘 개선되었습니다.
57-58: 상태 초기화 로직이 적절합니다.
prev || value패턴을 사용하여 사용자가 이미 입력한 값을 덮어쓰지 않도록 보호하고 있습니다. 의도된 동작으로 보입니다.frontend/src/pages/AdminPage/AdminPage.styles.ts (1)
7-23: LGTM!
Layout과MainContent구조가 관리자 페이지 전용 레이아웃 요구사항에 맞게 잘 설계되었습니다. flex 기반 레이아웃과 max-width 제한이 적절히 적용되었습니다.frontend/src/pages/AdminPage/components/ContentSection/ContentSection.styles.ts (2)
1-28: 컴파운드 패턴 스타일이 잘 구성되었습니다.
ContentSection,ContentSectionHeader,ContentSectionTitle,ContentSectionBody가 명확하게 분리되어 있고, 시맨틱 HTML 요소(section,h2)를 적절히 사용하여 접근성도 고려되었습니다. PR 목표에서 언급한 탭별 제목/섹션 스타일 통일에 적합한 구조입니다.
24-28:ContentSectionBody의 gap 값에 대한 확인.
ContentSectionBody의gap: 16px이 탭 컨테이너의gap: 60px과 다르지만, 섹션 내부 요소 간격으로는 적절합니다. 의도된 디자인이 맞는지 확인해주세요.frontend/src/pages/AdminPage/components/SideBar/SideBar.tsx (2)
61-63: 로그아웃 확인 다이얼로그 추가 - 좋습니다!실수로 로그아웃하는 것을 방지하는 좋은 UX 개선입니다.
43-43: 사이드바 단순화 - 좋습니다!props 제거와 헤더 구조 단순화로 컴포넌트의 재사용성이 향상되었습니다. PR 목표에 부합합니다.
Also applies to: 86-86
frontend/src/pages/AdminPage/components/ContentSection/ContentSection.tsx (1)
1-40: ContentSection 컴파운드 패턴 구조 - 훌륭합니다!컴파운드 컴포넌트 패턴이 적절하게 구현되었습니다. 관리자 페이지 전체에 일관된 레이아웃을 제공하는 명확한 구조입니다. PR 목표와 잘 부합합니다.
Based on learnings, 컴포넌트 컴포지션을 활용하여 props drilling을 줄이고 있습니다.
frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.tsx (3)
123-178: 첫 번째 ContentSection 블록 구조 - 좋습니다!동아리 카드 정보 섹션이 ContentSection 패턴을 적절하게 사용하고 있습니다. 헤더와 바디가 명확하게 분리되어 있고, 모든 필드가 상태와 올바르게 연결되어 있습니다.
180-215: SNS 섹션 리팩토링 - 훌륭합니다!
Object.entries를 사용한 SNS_CONFIG 반복 처리가 깔끔하고, 각 플랫폼에 대한 에러 상태와 값 관리가 잘 구현되어 있습니다. 이벤트 트래킹에snsPlatform정보를 포함한 것도 좋습니다.
122-217: 전체 탭 레이아웃 일관성 적용 - 우수합니다!ClubInfoEditTab이 ContentSection 패턴을 완벽하게 채택하여 관리자 페이지 전체의 일관성을 확보했습니다. 두 개의 명확한 섹션(동아리 카드 정보, SNS 연결)으로 구조화되어 있고, 모든 기능이 제대로 작동합니다.
frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.styles.ts (1)
3-35: 스타일 일관성 확보 - 좋습니다!
Container스타일(60px gap)이 다른 관리자 탭들(AccountEditTab, PhotoEditTab, RecruitEditTab)과 일관되게 적용되었습니다.FieldGroup과SNSLabel도 명확한 용도로 잘 정의되어 있습니다.
frontend/src/pages/AdminPage/tabs/AccountEditTab/AccountEditTab.styles.ts
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
frontend/src/pages/AdminPage/tabs/PhotoEditTab/PhotoEditTab.tsx (1)
99-109: 선택적 개선: 용량 초과 파일 메시지 개선현재 구현은 첫 번째 용량 초과 파일만 사용자에게 알려줍니다. 여러 파일이 용량을 초과한 경우 모든 파일 이름을 표시하면 사용자 경험이 개선됩니다.
🔎 제안하는 개선 방안
- const oversizedFile = Array.from(files).find( - (file) => file.size > MAX_FILE_SIZE, - ); - - if (oversizedFile) { - alert( - `선택한 사진 중 ${oversizedFile.name}의 용량이 제한을 초과했습니다.`, - ); + const oversizedFiles = Array.from(files).filter( + (file) => file.size > MAX_FILE_SIZE, + ); + + if (oversizedFiles.length > 0) { + const fileNames = oversizedFiles.map(f => f.name).join(', '); + alert( + `선택한 사진 중 ${fileNames}의 용량이 제한을 초과했습니다.`, + ); e.target.value = ''; return; }
📜 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/PhotoEditTab/PhotoEditTab.tsx(2 hunks)
🧰 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/PhotoEditTab/PhotoEditTab.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/PhotoEditTab/PhotoEditTab.tsx
frontend/**/*.{ts,tsx}
📄 CodeRabbit inference engine (frontend/.cursorrules)
Use consistent return types for similar functions/hooks
Files:
frontend/src/pages/AdminPage/tabs/PhotoEditTab/PhotoEditTab.tsx
🧠 Learnings (5)
📓 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-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} : Use Component Composition instead of Props Drilling to reduce coupling
Applied to files:
frontend/src/pages/AdminPage/tabs/PhotoEditTab/PhotoEditTab.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/PhotoEditTab/PhotoEditTab.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,js,jsx} : Break down broad state management into smaller, focused hooks/contexts to reduce coupling
Applied to files:
frontend/src/pages/AdminPage/tabs/PhotoEditTab/PhotoEditTab.tsx
📚 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/PhotoEditTab/PhotoEditTab.tsx
🧬 Code graph analysis (1)
frontend/src/pages/AdminPage/tabs/PhotoEditTab/PhotoEditTab.tsx (3)
frontend/src/constants/eventName.ts (1)
ADMIN_EVENT(38-78)frontend/src/constants/uploadLimit.ts (2)
MAX_FILE_COUNT(3-3)MAX_FILE_SIZE(1-1)frontend/src/pages/AdminPage/tabs/PhotoEditTab/components/ImagePreview/ImagePreview.tsx (1)
ImagePreview(10-30)
🔇 Additional comments (6)
frontend/src/pages/AdminPage/tabs/PhotoEditTab/PhotoEditTab.tsx (6)
1-19: LGTM! 임포트 구조가 깔끔합니다.새로운 ContentSection 컴포넌트를 포함하여 필요한 모든 의존성이 올바르게 임포트되었습니다.
20-38: LGTM! 상태 관리와 초기화 로직이 적절합니다.
useEffect를 통해clubDetail.feeds로부터 이미지 목록을 초기화하는 로직이 올바르게 구현되었습니다. 두 개의 뮤테이션 상태를 조합한isLoading처리도 적절합니다.
40-49: LGTM! 파일 업로드 처리 로직이 올바릅니다.
Array.from(files)를 사용하여 FileList를 배열로 변환하고, 기존 이미지 URL과 함께 뮤테이션에 전달하는 로직이 적절합니다.
51-63: LGTM! 업로드 버튼 클릭 핸들러가 잘 구현되었습니다.로딩 상태 체크, 이벤트 트래킹, 최대 파일 개수 검증이 모두 적절하게 처리되었습니다.
65-76: LGTM! 이미지 삭제 로직이 올바르게 구현되었습니다.로컬 상태를 업데이트한 후
newList를 직접 뮤테이션에 전달하여 state 업데이트 타이밍 이슈를 피하는 방식이 적절합니다.
79-137: LGTM! ContentSection 패턴이 올바르게 적용되었습니다.새로운
ContentSection컴포넌트 구조를 사용하여 레이아웃을 일관성 있게 개선했습니다.ContentSection.Header와ContentSection.Body의 사용이 PR 목표에 부합하며, 다른 관리자 페이지 탭들과 일관된 패턴을 따르고 있습니다.
Label 컴포넌트의 margin-bottom 값을 12px에서 8px로 변경하여 UI 일관성 개선
사이드바 레이아웃 변경으로 로고 에디터를 동아리 기본 정보 수정 탭으로 이동하고, 페이지 내 배치에 맞춰 드롭다운 방식에서 버튼 방식으로 UI 개선 - 드롭다운 메뉴 제거 - 이미지 수정/초기화 버튼을 가로 배치 - 안내 텍스트를 버튼 하단에 배치 - ClubInfoEditTab에 ClubLogoEditor 통합
There was a problem hiding this comment.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.tsx (3)
28-29: 사용하지 않는 상태 변수를 제거하세요.
clubPresidentName과telephoneNumber상태 변수가 선언되어 있지만, 리팩토링된 UI에서는 더 이상 사용되지 않습니다. 이러한 미사용 상태는 혼란을 야기하고 불필요한 메모리를 차지합니다.🔎 제안된 수정 사항
const [clubName, setClubName] = useState(''); -const [clubPresidentName, setClubPresidentName] = useState(''); -const [telephoneNumber, setTelephoneNumber] = useState(''); const [introduction, setIntroduction] = useState('');
useEffect와handleUpdateClub에서도 해당 필드 참조를 제거해야 합니다:useEffect(() => { if (clubDetail) { setClubName(clubDetail.name); - setClubPresidentName(clubDetail.presidentName); - setTelephoneNumber(clubDetail.presidentPhoneNumber); setIntroduction(clubDetail.introduction); // ... } }, [clubDetail]);const updatedData = { id: clubDetail.id, name: clubName, category: selectedCategory, division: selectedDivision, tags: clubTags, introduction: introduction, - presidentName: clubPresidentName, - presidentPhoneNumber: telephoneNumber, socialLinks: socialLinks, };
53-54: 사용하지 않는 필드 초기화를 제거하세요.UI에서 제거된
clubPresidentName과telephoneNumber필드에 대한 상태 업데이트가 여전히useEffect에 남아있습니다. 이는 Lines 28-29의 상태 변수 제거와 함께 정리되어야 합니다.
104-105: 업데이트 데이터에서 제거된 필드를 삭제하세요.
presidentName과presidentPhoneNumber가 UI에서 제거되었으므로, 업데이트 요청 데이터에서도 제거해야 합니다. 현재는 빈 문자열이나 이전 값을 전송하게 되어 의도하지 않은 데이터 덮어쓰기가 발생할 수 있습니다.
🧹 Nitpick comments (3)
frontend/src/components/common/InputField/InputField.styles.ts (1)
35-36: 선택적 제안: 중첩 삼항 연산자를 헬퍼 함수로 추출 고려border-color를 결정하는 중첩 삼항 연산자가 다소 복잡합니다. 특히 lines 53-60의 focus 상태 로직은 4단계 분기를 포함하고 있어 가독성이 떨어집니다.
코딩 가이드라인에 따르면 복잡한 중첩 삼항 연산자는 헬퍼 함수로 추출하는 것이 권장됩니다. styled-components 외부에 헬퍼 함수를 정의하여 사용할 수 있습니다.
🔎 헬퍼 함수 추출 예시
+const getBorderColor = (hasError?: boolean, isSuccess?: boolean) => { + if (hasError) return 'red'; + if (isSuccess) return '#28a745'; + return '#c5c5c5'; +}; + +const getFocusBorderColor = ( + readOnly?: boolean, + hasError?: boolean, + isSuccess?: boolean +) => { + if (readOnly) return '#c5c5c5'; + if (hasError) return 'red'; + if (isSuccess) return '#28a745'; + return '#007bff'; +}; export const Input = styled.input<{ hasError?: boolean; readOnly?: boolean; isSuccess?: boolean; }>` /* ... */ border: 1px solid - ${({ hasError, isSuccess }) => - hasError ? 'red' : isSuccess ? '#28a745' : '#c5c5c5'}; + ${({ hasError, isSuccess }) => getBorderColor(hasError, isSuccess)}; /* ... */ :focus { /* ... */ - border-color: ${({ hasError, isSuccess, readOnly }) => - readOnly - ? '#c5c5c5' - : hasError - ? 'red' - : isSuccess - ? '#28a745' - : '#007bff'}; + border-color: ${({ hasError, isSuccess, readOnly }) => + getFocusBorderColor(readOnly, hasError, isSuccess)}; } `;코딩 가이드라인 기준이지만, 현재 로직은 정상 작동하며 이는 기존 코드이므로 우선순위가 낮습니다.
Also applies to: 53-60
frontend/src/pages/AdminPage/tabs/PhotoEditTab/PhotoEditTab.tsx (1)
87-111: 파일 검증 로직 추출을 고려해보세요.onChange 핸들러 내부의 파일 검증 로직이 다소 복잡합니다. 가독성과 재사용성 향상을 위해 별도 함수로 추출하는 것을 고려해보세요.
코딩 가이드라인에 따르면, 복잡한 로직은 전용 함수나 컴포넌트로 추상화하는 것이 권장됩니다.
🔎 제안: 파일 검증 로직 추출
+ /** 파일 선택 시 검증 및 업로드 */ + const handleFileSelect = (e: React.ChangeEvent<HTMLInputElement>) => { + const files = e.target.files; + if (!files || files.length === 0) return; + + const oversizedFile = Array.from(files).find( + (file) => file.size > MAX_FILE_SIZE, + ); + + if (oversizedFile) { + alert( + `선택한 사진 중 ${oversizedFile.name}의 용량이 제한을 초과했습니다.`, + ); + e.target.value = ''; + return; + } + + handleFiles(files); + }; <input ref={inputRef} type='file' accept='image/*' multiple hidden - onChange={(e) => { - const files = e.target.files; - if (!files || files.length === 0) return; - - const oversizedFile = Array.from(files).find( - (file) => file.size > MAX_FILE_SIZE, - ); - - if (oversizedFile) { - alert( - `선택한 사진 중 ${oversizedFile.name}의 용량이 제한을 초과했습니다.`, - ); - e.target.value = ''; - return; - } - - handleFiles(files); - }} + onChange={handleFileSelect} />frontend/src/pages/AdminPage/components/ClubLogoEditor/ClubLogoEditor.styles.ts (1)
3-81: 매직 넘버를 네임드 상수로 교체하세요.스타일 파일 전체에 하드코딩된 수치들(padding, size, color 등)이 많이 있습니다. 코딩 가이드라인에 따라 이러한 매직 넘버들을 네임드 상수나 디자인 토큰으로 교체하면 유지보수성과 일관성이 향상됩니다.
주요 사례:
- 크기 값:
100px,6px,20px,80px(border-radius)- 패딩 값:
4px,10px,20px- 색상 코드:
#ff6b35,#999,#c5c5c5,#ededed- 폰트 사이즈:
1.125rem,12px🔎 상수 추출 예시
스타일 상수 파일을 만들어 재사용 가능한 값들을 정의하세요:
// styles/constants.ts export const LOGO_SIZE = 100; export const LOGO_BORDER_RADIUS = 20; export const BUTTON_BORDER_RADIUS = 80; export const SPACING = { xs: 4, sm: 6, md: 10, lg: 16, xl: 20, }; export const COLORS = { primary: '#ff6b35', gray: '#999', lightGray: '#c5c5c5', background: '#ededed', };그리고 스타일 파일에서 사용:
+import { LOGO_SIZE, LOGO_BORDER_RADIUS, SPACING, COLORS } from '@/styles/constants'; export const ClubLogoWrapper = styled.div` position: relative; - width: 100px; - height: 100px; + width: ${LOGO_SIZE}px; + height: ${LOGO_SIZE}px; `;코딩 가이드라인 기반.
📜 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 (7)
frontend/src/components/common/InputField/InputField.styles.ts(4 hunks)frontend/src/pages/AdminPage/components/ClubLogoEditor/ClubLogoEditor.styles.ts(1 hunks)frontend/src/pages/AdminPage/components/ClubLogoEditor/ClubLogoEditor.tsx(3 hunks)frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.tsx(2 hunks)frontend/src/pages/AdminPage/tabs/PhotoEditTab/PhotoEditTab.styles.ts(2 hunks)frontend/src/pages/AdminPage/tabs/PhotoEditTab/PhotoEditTab.tsx(2 hunks)frontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.styles.ts(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- frontend/src/pages/AdminPage/tabs/PhotoEditTab/PhotoEditTab.styles.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/pages/AdminPage/tabs/PhotoEditTab/PhotoEditTab.tsxfrontend/src/pages/AdminPage/components/ClubLogoEditor/ClubLogoEditor.tsxfrontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.tsxfrontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.styles.tsfrontend/src/pages/AdminPage/components/ClubLogoEditor/ClubLogoEditor.styles.tsfrontend/src/components/common/InputField/InputField.styles.ts
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/PhotoEditTab/PhotoEditTab.tsxfrontend/src/pages/AdminPage/components/ClubLogoEditor/ClubLogoEditor.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/pages/AdminPage/tabs/PhotoEditTab/PhotoEditTab.tsxfrontend/src/pages/AdminPage/components/ClubLogoEditor/ClubLogoEditor.tsxfrontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.tsxfrontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.styles.tsfrontend/src/pages/AdminPage/components/ClubLogoEditor/ClubLogoEditor.styles.tsfrontend/src/components/common/InputField/InputField.styles.ts
🧠 Learnings (10)
📓 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-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} : Use Component Composition instead of Props Drilling to reduce coupling
Applied to files:
frontend/src/pages/AdminPage/tabs/PhotoEditTab/PhotoEditTab.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} : Separate significantly different conditional UI/logic into distinct components
Applied to files:
frontend/src/pages/AdminPage/tabs/PhotoEditTab/PhotoEditTab.tsxfrontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.tsxfrontend/src/pages/AdminPage/components/ClubLogoEditor/ClubLogoEditor.styles.ts
📚 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,js,jsx} : Break down broad state management into smaller, focused hooks/contexts to reduce coupling
Applied to files:
frontend/src/pages/AdminPage/tabs/PhotoEditTab/PhotoEditTab.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/PhotoEditTab/PhotoEditTab.tsxfrontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.tsxfrontend/src/pages/AdminPage/components/ClubLogoEditor/ClubLogoEditor.styles.ts
📚 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/PhotoEditTab/PhotoEditTab.tsxfrontend/src/pages/AdminPage/components/ClubLogoEditor/ClubLogoEditor.tsxfrontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.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/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} : Colocate simple, localized logic or use inline definitions to reduce context switching
Applied to files:
frontend/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} : Choose field-level or form-level cohesion based on form requirements when using form libraries like react-hook-form
Applied to files:
frontend/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/**/*.{ts,tsx,js,jsx} : Replace magic numbers with named constants for clarity
Applied to files:
frontend/src/pages/AdminPage/components/ClubLogoEditor/ClubLogoEditor.styles.ts
🧬 Code graph analysis (2)
frontend/src/pages/AdminPage/components/ClubLogoEditor/ClubLogoEditor.tsx (1)
frontend/src/constants/eventName.ts (1)
ADMIN_EVENT(38-78)
frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.tsx (4)
frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/components/SelectTags/SelectTags.styles.ts (1)
Button(7-21)frontend/src/constants/eventName.ts (1)
ADMIN_EVENT(38-78)frontend/src/constants/snsConfig.ts (1)
SNS_CONFIG(5-26)frontend/src/types/club.ts (1)
SNSPlatform(14-14)
🔇 Additional comments (10)
frontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.styles.ts (4)
3-7: 레이아웃 리팩토링이 잘 적용되었습니다.Container로 이름 변경과 60px gap 설정이 새로운 ContentSection 기반 레이아웃 구조와 잘 맞습니다. 60px gap이 다른 관리자 탭들과 일관되게 적용되었는지 확인해 주세요.
9-13: Flexbox 레이아웃 개선이 적절합니다.
display: flex와gap: 16px추가로 요소 간 간격 처리가 깔끔해졌습니다.
15-34: 코드 품질 개선이 잘 되었습니다.패딩의 불필요한 공백 제거와 transition 속성의 멀티라인 포맷팅으로 가독성이 향상되었습니다.
36-40: 레이블 간격 조정이 적절합니다.
margin-bottom을 8px로 조정하여 전체 관리자 탭의 스타일 일관성을 확보했습니다.frontend/src/components/common/InputField/InputField.styles.ts (1)
16-16: 디자인 일관성을 위한 간격 조정 승인라벨과 입력 필드 사이의 간격이 12px에서 8px로 줄어들어 더 타이트한 레이아웃을 제공합니다. 이는 PR의 관리자 페이지 UI 통일 목표와 일치합니다.
frontend/src/pages/AdminPage/tabs/PhotoEditTab/PhotoEditTab.tsx (2)
79-138: ContentSection 패턴 적용 잘 되었습니다.관리자 페이지 전체에 일관된 레이아웃을 적용하기 위한 ContentSection 마이그레이션이 적절하게 이루어졌습니다. Header와 Body의 분리가 명확하고, 기존 기능들이 새로운 구조 안에서 잘 유지되고 있습니다.
72-75: updateFeed 호출 방식이 올바르게 구현되었습니다.
FeedUpdateParams인터페이스(useFeedMutation.ts)가{ clubId: string; urls: string[] }형태를 명시적으로 지원하며,useUpdateFeed훅의mutationFn이 이 형식을 정확히 수용합니다. 현재 PhotoEditTab.tsx의 호출 방식과 완벽하게 일치합니다.Likely an incorrect or invalid review comment.
frontend/src/pages/AdminPage/components/ClubLogoEditor/ClubLogoEditor.tsx (1)
1-97: 메뉴 기반 UI에서 인라인 버튼 UI로의 리팩토링이 잘 되었습니다.이 변경사항은 다음과 같은 개선을 제공합니다:
- 불필요한 상태 관리 제거 (isMenuOpen, 외부 클릭 처리 등)
- UI 단순화로 사용자 경험 개선
- 이벤트 트래킹 적절히 추가 (CLUB_LOGO_UPLOAD_BUTTON_CLICKED, CLUB_LOGO_RESET_BUTTON_CLICKED)
- 파일 크기 검증 및 조건부 렌더링 로직 유지
전체적으로 코드가 더 읽기 쉽고 유지보수하기 좋아졌습니다.
frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.tsx (2)
123-180: ContentSection 패턴을 사용한 레이아웃 리팩토링이 우수합니다.컴파운드 컴포넌트 패턴으로 ContentSection을 도입하여 다음과 같은 이점을 얻었습니다:
- 일관된 레이아웃 구조
- ClubLogoEditor 통합으로 모듈화 향상
- Header와 Body의 명확한 역할 분리
- PR 목표인 관리자 페이지 전체의 레이아웃 통일성 확보
특히 ClubLogoEditor를 별도 컴포넌트로 분리하여 재사용성이 높아졌습니다.
186-216: SNS 입력 필드 매핑 방식이 훌륭합니다.
SNS_CONFIG를 순회하며 동적으로 입력 필드를 생성하는 방식은 다음과 같은 장점이 있습니다:
- 코드 중복 제거
- 새로운 SNS 플랫폼 추가 시 확장성 우수
- 각 플랫폼별 검증 로직 일관성 유지
- 이벤트 트래킹도 플랫폼별로 적절히 구분
유지보수성이 크게 개선되었습니다.
- background-url 방식에서 img 태그 방식으로 변경
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/components/MakeTags/MakeTags.styles.ts (1)
38-39: 매직 넘버를 명명된 상수로 추출하는 것을 권장합니다.현재 여러 픽셀 값(
100px,30px,8px,20px,16px)이 하드코딩되어 있습니다. 디자인 토큰이나 테마 값으로 추출하면 유지보수성이 향상됩니다.🔎 제안된 리팩토링
파일 상단에 상수 정의:
+const TAG_INPUT_WIDTH = '100px'; +const TAG_INPUT_PADDING_RIGHT = '30px'; +const REMOVE_BUTTON_SIZE = 20; +const REMOVE_BUTTON_ICON_SIZE = 16; +const REMOVE_BUTTON_OFFSET = 8;styled 컴포넌트에 적용:
export const TagTextInput = styled.input` border: none; outline: none; background: transparent; font-size: 0.875rem; color: #4b4b4b; - width: 100px; - padding-right: 30px; + width: ${TAG_INPUT_WIDTH}; + padding-right: ${TAG_INPUT_PADDING_RIGHT}; `; export const RemoveButton = styled.button` position: absolute; top: 50%; - right: 8px; + right: ${REMOVE_BUTTON_OFFSET}px; transform: translateY(-50%); - width: 20px; - height: 20px; + width: ${REMOVE_BUTTON_SIZE}px; + height: ${REMOVE_BUTTON_SIZE}px; padding: 0; background: transparent; border: none; cursor: pointer; display: flex; align-items: center; justify-content: center; img { - width: 16px; - height: 16px; + width: ${REMOVE_BUTTON_ICON_SIZE}px; + height: ${REMOVE_BUTTON_ICON_SIZE}px; opacity: 0.5; } &:hover img { opacity: 1; } `;코딩 가이드라인 기준.
Also applies to: 45-45, 47-65
📜 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 (2)
frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/components/MakeTags/MakeTags.styles.ts(1 hunks)frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/components/MakeTags/MakeTags.tsx(3 hunks)
🧰 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/ClubInfoEditTab/components/MakeTags/MakeTags.tsxfrontend/src/pages/AdminPage/tabs/ClubInfoEditTab/components/MakeTags/MakeTags.styles.ts
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/ClubInfoEditTab/components/MakeTags/MakeTags.tsx
frontend/**/*.{ts,tsx}
📄 CodeRabbit inference engine (frontend/.cursorrules)
Use consistent return types for similar functions/hooks
Files:
frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/components/MakeTags/MakeTags.tsxfrontend/src/pages/AdminPage/tabs/ClubInfoEditTab/components/MakeTags/MakeTags.styles.ts
🧠 Learnings (1)
📓 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')는 로그인 기능 구현 후 동적으로 가져오는 방식으로 수정될 예정입니다.
🔇 Additional comments (2)
frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/components/MakeTags/MakeTags.tsx (2)
52-53: 접근성 개선이 잘 적용되었습니다!각 태그 입력 필드에
placeholder와aria-label을 추가하여 사용자 경험과 접근성이 향상되었습니다.
56-62: 버튼 접근성 및 시맨틱이 올바르게 구현되었습니다!다음 개선사항들이 잘 적용되었습니다:
aria-label로 스크린 리더 지원 강화type="button"으로 폼 제출 방지- 아이콘을
img요소로 명시적 렌더링 (빈alt는 버튼 자체에 레이블이 있으므로 적절함)
| export const ContentSection = Object.assign(ContentSectionRoot, { | ||
| Header: ContentSectionHeader, | ||
| Body: ContentSectionBody, | ||
| }); |
There was a problem hiding this comment.
컴파운드 패턴을 적용하셨군요. 선언적인 느낌이 강하고 가져다 쓸 때 가독성이 좋을 것 같네요.
ContentSection 의 확장성을 고려하신 설계일까요?
There was a problem hiding this comment.
확장성을 특별히 고려하지는 않았지만, 모든 탭에서 반복적으로 사용되는 섹션 단위 레이아웃이 필요했습니다!
일부 섹션은 Body가 필요 없는 경우도 있어 Header와 Body를 선택적으로 사용할 수 있도록 컴파운드 패턴을 적용했습니다.
이를 통해 선언적으로 컴포넌트를 사용하면서 필요한 요소만 선택해 쓸 수 있습니다.
frontend/src/pages/AdminPage/tabs/AccountEditTab/AccountEditTab.styles.ts
Show resolved
Hide resolved
| <div style={{ marginBottom: '32px' }}> | ||
| <ContentSection.Header title='지원 현황' /> |
There was a problem hiding this comment.
인지하고 있었지만, 해당 탭의 스타일이 바뀌어야 해서 임시로 보기 좋게 인라인 스타일을 적용했습니다!
이후 변경사항에 맞춰서 추후에 수정하면 좋을 것 같습니다.
frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/components/MakeTags/MakeTags.tsx
Show resolved
Hide resolved
| onChange={(e) => { | ||
| const files = e.target.files; | ||
| if (!files || files.length === 0) return; | ||
|
|
||
| const oversizedFile = Array.from(files).find( | ||
| (file) => file.size > MAX_FILE_SIZE, |
There was a problem hiding this comment.
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 (2)
frontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.tsx (1)
104-137: 날짜 유효성 검증 추가 고려상시모집이 아닌 경우 모집 시작일이 종료일보다 앞서는지 검증하는 로직이 없습니다. 사용자가 유효하지 않은 날짜 범위를 저장할 수 있습니다.
🔎 제안된 검증 로직
const handleUpdateClub = async () => { trackEvent(ADMIN_EVENT.UPDATE_RECRUIT_BUTTON_CLICKED); if (!clubDetail) return; let startForSave: Date | null = recruitmentStart; let endForSave: Date | null = recruitmentEnd; if (always) { const base = recruitmentStart ?? new Date(); startForSave = base; endForSave = setYear(base, FAR_FUTURE_YEAR); + } else { + // 상시모집이 아닌 경우 날짜 유효성 검증 + if (startForSave && endForSave && startForSave > endForSave) { + alert('모집 시작일은 종료일보다 앞서야 합니다.'); + return; + } } const updatedData = {frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.tsx (1)
28-30: UI와 제출 로직 간 불일치로 인한 데이터 무결성 문제
clubPresidentName(29번째 줄)과telephoneNumber(30번째 줄) 상태 변수가 선언되고 초기화되며(54-55번째 줄),handleUpdateClub에서 백엔드로 제출되고 있지만(105-106번째 줄), UI에는 해당 입력 필드가 존재하지 않습니다. AI 요약에 따르면 이러한 입력이 의도적으로 제거되었지만, 제출 로직은 업데이트되지 않았습니다.이로 인해 다음과 같은 문제가 발생합니다:
- 사용자가 회장 이름이나 전화번호를 수정할 수 없음
- 초기 값(또는 이전 값)이 사용자 의도와 무관하게 항상 제출됨
- 데이터 무결성 문제 발생 가능
해결 방법:
- 이 필드들이 여전히 필요한 경우 UI에 입력 필드를 다시 추가
- 더 이상 필요하지 않은 경우 상태 변수와 제출 로직에서 완전히 제거
🔎 필드가 더 이상 필요하지 않은 경우의 수정 제안
const [clubName, setClubName] = useState(''); - const [clubPresidentName, setClubPresidentName] = useState(''); - const [telephoneNumber, setTelephoneNumber] = useState(''); const [introduction, setIntroduction] = useState('');useEffect(() => { if (clubDetail) { setClubName(clubDetail.name); - setClubPresidentName(clubDetail.presidentName); - setTelephoneNumber(clubDetail.presidentPhoneNumber); setIntroduction(clubDetail.introduction); // ... } }, [clubDetail]);const updatedData = { id: clubDetail.id, name: clubName, category: selectedCategory, division: selectedDivision, tags: clubTags, introduction: introduction, - presidentName: clubPresidentName, - presidentPhoneNumber: telephoneNumber, socialLinks: socialLinks, };Also applies to: 98-108
♻️ Duplicate comments (1)
frontend/src/pages/AdminPage/tabs/PhotoEditTab/PhotoEditTab.tsx (1)
89-106: 복잡한 로직을 별도 함수로 추출하세요.onChange 핸들러 내부의 파일 검증 로직이 복잡합니다. 이전 리뷰에서도 함수 분리가 제안되었으며, 코딩 가이드라인에 따라 복잡한 로직은 별도 함수로 추상화해야 합니다.
🔎 제안: 파일 검증 로직 추출
+ const handleFileInputChange = (e: React.ChangeEvent<HTMLInputElement>) => { + const files = e.target.files; + if (!files || files.length === 0) return; + + const oversizedFile = Array.from(files).find( + (file) => file.size > MAX_FILE_SIZE, + ); + + if (oversizedFile) { + alert( + `선택한 사진 중 ${oversizedFile.name}의 용량이 제한을 초과했습니다.`, + ); + e.target.value = ''; + return; + } + + handleFiles(files); + }; <input ref={inputRef} type='file' accept='image/*' multiple hidden - onChange={(e) => { - const files = e.target.files; - if (!files || files.length === 0) return; - - const oversizedFile = Array.from(files).find( - (file) => file.size > MAX_FILE_SIZE, - ); - - if (oversizedFile) { - alert( - `선택한 사진 중 ${oversizedFile.name}의 용량이 제한을 초과했습니다.`, - ); - e.target.value = ''; - return; - } - - handleFiles(files); - }} + onChange={handleFileInputChange} />Based on learnings, 이전 리뷰에서도 동일한 부분(lines 89-94)에 대해 함수 분리가 요청되었습니다.
🧹 Nitpick comments (5)
frontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.tsx (2)
18-18: 상수 문서화 권장
FAR_FUTURE_YEAR상수의 의도(상시모집을 나타내기 위한 특수 연도 값)를 설명하는 주석을 추가하는 것이 좋습니다.🔎 제안된 개선 사항
+// 상시모집 상태를 나타내기 위해 사용하는 특수 연도 값 const FAR_FUTURE_YEAR = 2999;
128-134: 사용자 피드백 개선 고려네이티브
alert()를 사용하고 있습니다. 토스트 알림이나 모달 컴포넌트를 사용하면 더 나은 사용자 경험을 제공할 수 있습니다.frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.tsx (3)
129-129: 매직 넘버를 상수로 추출하여 명확성 향상버튼 너비 값
'150px'가 하드코딩되어 있습니다. 코딩 가이드라인에 따라 이러한 값을 명명된 상수로 추출하면 유지보수성과 일관성이 향상됩니다.🔎 제안하는 리팩토링
파일 상단에 상수를 정의하세요:
const BUTTON_WIDTHS = { ACTION_BUTTON: '150px', } as const;그런 다음 적용:
- <Button width={'150px'} animated onClick={handleUpdateClub}> + <Button width={BUTTON_WIDTHS.ACTION_BUTTON} animated onClick={handleUpdateClub}>코딩 가이드라인 기반: "Replace magic numbers with named constants for clarity"
137-149: 매직 넘버를 명명된 상수로 추출입력 필드에 여러 매직 값이 하드코딩되어 있습니다:
- 146번째 줄:
width='40%'- 147번째 줄:
maxLength={20}이러한 값은 파일 전체에서 반복되며(예: 155번째 줄에도
maxLength={20}), 명명된 상수로 추출하면 유지보수성이 향상됩니다.🔎 제안하는 리팩토링
컴포넌트 상단에 상수를 정의:
const INPUT_CONFIG = { CLUB_NAME_WIDTH: '40%', CLUB_NAME_MAX_LENGTH: 20, INTRODUCTION_MAX_LENGTH: 20, } as const;적용:
<InputField label='동아리명' placeholder='동아리명' value={clubName} onChange={(e) => setClubName(e.target.value)} onClear={() => { trackEvent(ADMIN_EVENT.CLUB_NAME_CLEAR_BUTTON_CLICKED); setClubName(''); }} - width='40%' + width={INPUT_CONFIG.CLUB_NAME_WIDTH} - maxLength={20} + maxLength={INPUT_CONFIG.CLUB_NAME_MAX_LENGTH} showMaxChar={true} />코딩 가이드라인 기반: "Replace magic numbers with named constants for clarity"
187-190: 타입 캐스팅 개선으로 타입 안정성 향상189번째 줄의
rawKey as SNSPlatform타입 캐스팅은 작동하지만, TypeScript의 타입 추론을 더 잘 활용할 수 있습니다.🔎 제안하는 리팩토링
Object.entries를 타입이 지정된 헬퍼 함수로 래핑:// 파일 상단 또는 utils에 추가 const getTypedEntries = <T extends Record<string, unknown>>(obj: T) => Object.entries(obj) as Array<[keyof T, T[keyof T]]>;적용:
- {Object.entries(SNS_CONFIG).map( - ([rawKey, { label, placeholder }]) => { - const key = rawKey as SNSPlatform; - + {getTypedEntries(SNS_CONFIG).map( + ([key, { label, placeholder }]) => { return (이렇게 하면 타입 캐스팅 없이
key가 자동으로SNSPlatform으로 추론됩니다.
📜 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 (3)
frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.tsx(2 hunks)frontend/src/pages/AdminPage/tabs/PhotoEditTab/PhotoEditTab.tsx(3 hunks)frontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.tsx(3 hunks)
🧰 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/PhotoEditTab/PhotoEditTab.tsxfrontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.tsxfrontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.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/PhotoEditTab/PhotoEditTab.tsxfrontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.tsxfrontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.tsx
frontend/**/*.{ts,tsx}
📄 CodeRabbit inference engine (frontend/.cursorrules)
Use consistent return types for similar functions/hooks
Files:
frontend/src/pages/AdminPage/tabs/PhotoEditTab/PhotoEditTab.tsxfrontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.tsxfrontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.tsx
🧠 Learnings (7)
📓 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-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} : Separate significantly different conditional UI/logic into distinct components
Applied to files:
frontend/src/pages/AdminPage/tabs/PhotoEditTab/PhotoEditTab.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/PhotoEditTab/PhotoEditTab.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/**/*.{ts,tsx,js,jsx} : Break down broad state management into smaller, focused hooks/contexts to reduce coupling
Applied to files:
frontend/src/pages/AdminPage/tabs/PhotoEditTab/PhotoEditTab.tsx
📚 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/ClubInfoEditTab/ClubInfoEditTab.tsxfrontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.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/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} : Colocate simple, localized logic or use inline definitions to reduce context switching
Applied to files:
frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.tsxfrontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.tsx
🧬 Code graph analysis (2)
frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.tsx (4)
frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/components/SelectTags/SelectTags.styles.ts (1)
Button(7-21)frontend/src/constants/eventName.ts (1)
ADMIN_EVENT(38-78)frontend/src/constants/snsConfig.ts (1)
SNS_CONFIG(5-26)frontend/src/types/club.ts (1)
SNSPlatform(14-14)
frontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.tsx (1)
frontend/src/constants/eventName.ts (1)
ADMIN_EVENT(38-78)
🔇 Additional comments (8)
frontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.tsx (3)
1-1: React 19 자동 JSX 런타임 적용 확인명시적 React import를 제거하고 필요한 훅만 named import로 가져오는 것은 React 19의 자동 JSX 런타임과 일치하며 적절합니다.
74-102: 복잡한 토글 로직 확인 완료
toggleAlways함수의 로직이 복잡하지만, 상시모집 전환 시 이전 날짜 범위를 백업/복원하는 요구사항을 적절히 처리하고 있습니다. 백업 자체가 상시모집인 경우의 엣지 케이스도 고려되었습니다.
139-194: ContentSection 패턴 적용 확인새로운
ContentSection컴파운드 컴포넌트 패턴으로의 리팩토링이 적절하게 수행되었습니다:
- Header에 제목과 액션 버튼이 명확히 분리됨
- Body에 모든 폼 필드가 구조화되어 배치됨
- 기존 기능(날짜 선택, 상시모집 토글, 이벤트 트래킹)이 모두 유지됨
- 접근성(aria-pressed) 속성도 적절히 유지됨
버튼 텍스트를 "수정하기"에서 "저장하기"로 변경한 것도 저장 액션의 의미를 더 명확하게 전달합니다.
frontend/src/pages/AdminPage/tabs/PhotoEditTab/PhotoEditTab.tsx (4)
1-18: LGTM!import 구조가 명확하고 ContentSection 컴포넌트 추가가 적절합니다. 상수(MAX_FILE_SIZE, MAX_FILE_COUNT)를 사용하여 매직 넘버를 피한 점도 좋습니다.
49-60: LGTM!업로드 버튼 클릭 핸들러가 명확하며, 로딩 상태 확인, 이벤트 트래킹, 파일 개수 검증이 적절히 구현되어 있습니다.
74-136: ContentSection 구조가 적절하게 적용되었습니다.ContentSection.Header와 ContentSection.Body를 사용한 레이아웃 구조가 PR 목표에 맞게 잘 구현되었습니다. 이미지 그리드와 업로드 버튼 배치도 명확합니다.
39-47: 에러 처리가 이미 mutation hook에 구현되어 있습니다.
useUploadFeed()hook 정의를 확인한 결과, 이미onError콜백이 포함되어 있습니다 (useFeedMutation.ts 76-78줄). 업로드 실패 시 사용자에게 "이미지 업로드에 실패했어요. 다시 시도해주세요!"라는 알림이 자동으로 표시됩니다. 추가 에러 처리는 필요하지 않습니다.Likely an incorrect or invalid review comment.
frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.tsx (1)
183-218: SNS 섹션 구현이 잘 구조화되어 있습니다새로운 ContentSection을 사용한 SNS 링크 관리 구현이 우수합니다:
- SNS_CONFIG를 활용한 동적 렌더링으로 유지보수성 향상
- 에러 상태와 헬퍼 텍스트가 적절히 바인딩됨
- 이벤트 트래킹에 플랫폼별 메타데이터 포함 (204번째 줄)
- Clear 핸들러가 값과 에러 상태를 모두 초기화 (207-208번째 줄)
ContentSection 패턴의 일관성 있는 적용이 PR 목표와 잘 부합합니다.
- 인라인 onChange 핸들러를 handleFileChange 함수로 추출 - 파일 크기 검증 로직의 가독성 개선
…//github.com/Moadong/moadong into feature/#941-admin-ui-sidebar-layout-MOA-429
- 인라인 onChange 핸들러를 handleFileChange 함수로 추출 - 파일 크기 검증 로직의 가독성 개선 - JSX 코드의 복잡도 감소
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
frontend/src/pages/AdminPage/tabs/PhotoEditTab/PhotoEditTab.tsx (1)
86-96: 이전 리뷰에서 지적된 에러 핸들링 누락 문제가 여전히 남아있습니다.
updateFeedmutation 실패 시 사용자 피드백 및 상태 롤백이 여전히 구현되지 않았습니다. 이전 리뷰 코멘트를 참고하여onError콜백을 추가해주세요.
📜 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/PhotoEditTab/PhotoEditTab.tsx(3 hunks)
🧰 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/PhotoEditTab/PhotoEditTab.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/PhotoEditTab/PhotoEditTab.tsx
frontend/**/*.{ts,tsx}
📄 CodeRabbit inference engine (frontend/.cursorrules)
Use consistent return types for similar functions/hooks
Files:
frontend/src/pages/AdminPage/tabs/PhotoEditTab/PhotoEditTab.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-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/PhotoEditTab/PhotoEditTab.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,js,jsx} : Break down broad state management into smaller, focused hooks/contexts to reduce coupling
Applied to files:
frontend/src/pages/AdminPage/tabs/PhotoEditTab/PhotoEditTab.tsx
🪛 Biome (2.1.2)
frontend/src/pages/AdminPage/tabs/PhotoEditTab/PhotoEditTab.tsx
[error] 64-64: Expected a statement but instead found '<<<<<<< HEAD'.
Expected a statement here.
(parse)
[error] 86-89: Expected a statement but instead found '=======
1b1874a'.
Expected a statement here.
(parse)
[error] 87-89: numbers cannot be followed by identifiers directly after
an identifier cannot appear here
(parse)
🔇 Additional comments (2)
frontend/src/pages/AdminPage/tabs/PhotoEditTab/PhotoEditTab.tsx (2)
63-81: 파일 크기 검증 로직이 적절합니다.업로드 전에 파일 크기를 검증하고, 초과 시 사용자에게 구체적인 파일명과 함께 알림을 제공하는 구현이 좋습니다. 입력 필드 초기화도 올바르게 처리되었습니다.
98-142: ContentSection 리팩토링이 적절합니다.
ContentSection컴파운드 패턴을 활용한 레이아웃 구조화가 PR 목표와 일치하며, 관리자 탭 전반의 UI 일관성을 개선합니다. 기존 기능은 모두 유지되면서 구조만 개선되었습니다.
#️⃣연관된 이슈
📝작업 내용
배경
0. 관리자 페이지 레이아웃 리팩토링
변경 이유: 관리자 페이지만 배경색이 달라지면서 기존
PageContainer로는 대응 불가변경 내용:
PageContainer사용 XAdminBackground,AdminLayout으로 구조 변경하여 관리자 페이지 전용 배경색 적용일반 페이지와 동일한 배경색
관리자 전용 배경색 적용
1. 관리자 사이드바 UI 개선
변경 이유:
변경 내용:
종속적인 구조
독립적이고 재사용 가능한 컴포넌트
2. 로고 에디터 UI 개선 및 ClubInfoEditTab 통합
변경 이유:
변경 내용:
CLUB_LOGO_EDIT_BUTTON_CLICKED이벤트를CLUB_LOGO_UPLOAD_BUTTON_CLICKED로 통합드롭다운 메뉴 방식
버튼 가로 배치 + 하단 안내 텍스트
3. ContentSection 컴포넌트 도입
도입 이유:
변경 내용:
ContentSection,ContentSection.Header,ContentSection.Body4. 전체적인 레이아웃 통일 및 스타일 정리
🎯 중점적으로 리뷰받고 싶은 부분
🫡 참고사항
ContentSection.Header로 통일하여 유지보수성 향상Summary by CodeRabbit
새로운 기능
UI 변경
사용성 개선
✏️ Tip: You can customize this high-level summary in your review settings.