Skip to content

[feature] 관리자 페이지 UI 변경에 따른 사이드바 및 레이아웃 수정#942

Merged
oesnuj merged 13 commits intodevelop-fefrom
feature/#941-admin-ui-sidebar-layout-MOA-429
Dec 21, 2025
Merged

[feature] 관리자 페이지 UI 변경에 따른 사이드바 및 레이아웃 수정#942
oesnuj merged 13 commits intodevelop-fefrom
feature/#941-admin-ui-sidebar-layout-MOA-429

Conversation

@oesnuj
Copy link
Member

@oesnuj oesnuj commented Dec 20, 2025

#️⃣연관된 이슈

#941


📝작업 내용

배경

  • 디자인 변경으로 관리자 페이지만 다른 배경색 적용 필요
  • 기존 사이드바는 클럽 로고에 종속적이며 재사용성이 안되는데 props를 가지는 구조
  • 각 탭마다 제각각인 제목 스타일과 레이아웃으로 인한 일관성 부족

0. 관리자 페이지 레이아웃 리팩토링

변경 이유: 관리자 페이지만 배경색이 달라지면서 기존 PageContainer로는 대응 불가

변경 내용:

  • 관리자페이지에서 PageContainer 사용 X
  • AdminBackground, AdminLayout으로 구조 변경하여 관리자 페이지 전용 배경색 적용
이전 (Before) 이후 (After)
image image
PageContainer 사용
일반 페이지와 동일한 배경색
AdminBackground + AdminLayout
관리자 전용 배경색 적용

1. 관리자 사이드바 UI 개선

변경 이유:

  • 클럽 로고에 종속적인 구조로 재사용성 없음
  • 불필요한 props 사용

변경 내용:

  • 클럽 로고 관련 UI 제거
  • 사이드바 디자인 개선 및 독립적인 컴포넌트로 리팩토링
이전 (Before) 이후 (After)
image image
클럽 로고 포함
종속적인 구조
클럽 로고 제거
독립적이고 재사용 가능한 컴포넌트

2. 로고 에디터 UI 개선 및 ClubInfoEditTab 통합

변경 이유:

  • 사이드바 레이아웃 변경으로 클럽 로고 편집 기능을 ClubInfoEditTab으로 이동 필요
  • 페이지 내 배치에 맞춰 더 직관적인 UI 개선 필요

변경 내용:

  • 사이드바의 ClubLogoEditor를 동아리 기본 정보 수정 탭으로 이동
  • 드롭다운 메뉴 방식에서 버튼 방식으로 UI 변경
    • 이미지 수정/초기화 버튼을 가로 배치
    • 안내 텍스트를 버튼 하단에 배치
  • Mixpanel 이벤트 통합
    • CLUB_LOGO_EDIT_BUTTON_CLICKED 이벤트를 CLUB_LOGO_UPLOAD_BUTTON_CLICKED로 통합
이전 (Before) 이후 (After)
image image
사이드바에 위치
드롭다운 메뉴 방식
ClubInfoEditTab으로 이동
버튼 가로 배치 + 하단 안내 텍스트

3. ContentSection 컴포넌트 도입

도입 이유:

  • 각 탭마다 제목 스타일이 제각각(h2, InfoTitle, ApplicationTitle 등)이어서 일관성 부족
  • 동일한 역할의 제목/헤더 컴포넌트가 각 탭 스타일 파일마다 중복 정의됨
  • 향후 디자인 변경 시 모든 탭을 일일이 수정해야 하는 유지보수 문제

변경 내용:

  • 관리자 페이지 탭 간 일관된 레이아웃을 위한 컴파운드 패턴 컴포넌트 추가
    • ContentSection, ContentSection.Header, ContentSection.Body
  • 모든 관리자 탭에 ContentSection 적용:
    • ✅ 지원 현황 (ApplicantsTab)
    • ✅ 지원서 목록 (ApplicantsListTab, ApplicationListTab)
    • ✅ 비밀번호 수정 (AccountEditTab)
    • ✅ 동아리 정보 수정 (ClubInfoEditTab)
    • ✅ 활동 사진 (PhotoEditTab)
    • ✅ 모집 정보 (RecruitEditTab)

4. 전체적인 레이아웃 통일 및 스타일 정리

  • 각 탭의 제목, 간격, 섹션 스타일 통일
  • 불필요한 스타일 코드 제거
    • 중복 컨테이너
    • 사용하지 않는 스타일

