[feature] 관리자 페이지 클럽 로고 수정 및 초기화 UI/UX 수정#342
Conversation
Walkthrough이번 변경 사항에서는 관리자 페이지의 클럽 로고 편집 UI/UX가 개선되었습니다. 기존에 사이드바(SideBar)에서 직접 처리하던 로고 업로드 및 삭제 로직과 관련 스타일 컴포넌트가 제거되고, 별도의 Changes
Sequence Diagram(s)sequenceDiagram
participant Admin as 관리자
participant SideBar as SideBar
participant ClubLogoEditor as ClubLogoEditor
participant Server as 서버
Admin->>SideBar: 관리자 페이지 접속
SideBar->>ClubLogoEditor: 클럽 로고 정보 전달
Admin->>ClubLogoEditor: 로고 편집 버튼 클릭
ClubLogoEditor->>Admin: 드롭다운 메뉴 표시
Admin->>ClubLogoEditor: 새 로고 업로드 선택
ClubLogoEditor->>Server: 로고 이미지 업로드 요청
Server-->>ClubLogoEditor: 업로드 결과 반환
ClubLogoEditor->>Admin: 로고 변경 결과 표시
Admin->>ClubLogoEditor: 로고 초기화 선택
ClubLogoEditor->>Admin: 초기화 확인 모달 표시
Admin->>ClubLogoEditor: 초기화 확정
ClubLogoEditor->>Server: 로고 삭제 요청
Server-->>ClubLogoEditor: 삭제 결과 반환
ClubLogoEditor->>Admin: 기본 로고로 변경 표시
Assessment against linked issues
✨ 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:
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 (
|
✅ Deploy Preview for moadong ready!
To edit notification comments on pull requests, go to your Netlify site configuration. |
There was a problem hiding this comment.
Pull Request Overview
This PR refactors the club logo functionality in the admin page by extracting and enhancing the logo editing and reset logic, while also updating some assets and related UI components.
- Extracts club logo editing functionality from SideBar into a dedicated ClubLogoEditor component.
- Refactors UI and error messaging for logo upload and reset actions.
- Updates asset references (e.g., pencil icon) in the MarkdownEditor component.
Reviewed Changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| frontend/src/pages/AdminPage/components/SideBar/SideBar.tsx | Removed inline logo editing code and integrated the new ClubLogoEditor component. |
| frontend/src/pages/AdminPage/components/SideBar/SideBar.styles.ts | Removed styling for the removed inline club logo UI components. |
| frontend/src/pages/AdminPage/components/MarkdownEditor/MarkdownEditor.tsx | Updated the pencil icon asset reference. |
| frontend/src/pages/AdminPage/components/ClubLogoEditor/ClubLogoEditor.tsx | Introduced a dedicated component to handle club logo upload and reset with updated UI/UX messaging. |
| frontend/src/pages/AdminPage/components/ClubLogoEditor/ClubLogoEditor.styles.ts | Added styling for the new ClubLogoEditor component. |
Comments suppressed due to low confidence (3)
frontend/src/pages/AdminPage/components/ClubLogoEditor/ClubLogoEditor.tsx:41
- [nitpick] The alert message for exceeding the file size limit has been updated compared to the previous version; please verify that this friendlier tone aligns with the overall UI guidelines.
if (file.size > MAX_FILE_SIZE) {
frontend/src/pages/AdminPage/components/ClubLogoEditor/ClubLogoEditor.tsx:62
- [nitpick] Consider using a custom confirmation modal instead of window.confirm for a more consistent UI experience across the admin page.
if (!window.confirm('정말 로고를 기본 이미지로 되돌릴까요?')) return;
frontend/src/pages/AdminPage/components/MarkdownEditor/MarkdownEditor.tsx:6
- [nitpick] Please confirm that the updated asset 'pencil_icon_1.svg' is the intended replacement for 'pencil_icon.svg' as per the recent design updates.
import pencil_icon from '@/assets/images/icons/pencil_icon_1.svg';
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (6)
frontend/src/pages/AdminPage/components/ClubLogoEditor/ClubLogoEditor.tsx (3)
19-32: 컴포넌트 초기 설정 및 상태 관리컴포넌트가 clubId가 없을 경우 null을 반환하는 것은 효율적인 접근법입니다. 또한 기본 로고 처리와 상태 관리가 잘 구현되어 있습니다.
한 가지 개선점은
isClubLogoEmpty로직에 더 많은 예외 처리를 추가하는 것입니다. 현재는 빈 문자열과 null을 처리하지만, 잘못된 URL이나 존재하지 않는 이미지에 대한 처리는 포함되어 있지 않습니다.-const isClubLogoEmpty = !clubLogo || clubLogo.trim() === ''; +const isClubLogoEmpty = !clubLogo || clubLogo.trim() === '' || clubLogo.includes('default_profile_image');
56-68: 로고 초기화 처리로고 초기화 함수는 이미 기본 로고인 경우와 사용자 확인을 위한 조건을 잘 처리하고 있습니다. 오류 메시지도 사용자 친화적입니다.
그러나 오류 발생 시 메뉴를 닫는 로직이 누락되어 있습니다. 사용자가 오류 메시지를 확인한 후에도 메뉴가 열려 있을 수 있습니다.
deleteMutation.mutate(undefined, { onError: () => alert('로고 초기화에 실패했어요. 잠시 후 다시 시도해 주세요.'), + onSettled: () => setIsMenuOpen(false), });
86-128: 컴포넌트 렌더링 로직전체적인 UI 구조가 잘 구성되어 있으며, 사용자 경험을 고려한 요소들(아이콘 변경, 모달 메뉴 등)이 잘 구현되어 있습니다.
메뉴 아이템 클릭 후 메뉴를 닫는 로직(setIsMenuOpen(false))이 각 항목에 반복되고 있습니다. 이런 중복을 피하기 위해 공통 핸들러 함수를 만드는 것이 좋을 수 있습니다.
+const handleMenuItemClick = (action: () => void) => { + action(); + setIsMenuOpen(false); +}; {isMenuOpen && ( <Styled.EditMenu ref={menuRef}> <Styled.EditMenuItem onClick={() => { - triggerFileInput(); - setIsMenuOpen(false); + handleMenuItemClick(triggerFileInput); }}> <img src={editIcon} alt='사진 수정 아이콘' /> 사진 수정하기 </Styled.EditMenuItem> <Styled.Divider /> <Styled.EditMenuItem onClick={() => { - handleLogoReset(); - setIsMenuOpen(false); + handleMenuItemClick(handleLogoReset); }}> <img src={deleteIcon} alt='초기화 아이콘' /> 초기화하기 </Styled.EditMenuItem> </Styled.EditMenu> )}frontend/src/pages/AdminPage/components/ClubLogoEditor/ClubLogoEditor.styles.ts (3)
17-36: 편집 버튼 스타일편집 버튼의 스타일이 깔끔하게 정의되어 있습니다. 절대 위치를 사용하여 로고 이미지 위에 버튼을 배치한 것이 좋습니다.
접근성을 개선하기 위해 호버 상태에 대한 스타일을 추가하는 것이 좋습니다.
export const EditButton = styled.button` position: absolute; bottom: 8px; right: 8px; width: 32px; height: 32px; background: transparent; border: none; outline: none; border-radius: 50%; display: flex; align-items: center; justify-content: center; cursor: pointer; + transition: background-color 0.2s; + + &:hover, &:focus { + background-color: rgba(0, 0, 0, 0.1); + } img { width: 24px; height: 24px; } `;
38-50: 편집 메뉴 스타일메뉴의 위치와 스타일이 잘 정의되어 있습니다. z-index를 사용하여 다른 요소 위에 표시되도록 한 것이 좋습니다.
다양한 화면 크기에서 메뉴가 잘리지 않도록 미디어 쿼리를 추가하는 것이 좋을 수 있습니다.
export const EditMenu = styled.div` position: absolute; left: 100%; transform: translateY(-50%); margin-left: 8px; background: #fff; box-shadow: 0 4px 20px 0 rgba(0, 0, 0, 0.4); border-radius: 18px; overflow: hidden; min-width: 160px; z-index: 2; + + @media (max-width: 768px) { + left: auto; + right: 0; + transform: translateY(0); + margin-top: 8px; + margin-left: 0; + } `;
51-73: 메뉴 아이템 스타일메뉴 아이템 스타일이 잘 정의되어 있습니다. 호버 상태도 잘 처리되어 있습니다.
접근성을 개선하기 위해 focus 상태에 대한 스타일을 추가하는 것이 좋습니다.
export const EditMenuItem = styled.button` width: 100%; display: flex; align-items: center; gap: 10px; padding: 12px 16px; background: white; border: none; font-size: 16px; color: #333; cursor: pointer; transition: background-color 0.2s; - &:hover { + &:hover, &:focus { background-color: #f1f1f1; } img { width: 15px; height: 15px; } `;
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (4)
frontend/src/assets/images/icons/cancel_button_icon.svgis excluded by!**/*.svgfrontend/src/assets/images/icons/change_image_button_icon_hover.svgis excluded by!**/*.svgfrontend/src/assets/images/icons/pencil_icon_1.svgis excluded by!**/*.svgfrontend/src/assets/images/icons/pencil_icon_2.svgis excluded by!**/*.svg
📒 Files selected for processing (5)
frontend/src/pages/AdminPage/components/ClubLogoEditor/ClubLogoEditor.styles.ts(1 hunks)frontend/src/pages/AdminPage/components/ClubLogoEditor/ClubLogoEditor.tsx(1 hunks)frontend/src/pages/AdminPage/components/MarkdownEditor/MarkdownEditor.tsx(1 hunks)frontend/src/pages/AdminPage/components/SideBar/SideBar.styles.ts(0 hunks)frontend/src/pages/AdminPage/components/SideBar/SideBar.tsx(2 hunks)
💤 Files with no reviewable changes (1)
- frontend/src/pages/AdminPage/components/SideBar/SideBar.styles.ts
🔇 Additional comments (12)
frontend/src/pages/AdminPage/components/MarkdownEditor/MarkdownEditor.tsx (1)
6-6: 아이콘 경로 업데이트연필 아이콘 파일 경로가
pencil_icon.svg에서pencil_icon_1.svg로 변경되었습니다. 이는 PR에서 언급된 UI 개선의 일부로 보입니다.frontend/src/pages/AdminPage/components/SideBar/SideBar.tsx (2)
4-4: 구성 요소 분리와 관심사 분리
ClubLogoEditor컴포넌트를 import하여 로고 편집 기능을 별도의 컴포넌트로 분리한 것은 좋은 결정입니다. 이렇게 하면 각 컴포넌트가 단일 책임을 가지게 되어 코드 유지보수성이 향상됩니다.
55-55: 기존 로고 편집 UI를 ClubLogoEditor로 대체기존의 복잡한 로고 편집 코드가 단일 컴포넌트로 교체되어 SideBar 컴포넌트가 더 간결해졌습니다. 현재 구현은 clubLogo 속성만 전달하고 있어 매우 깔끔합니다.
frontend/src/pages/AdminPage/components/ClubLogoEditor/ClubLogoEditor.tsx (5)
1-14: 필요한 의존성 및 자원 가져오기필요한 모든 라이브러리, 컴포넌트, 아이콘 및 커스텀 훅을 적절하게 가져왔습니다. 특히
useUploadClubLogo와useDeleteClubLogo훅을 사용하여 API 로직을 캡슐화한 것이 좋습니다.
15-18: 간결한 프롭스 인터페이스
ClubLogoEditorProps인터페이스는 간결하고 명확합니다.clubLogo를 선택적(optional)으로 만들고 null 값도 허용함으로써 유연성을 제공합니다.
33-36: 메뉴 토글 함수의 useCallback 사용
toggleMenu함수에useCallback을 사용한 것은 좋습니다. 하지만 이 함수는 의존성이 없어 재생성될 필요가 없으므로 빈 의존성 배열을 사용하는 것이 적절합니다.
51-55: 파일 입력 트리거 함수파일 입력 트리거 로직이 간결하고 명확합니다. ref가 없는 경우를 적절히 처리하고 있습니다.
70-84: 외부 클릭 감지 로직메뉴 외부 클릭을 감지하여 메뉴를 닫는 기능이 잘 구현되어 있습니다. useEffect를 사용하여 이벤트 리스너를 추가하고 정리하는 방식이 적절합니다.
frontend/src/pages/AdminPage/components/ClubLogoEditor/ClubLogoEditor.styles.ts (4)
3-8: 로고 컨테이너 스타일로고 컨테이너 스타일이 적절히 정의되어 있습니다. 상대적 위치를 사용하여 편집 버튼을 배치할 수 있게 한 점이 좋습니다.
9-16: 로고 이미지 스타일로고 이미지 스타일이 잘 정의되어 있습니다.
object-fit: cover를 사용하여 이미지 비율을 유지하면서 컨테이너를 채우는 것이 좋습니다.
74-80: 구분선 스타일구분선 스타일이 깔끔하게 정의되어 있습니다. 90% 너비를 사용하여 여백을 주는 방식이 시각적으로 좋습니다.
81-83: 숨겨진 파일 입력 스타일파일 입력을 숨기는 스타일이 간결하게 정의되어 있습니다. 이 접근 방식은 사용자 정의 UI를 제공하면서 파일 선택 기능을 유지하는 좋은 패턴입니다.
| const handleFileSelect = (e: React.ChangeEvent<HTMLInputElement>) => { | ||
| const file = e.target.files?.[0]; | ||
| if (!file) return; | ||
|
|
||
| if (file.size > MAX_FILE_SIZE) { | ||
| alert('파일 크기가 너무 커요! 10MB 이하 이미지만 업로드할 수 있어요.'); | ||
| return; | ||
| } | ||
|
|
||
| uploadMutation.mutate(file, { | ||
| onError: () => alert('로고 업로드에 실패했어요. 다시 시도해주세요!'), | ||
| }); | ||
| }; |
There was a problem hiding this comment.
🛠️ Refactor suggestion
파일 업로드 처리
파일 선택 핸들러는 파일 존재 여부 및 크기 제한을 적절히 확인합니다. 오류 메시지도 사용자 친화적입니다.
그러나 파일 형식 검증이 누락되어 있습니다. 이미지 파일만 허용하도록 파일 유형을 확인하는 것이 좋습니다.
const handleFileSelect = (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
if (!file) return;
+ const validImageTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/svg+xml'];
+ if (!validImageTypes.includes(file.type)) {
+ alert('지원하지 않는 파일 형식입니다. 이미지 파일(JPEG, PNG, GIF, SVG)만 업로드할 수 있어요.');
+ return;
+ }
if (file.size > MAX_FILE_SIZE) {
alert('파일 크기가 너무 커요! 10MB 이하 이미지만 업로드할 수 있어요.');
return;
}
uploadMutation.mutate(file, {
onError: () => alert('로고 업로드에 실패했어요. 다시 시도해주세요!'),
});
};📝 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 handleFileSelect = (e: React.ChangeEvent<HTMLInputElement>) => { | |
| const file = e.target.files?.[0]; | |
| if (!file) return; | |
| if (file.size > MAX_FILE_SIZE) { | |
| alert('파일 크기가 너무 커요! 10MB 이하 이미지만 업로드할 수 있어요.'); | |
| return; | |
| } | |
| uploadMutation.mutate(file, { | |
| onError: () => alert('로고 업로드에 실패했어요. 다시 시도해주세요!'), | |
| }); | |
| }; | |
| const handleFileSelect = (e: React.ChangeEvent<HTMLInputElement>) => { | |
| const file = e.target.files?.[0]; | |
| if (!file) return; | |
| // Only allow common image types | |
| const validImageTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/svg+xml']; | |
| if (!validImageTypes.includes(file.type)) { | |
| alert( | |
| '지원하지 않는 파일 형식입니다. 이미지 파일(JPEG, PNG, GIF, SVG)만 업로드할 수 있어요.' | |
| ); | |
| return; | |
| } | |
| if (file.size > MAX_FILE_SIZE) { | |
| alert('파일 크기가 너무 커요! 10MB 이하 이미지만 업로드할 수 있어요.'); | |
| return; | |
| } | |
| uploadMutation.mutate(file, { | |
| onError: () => | |
| alert('로고 업로드에 실패했어요. 다시 시도해주세요!'), | |
| }); | |
| }; |

#️⃣연관된 이슈
📝작업 내용
default.mp4
기존 SiderBar.tsx에서 ClubLogoEditor.tsx로 로고 관련 기능 분리
ClubLogoEditor 내에 클럽 로고 수정/초기화 모달 구현
기본 로고와 사용자 지정 로고를 구분하는 로직 재정리
메뉴 오픈/클릭/외부 클릭 감지 로직 구현
alert 멘트가 너무 딱딱하여 친숙하게 수정
이미 초기화된 로고일 경우 초기화 사용 불가능하도록 구현
연필 및 X 아이콘 추가
중점적으로 리뷰받고 싶은 부분(선택)
로직 분리 및 리팩토링 구조가 적절한지
논의하고 싶은 부분(선택)
🫡 참고사항
Summary by CodeRabbit
신규 기능
스타일
리팩터
기타