🎯 중점적으로 리뷰받고 싶은 부분

  • ContentSection 컴포넌트의 컴파운드 패턴 구조가 적절한지
  • 각 탭의 레이아웃이 일관성 있게 적용되었는지
  • 전체 관리자 페이지 레이아웃 구조 개선이 적절한지

🫡 참고사항

  • 기존 탭마다 제각각이던 헤더 스타일을 모두 ContentSection.Header로 통일하여 유지보수성 향상
  • 관리자 페이지 전용 배경색 적용을 위해 레이아웃 구조 분리
  • 사이드바를 독립적인 컴포넌트로 개선하여 재사용성 확보


Summary by CodeRabbit

  • 새로운 기능

    • ContentSection 컴포넌트 추가로 섹션화된 표준 레이아웃 도입
    • 로그아웃 시 확인 프롬프트 추가
  • UI 변경

    • 어드민 페이지 레이아웃 재구성(배경·레이아웃·메인 콘텐츠 계층화, 중앙 정렬)
    • 여러 탭 헤더를 ContentSection으로 통일
    • 편집 버튼 텍스트 및 위치 변경(예: “저장하기”)
    • 동아리 로고 편집 UI 단순화 및 크기/버튼 조정
  • 사용성 개선

    • 사진 업로드에 파일 크기 검사 추가 및 접근성 향상(명확한 버튼 라벨·placeholder·aria)

✏️ Tip: You can customize this high-level summary in your review settings.

- ClubLogoEditor, 클럽 이름 표시 제거
- 사이드바 스타일 개선 (padding, gap, border-radius)
- SideBar props 제거 (clubLogo, clubName)
- PageContainer 제거하고 Background/Layout 구조로 분리
- 전체 배경색(#f2f2f2) 적용
- 컴포넌트 네이밍 개선 (Content → MainContent)
- Divider 제거하고 gap으로 간격 조정
- 모든 탭에서 동일한 헤더 및 레이아웃 스타일 적용
- UI 구조 통일로 유지보수 편의성 향상
@oesnuj oesnuj requested a review from seongwon030 December 20, 2025 08:47
@oesnuj oesnuj self-assigned this Dec 20, 2025
@oesnuj oesnuj added ✨ Feature 기능 개발 🎨 Design 마크업 & 스타일링 💻 FE Frontend labels Dec 20, 2025
@vercel
Copy link

vercel bot commented Dec 20, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (UTC)
moadong Ready Ready Preview, Comment Dec 21, 2025 1:25pm

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 20, 2025

Warning

.coderabbit.yaml has a parsing error

The CodeRabbit configuration file in this repository has a parsing error and default settings were used instead. Please fix the error(s) in the configuration file. You can initialize chat with CodeRabbit to get help with the configuration file.

💥 Parsing errors (1)
Validation error: Invalid regex pattern for base branch. Received: "**" at "reviews.auto_review.base_branches[0]"
⚙️ Configuration instructions
  • Please see the configuration documentation for more information.
  • You can also validate your configuration using the online YAML validator.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Walkthrough

관리자 페이지 레이아웃을 Background > Layout > MainContent 구조로 재정의했고, 공통 레이아웃인 ContentSection 컴포넌트를 추가해 여러 탭(ClubInfo, PhotoEdit, Recruit, Account 등), SideBar, ClubLogoEditor의 UI를 ContentSection/버튼 기반으로 리팩토링했습니다. SideBar는 클럽 props 제거 및 로그아웃 확인을 추가했습니다.

Changes

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 제거), 스타일명(예: DividerSidebarDivider) 변경, 헤더 간소화, 로그아웃 확인 프롬프트 추가.
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

Possibly related PRs

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.

📥 Commits

Reviewing files that changed from the base of the PR and between a8ee59b and 2b1b6b0.

📒 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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

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, RecruitEditTab 4개 파일에서 동일하게 정의되어 있습니다. 공통 스타일 파일로 추출하면 유지보수성이 향상됩니다.

🔎 공통 스타일 추출 예시

공통 스타일 파일 (예: 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.

📥 Commits

Reviewing files that changed from the base of the PR and between d14a323 and b1b1bc3.

📒 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 with if/else or 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.tsx
  • frontend/src/pages/AdminPage/components/ContentSection/ContentSection.tsx
  • frontend/src/pages/AdminPage/components/SideBar/SideBar.styles.ts
  • frontend/src/pages/AdminPage/tabs/AccountEditTab/AccountEditTab.tsx
  • frontend/src/pages/AdminPage/components/ContentSection/ContentSection.styles.ts
  • frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/ApplicantsListTab.tsx
  • frontend/src/pages/AdminPage/components/SideBar/SideBar.tsx
  • frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.styles.ts
  • frontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.styles.ts
  • frontend/src/pages/AdminPage/tabs/AccountEditTab/AccountEditTab.styles.ts
  • frontend/src/pages/AdminPage/tabs/ApplicationListTab/ApplicationListTab.tsx
  • frontend/src/pages/AdminPage/tabs/PhotoEditTab/PhotoEditTab.tsx
  • frontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.tsx
  • frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.tsx
  • frontend/src/pages/AdminPage/AdminPage.styles.ts
  • frontend/src/pages/AdminPage/AdminPage.tsx
  • frontend/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.tsx
  • frontend/src/pages/AdminPage/components/ContentSection/ContentSection.tsx
  • frontend/src/pages/AdminPage/tabs/AccountEditTab/AccountEditTab.tsx
  • frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/ApplicantsListTab.tsx
  • frontend/src/pages/AdminPage/components/SideBar/SideBar.tsx
  • frontend/src/pages/AdminPage/tabs/ApplicationListTab/ApplicationListTab.tsx
  • frontend/src/pages/AdminPage/tabs/PhotoEditTab/PhotoEditTab.tsx
  • frontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.tsx
  • frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.tsx
  • frontend/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.tsx
  • frontend/src/pages/AdminPage/components/ContentSection/ContentSection.tsx
  • frontend/src/pages/AdminPage/components/SideBar/SideBar.styles.ts
  • frontend/src/pages/AdminPage/tabs/AccountEditTab/AccountEditTab.tsx
  • frontend/src/pages/AdminPage/components/ContentSection/ContentSection.styles.ts
  • frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/ApplicantsListTab.tsx
  • frontend/src/pages/AdminPage/components/SideBar/SideBar.tsx
  • frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.styles.ts
  • frontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.styles.ts
  • frontend/src/pages/AdminPage/tabs/AccountEditTab/AccountEditTab.styles.ts
  • frontend/src/pages/AdminPage/tabs/ApplicationListTab/ApplicationListTab.tsx
  • frontend/src/pages/AdminPage/tabs/PhotoEditTab/PhotoEditTab.tsx
  • frontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.tsx
  • frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.tsx
  • frontend/src/pages/AdminPage/AdminPage.styles.ts
  • frontend/src/pages/AdminPage/AdminPage.tsx
  • frontend/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.tsx
  • frontend/src/pages/AdminPage/components/ContentSection/ContentSection.tsx
  • frontend/src/pages/AdminPage/tabs/AccountEditTab/AccountEditTab.tsx
  • frontend/src/pages/AdminPage/components/ContentSection/ContentSection.styles.ts
  • frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/ApplicantsListTab.tsx
  • frontend/src/pages/AdminPage/tabs/PhotoEditTab/PhotoEditTab.tsx
  • frontend/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.tsx
  • frontend/src/pages/AdminPage/tabs/AccountEditTab/AccountEditTab.tsx
  • frontend/src/pages/AdminPage/components/ContentSection/ContentSection.styles.ts
  • frontend/src/pages/AdminPage/tabs/PhotoEditTab/PhotoEditTab.tsx
  • 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} : Use Component Composition instead of Props Drilling to reduce coupling

Applied to files:

  • frontend/src/pages/AdminPage/components/ContentSection/ContentSection.tsx
  • 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/components/ContentSection/ContentSection.tsx
  • frontend/src/pages/AdminPage/tabs/AccountEditTab/AccountEditTab.tsx
  • frontend/src/pages/AdminPage/tabs/PhotoEditTab/PhotoEditTab.tsx
  • 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} : 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.tsx
  • frontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.tsx
  • frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.tsx
  • frontend/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.tsx
  • frontend/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 목표와 일치하며, 일관된 레이아웃을 제공합니다. DividerSidebarDivider로 이름 변경한 것은 명확성을 높입니다.

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!

LayoutMainContent 구조가 관리자 페이지 전용 레이아웃 요구사항에 맞게 잘 설계되었습니다. 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 값에 대한 확인.

ContentSectionBodygap: 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)과 일관되게 적용되었습니다. FieldGroupSNSLabel도 명확한 용도로 잘 정의되어 있습니다.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (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.

📥 Commits

Reviewing files that changed from the base of the PR and between b1b1bc3 and aff44e4.

📒 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 with if/else or 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.HeaderContentSection.Body의 사용이 PR 목표에 부합하며, 다른 관리자 페이지 탭들과 일관된 패턴을 따르고 있습니다.

Label 컴포넌트의 margin-bottom 값을 12px에서 8px로 변경하여
UI 일관성 개선
사이드바 레이아웃 변경으로 로고 에디터를 동아리 기본 정보 수정 탭으로
이동하고, 페이지 내 배치에 맞춰 드롭다운 방식에서 버튼 방식으로 UI 개선

- 드롭다운 메뉴 제거
- 이미지 수정/초기화 버튼을 가로 배치
- 안내 텍스트를 버튼 하단에 배치
- ClubInfoEditTab에 ClubLogoEditor 통합
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

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: 사용하지 않는 상태 변수를 제거하세요.

clubPresidentNametelephoneNumber 상태 변수가 선언되어 있지만, 리팩토링된 UI에서는 더 이상 사용되지 않습니다. 이러한 미사용 상태는 혼란을 야기하고 불필요한 메모리를 차지합니다.

🔎 제안된 수정 사항
 const [clubName, setClubName] = useState('');
-const [clubPresidentName, setClubPresidentName] = useState('');
-const [telephoneNumber, setTelephoneNumber] = useState('');
 const [introduction, setIntroduction] = useState('');

useEffecthandleUpdateClub에서도 해당 필드 참조를 제거해야 합니다:

 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에서 제거된 clubPresidentNametelephoneNumber 필드에 대한 상태 업데이트가 여전히 useEffect에 남아있습니다. 이는 Lines 28-29의 상태 변수 제거와 함께 정리되어야 합니다.


104-105: 업데이트 데이터에서 제거된 필드를 삭제하세요.

presidentNamepresidentPhoneNumber가 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.

📥 Commits

Reviewing files that changed from the base of the PR and between aff44e4 and 7ab9b19.

📒 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 with if/else or 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/src/pages/AdminPage/components/ClubLogoEditor/ClubLogoEditor.tsx
  • frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.tsx
  • frontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.styles.ts
  • frontend/src/pages/AdminPage/components/ClubLogoEditor/ClubLogoEditor.styles.ts
  • frontend/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.tsx
  • frontend/src/pages/AdminPage/components/ClubLogoEditor/ClubLogoEditor.tsx
  • frontend/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.tsx
  • frontend/src/pages/AdminPage/components/ClubLogoEditor/ClubLogoEditor.tsx
  • frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.tsx
  • frontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.styles.ts
  • frontend/src/pages/AdminPage/components/ClubLogoEditor/ClubLogoEditor.styles.ts
  • frontend/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.tsx
  • frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.tsx
  • frontend/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.tsx
  • 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} : Abstract complex logic/interactions into dedicated components/HOCs

Applied to files:

  • frontend/src/pages/AdminPage/tabs/PhotoEditTab/PhotoEditTab.tsx
  • frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.tsx
  • frontend/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.tsx
  • frontend/src/pages/AdminPage/components/ClubLogoEditor/ClubLogoEditor.tsx
  • frontend/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: flexgap: 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 플랫폼 추가 시 확장성 우수
  • 각 플랫폼별 검증 로직 일관성 유지
  • 이벤트 트래킹도 플랫폼별로 적절히 구분

유지보수성이 크게 개선되었습니다.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (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.

📥 Commits

Reviewing files that changed from the base of the PR and between 7ab9b19 and 93474a0.

📒 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 with if/else or 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.tsx
  • frontend/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.tsx
  • frontend/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: 접근성 개선이 잘 적용되었습니다!

각 태그 입력 필드에 placeholderaria-label을 추가하여 사용자 경험과 접근성이 향상되었습니다.


56-62: 버튼 접근성 및 시맨틱이 올바르게 구현되었습니다!

다음 개선사항들이 잘 적용되었습니다:

  • aria-label로 스크린 리더 지원 강화
  • type="button"으로 폼 제출 방지
  • 아이콘을 img 요소로 명시적 렌더링 (빈 alt는 버튼 자체에 레이블이 있으므로 적절함)

Copy link
Member

@seongwon030 seongwon030 left a comment

Choose a reason for hiding this comment

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

바뀐 UI 깔끔하네요 👍

export const ContentSection = Object.assign(ContentSectionRoot, {
Header: ContentSectionHeader,
Body: ContentSectionBody,
});
Copy link
Member

Choose a reason for hiding this comment

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

컴파운드 패턴을 적용하셨군요. 선언적인 느낌이 강하고 가져다 쓸 때 가독성이 좋을 것 같네요.

ContentSection 의 확장성을 고려하신 설계일까요?

Copy link
Member Author

Choose a reason for hiding this comment

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

확장성을 특별히 고려하지는 않았지만, 모든 탭에서 반복적으로 사용되는 섹션 단위 레이아웃이 필요했습니다!
일부 섹션은 Body가 필요 없는 경우도 있어 Header와 Body를 선택적으로 사용할 수 있도록 컴파운드 패턴을 적용했습니다.
이를 통해 선언적으로 컴포넌트를 사용하면서 필요한 요소만 선택해 쓸 수 있습니다.

Copy link
Member

Choose a reason for hiding this comment

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

선택적 사용에 용이하겠네요 굳굳입니다

Comment on lines +232 to +233
<div style={{ marginBottom: '32px' }}>
<ContentSection.Header title='지원 현황' />
Copy link
Member

@seongwon030 seongwon030 Dec 21, 2025

Choose a reason for hiding this comment

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

인라인 스타일이 있어 코멘트 남깁니다~

Copy link
Member Author

Choose a reason for hiding this comment

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

인지하고 있었지만, 해당 탭의 스타일이 바뀌어야 해서 임시로 보기 좋게 인라인 스타일을 적용했습니다!
이후 변경사항에 맞춰서 추후에 수정하면 좋을 것 같습니다.

Comment on lines 93 to 98
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,
Copy link
Member

Choose a reason for hiding this comment

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

요 부분도 함수 분리하면 좋겠네요

Copy link
Member Author

Choose a reason for hiding this comment

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

인정합니다!
분리완료 했습니다~

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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 (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 요약에 따르면 이러한 입력이 의도적으로 제거되었지만, 제출 로직은 업데이트되지 않았습니다.

이로 인해 다음과 같은 문제가 발생합니다:

  • 사용자가 회장 이름이나 전화번호를 수정할 수 없음
  • 초기 값(또는 이전 값)이 사용자 의도와 무관하게 항상 제출됨
  • 데이터 무결성 문제 발생 가능

해결 방법:

  1. 이 필드들이 여전히 필요한 경우 UI에 입력 필드를 다시 추가
  2. 더 이상 필요하지 않은 경우 상태 변수와 제출 로직에서 완전히 제거
🔎 필드가 더 이상 필요하지 않은 경우의 수정 제안
 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.

📥 Commits

Reviewing files that changed from the base of the PR and between 93474a0 and 1b1874a.

📒 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 with if/else or 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/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.tsx
  • frontend/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.tsx
  • frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.tsx
  • frontend/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.tsx
  • frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.tsx
  • frontend/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.tsx
  • 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} : 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.tsx
  • frontend/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.tsx
  • frontend/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 함수로 추출
- 파일 크기 검증 로직의 가독성 개선
- 인라인 onChange 핸들러를 handleFileChange 함수로 추출
- 파일 크기 검증 로직의 가독성 개선
- JSX 코드의 복잡도 감소
Copy link
Contributor

@coderabbitai coderabbitai bot left a 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

♻️ Duplicate comments (1)
frontend/src/pages/AdminPage/tabs/PhotoEditTab/PhotoEditTab.tsx (1)

86-96: 이전 리뷰에서 지적된 에러 핸들링 누락 문제가 여전히 남아있습니다.

updateFeed mutation 실패 시 사용자 피드백 및 상태 롤백이 여전히 구현되지 않았습니다. 이전 리뷰 코멘트를 참고하여 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.

📥 Commits

Reviewing files that changed from the base of the PR and between 1b1874a and a8ee59b.

📒 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 with if/else or 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 일관성을 개선합니다. 기존 기능은 모두 유지되면서 구조만 개선되었습니다.

@seongwon030 seongwon030 self-requested a review December 21, 2025 13:33
@oesnuj oesnuj merged commit 3c54087 into develop-fe Dec 21, 2025
3 checks passed
@oesnuj oesnuj deleted the feature/#941-admin-ui-sidebar-layout-MOA-429 branch January 25, 2026 07:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🎨 Design 마크업 & 스타일링 💻 FE Frontend ✨ Feature 기능 개발

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

Comments