Skip to content

Comments

[feature] 사이드바 내용을 계층 구조로 변경한다#711

Merged
suhyun113 merged 8 commits intodevelop-fefrom
feature/#709-update-sidebar-structure-MOA-205
Sep 1, 2025
Merged

[feature] 사이드바 내용을 계층 구조로 변경한다#711
suhyun113 merged 8 commits intodevelop-fefrom
feature/#709-update-sidebar-structure-MOA-205

Conversation

@suhyun113
Copy link
Collaborator

@suhyun113 suhyun113 commented Aug 29, 2025

#️⃣연관된 이슈

ex) #205

📝작업 내용

사이드바 버튼 추가, 구조 변경, 색상 변경

image

중점적으로 리뷰받고 싶은 부분(선택)

리뷰어가 특별히 봐주었으면 하는 부분이 있다면 작성해주세요

ex) 메서드 XXX의 이름을 더 잘 짓고 싶은데 혹시 좋은 명칭이 있을까요?

논의하고 싶은 부분(선택)

논의하고 싶은 부분이 있다면 작성해주세요.

🫡 참고사항

Summary by CodeRabbit

  • New Features

    • 사이드바를 카테고리별로 그룹화하고 카테고리 제목을 표시합니다.
    • 항목별 활성 상태가 카테고리 단위로 적용됩니다.
    • 일부 항목(아이디/비밀번호 수정, 회원탈퇴)은 준비 중 알림을 표시하고, 다른 항목은 해당 경로로 이동합니다.
    • 로그아웃 시 확인, 토큰 정리 및 로그인 페이지로 이동하는 완전한 로그아웃 흐름을 추가했습니다.
  • Style

    • 헤더·구분선·버튼 간격과 여백을 조정했습니다.
    • 구분선 색상·두께 및 버튼 글자 크기·패딩·활성 배경색을 업데이트했습니다.

@suhyun113 suhyun113 self-assigned this Aug 29, 2025
@suhyun113 suhyun113 added ✨ Feature 기능 개발 🎨 Design 마크업 & 스타일링 💻 FE Frontend labels Aug 29, 2025
@vercel
Copy link

vercel bot commented Aug 29, 2025

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

Project Deployment Preview Comments Updated (UTC)
moadong Ready Ready Preview Comment Sep 1, 2025 10:47am

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 29, 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.
  • 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

사이드바를 평면 탭에서 카테고리·아이템 계층 구조로 변경하고 스타일(간격·폰트·활성색 등)을 갱신했으며, 항목 클릭 로직을 항목별 분기(네비게이션, 알림, 로그아웃 흐름 포함)로 수정했습니다. (≤50단어)

Changes

Cohort / File(s) Change Summary
스타일 변경 및 새 컴포넌트 추가
frontend/src/pages/AdminPage/components/SideBar/SideBar.styles.ts
헤더 margin 변경, dividerDivider로 export 이름 변경 및 스타일 수정, 버튼 컨테이너 gap 확장, SidebarCategoryTitle(새 export) 추가, SidebarButtonlibutton으로 변경 및 스타일 확장, 활성 배경색·폰트 가중치 조정
구조적 리팩터 및 클릭 로직 변경
frontend/src/pages/AdminPage/components/SideBar/SideBar.tsx
평면 tabs → 카테고리(TabCategory) + 항목 배열 구조로 변경, 렌더링을 카테고리/아이템 루프로 전환, 활성 판정(location.pathname.startsWith), 항목 클릭 시 경로 이동 또는 안내 alert 분기 도입, 회원탈퇴/아이디·비밀번호 수정은 알림 처리, 로그아웃 시 토큰 처리·API 호출·네비게이트 흐름 추가

Sequence Diagram(s)

sequenceDiagram
    actor User as 사용자
    participant SB as SideBar
    participant Router as Router
    participant API as AuthAPI
    participant Alert as Alert

    Note over SB: 초기 렌더링 — 카테고리/아이템 구성 및 활성 판정(location.pathname)

    User->>SB: 항목 클릭 (item)
    alt item === "아이디/비밀번호 수정" or "회원탈퇴"
        SB->>Alert: 준비중 안내 표시
    else item === "로그아웃"
        SB->>Alert: 로그아웃 확인
        alt 확인됨
            SB->>API: (선택적) 로그아웃 호출 (refresh token 존재 시)
            API-->>SB: 응답
            SB->>Router: navigate('/auth/login')
        end
    else 네비게이션 대상
        SB->>Router: navigate(item.path)
        Router-->>SB: location 업데이트
        SB->>SB: 활성 상태 재계산
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Assessment against linked issues

Objective Addressed Explanation
사이드바 구조 업데이트 MOA-205
디자인 업데이트 MOA-205

Out-of-scope changes

Code Change Explanation
로그아웃 API 호출 및 토큰 정리 로직 추가 (frontend/src/pages/AdminPage/components/SideBar/SideBar.tsx) 링크된 이슈는 사이드바 구조/디자인 변경이 목표로, 서버 통신(로그아웃 API) 및 토큰 삭제 흐름은 명시된 목표에서 벗어납니다.

Possibly related issues

Possibly related PRs

Suggested reviewers

  • Zepelown
  • oesnuj
  • lepitaaar
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/#709-update-sidebar-structure-MOA-205

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore or @coderabbit ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

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: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
frontend/src/pages/AdminPage/components/SideBar/SideBar.styles.ts (1)

4-6: align-items: left는 잘못된 값입니다

Flex 정렬 키워드는 flex-start | center | flex-end ... 입니다. 의도대로라면 flex-start를 사용해야 합니다.

 export const SidebarWrapper = styled.aside`
   display: flex;
   flex-direction: column;
-  align-items: left;
+  align-items: flex-start;
🧹 Nitpick comments (3)
frontend/src/pages/AdminPage/components/SideBar/SideBar.styles.ts (1)

21-26: 여러 하드코딩 값(168px, 16px, 37px 등)을 토큰/상수로 치환 권장

디자인 스케일 조정·다크모드 대응을 위해 spacing/size/color를 테마 또는 상수로 관리해 주세요.

예시(일부):

// tokens.ts
export const SIDEBAR = {
  width: '168px',
  gap: '16px',
  itemHeight: '37px',
  radius: '10px',
  divider: '#C5C5C5',
} as const;
-import styled from 'styled-components';
+import styled from 'styled-components';
+import { SIDEBAR } from './tokens';
 
 export const ClubLogo = styled.img`
-  width: 168px;
-  height: 168px;
+  width: ${SIDEBAR.width};
+  height: ${SIDEBAR.width};
   background: #ededed;
   border-radius: 10px;
`;

Also applies to: 45-50, 60-65

frontend/src/pages/AdminPage/components/SideBar/SideBar.tsx (2)

45-49: useMemo는 과합니다(미세 최적화)

tabs가 정적 상수이므로 매 렌더에 선형 스캔해도 비용이 작습니다. 가독성을 위해 일반 변수로 단순화 가능.

-  const activeTab = useMemo(() => { 
-    return tabs.map((tab) =>
-      tab.items.findIndex((item) => location.pathname.startsWith(item.path)),
-    );
-  }, [location.pathname]);
+  const activeTab = tabs.map((tab) =>
+    tab.items.findIndex((item) => location.pathname.startsWith(item.path)),
+  );

13-39: 경로 문자열 상수화

라우트 경로를 하드코딩하면 추후 변경 시 리스크가 큽니다. routes.ts 등으로 분리해 상수/enum으로 관리해 주세요.

예시:

export const ROUTES = {
  CLUB_INFO: '/admin/club-info',
  RECRUIT_EDIT: '/admin/recruit-edit',
  PHOTO_EDIT: '/admin/photo-edit',
  APPLICATION_EDIT: '/admin/application-edit',
  APPLICANTS: '/admin/applicants',
  ACCOUNT_EDIT: '/admin/account-edit',
  USER_DELETE: '/admin/user-delete',
} as const;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear 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 6be7d37 and 4038a0e.

📒 Files selected for processing (2)
  • frontend/src/pages/AdminPage/components/SideBar/SideBar.styles.ts (2 hunks)
  • frontend/src/pages/AdminPage/components/SideBar/SideBar.tsx (2 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
frontend/**/*.{ts,tsx}

📄 CodeRabbit inference engine (frontend/.cursorrules)

frontend/**/*.{ts,tsx}: Replace magic numbers with named constants for clarity.
Replace complex or nested ternary operators with if/else statements or IIFEs for readability.
Assign complex boolean conditions to named variables.
Use consistent return types for similar functions and hooks.
Avoid hidden side effects; functions should only perform actions implied by their signature (Single Responsibility Principle).
Use unique, descriptive names for custom wrappers and functions to avoid ambiguity.
Define constants near related logic or ensure names link them clearly.

Files:

  • frontend/src/pages/AdminPage/components/SideBar/SideBar.tsx
  • frontend/src/pages/AdminPage/components/SideBar/SideBar.styles.ts
frontend/**/*.tsx

📄 CodeRabbit inference engine (frontend/.cursorrules)

frontend/**/*.tsx: Abstract complex logic/interactions into dedicated components or higher-order 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.
Break down broad state management into smaller, focused hooks or contexts.
Use component composition instead of props drilling.

Files:

  • frontend/src/pages/AdminPage/components/SideBar/SideBar.tsx
🧠 Learnings (1)
📚 Learning: 2025-03-19T05:18:07.818Z
Learnt from: seongwon030
PR: Moadong/moadong#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

Comment on lines +39 to 43
border: 1px solid;
color: #C5C5C5;
height: 0;
margin: 16px 0px 16px 0px;
`;
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

hr 테마 색 지정이 의도대로 적용되지 않을 수 있습니다

border: 1px solidcolor 조합은 브라우저에 따라 선 색상이 기대와 다를 수 있습니다. 명시적으로 border 색을 지정하는 편이 안전합니다.

다음으로 교체 제안:

 export const divider = styled.hr`
   width: 100%;
-  border: 1px solid;
-  color: #C5C5C5;
-  height: 0;
-  margin: 16px 0px 16px 0px;
+  border: 0;
+  border-top: 1px solid #C5C5C5;
+  height: 0;
+  margin: 16px 0;
 `;
📝 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.

Suggested change
border: 1px solid;
color: #C5C5C5;
height: 0;
margin: 16px 0px 16px 0px;
`;
export const divider = styled.hr`
width: 100%;
border: 0;
border-top: 1px solid #C5C5C5;
height: 0;
margin: 16px 0;
`;
🤖 Prompt for AI Agents
In frontend/src/pages/AdminPage/components/SideBar/SideBar.styles.ts around
lines 39 to 43, the CSS uses "border: 1px solid" and "color: #C5C5C5" which can
yield inconsistent hr line color across browsers; explicitly set the border
color instead (e.g. replace with a single declaration like "border: 1px solid
#C5C5C5" or add "border-color: #C5C5C5") and remove or keep the "color" property
only if it’s needed for text—ensure the hr/line uses the explicit border color
so the theme renders consistently.

Comment on lines 45 to 50
export const SidebarButtonContainer = styled.ul`
display: flex;
flex-direction: column;
gap: 8.5px;
gap: 16px;
list-style: none;
`;
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

현재 TSX 구조와의 시맨틱 불일치(ul/div/li) 해결을 위한 스타일 컴포넌트 교정 제안

ul > div > li는 유효한 리스트 마크업이 아닙니다. 빠르게 시맨틱을 바로잡으려면 컨테이너를 nav로, 항목을 button으로 바꾸는 접근을 권장합니다(아래 TSX 수정안과 짝을 이룹니다).

-export const SidebarButtonContainer = styled.ul`
+export const SidebarButtonContainer = styled.nav`
   display: flex;
   flex-direction: column;
   gap: 16px;
-  list-style: none;
 `;
 
-export const SidebarButton = styled.li`
+export const SidebarButton = styled.button`
   cursor: pointer;
   width: 100%;
   height: 37px;
   border-radius: 10px;
   display: flex;
   align-items: center;
   padding-left: 10px;
   transition: background-color 0.1s ease;
   font-size: 1rem;
   font-weight: 500;
+  background: transparent;
+  border: 0;
+  text-align: left;
 
   &.active {
     background-color: rgba(255, 117, 67, 1);
     color: white;
     font-weight: 500;
   }
`;

Also applies to: 60-77

🤖 Prompt for AI Agents
In frontend/src/pages/AdminPage/components/SideBar/SideBar.styles.ts around
lines 45-50 (and similarly for lines 60-77), the styled component currently uses
a ul which doesn't match the TSX structure (ul > div > li is invalid); replace
the container element style to export a nav (or a div with role="navigation")
for semantic correctness and change the list item styles to target/button
elements instead of li (update the styled component names to reflect SidebarNav
and SidebarButton and adjust selectors/properties accordingly) so the styled
components align with the TSX markup and produce valid semantic HTML.

Comment on lines +52 to +58
export const SidebarCategoryTitle = styled.p`
align-items: center;
padding: 6px 0px 6px 10px;
font-size: 0.75rem;
font-weight: medium;
color: #989898;
`;
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

align-items 무효 + 잘못된 font-weight 값(medium)

  • align-items는 flex 컨테이너에서만 동작합니다.
  • CSS font-weight: medium은 유효하지 않습니다(숫자 500 권장).

적용 예시:

 export const SidebarCategoryTitle = styled.p`
-  align-items: center;
+  display: flex;
+  align-items: center;
   padding: 6px 0px 6px 10px;
   font-size: 0.75rem;
-  font-weight: medium;
+  font-weight: 500;
   color: #989898;
 `;

추가로, 배경이 흰색(#fff)인 경우 현재 색(#989898)의 대비가 낮을 수 있습니다. 아래 스크립트로 WCAG 대비를 확인해 주세요.


🏁 Script executed:

#!/bin/bash
python - << 'PY'
def srgb_to_lin(c):
    c/=255.0
    return c/12.92 if c<=0.04045*255 else ((c/255+0.055)/1.055)**2.4
def rel_lum(rgb):
    r,g,b=rgb
    R= (r/255.0)
    G= (g/255.0)
    B= (b/255.0)
    def f(x): return x/12.92 if x<=0.04045 else ((x+0.055)/1.055)**2.4
    R,G,B=map(f,[R,G,B])
    return 0.2126*R+0.7152*G+0.0722*B
def hex_to_rgb(h): return tuple(int(h[i:i+2],16) for i in (0,2,4))
fg=hex_to_rgb("989898"); bg=hex_to_rgb("FFFFFF")
Lf, Lb = rel_lum(fg), rel_lum(bg)
ratio = (max(Lf,Lb)+0.05)/(min(Lf,Lb)+0.05)
print(f"contrast ratio (#989898 on #FFFFFF): {ratio:.2f}:1 (AA body text >= 4.5:1)")
PY

Length of output: 78


CSS 유효성 및 접근성 문제 수정

  • align-items는 flex 컨테이너에서만 동작하므로 display: flex; 추가
  • CSS 키워드 medium은 유효하지 않으므로 font-weight: 500; 으로 변경
  • #989898 on #FFFFFF 대비 비율이 2.88:1로 WCAG AA(본문, ≥4.5:1) 미달 → 대비 비율 ≥4.5:1을 만족하는 색상으로 조정
export const SidebarCategoryTitle = styled.p`
+ display: flex;
  align-items: center;
  padding: 6px 0px 6px 10px;
  font-size: 0.75rem;
- font-weight: medium;
+ font-weight: 500;
- color: #989898;
+ color: /* WCAG AA 대비(≥4.5:1)를 만족하는 색상으로 변경 */;
`;

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
frontend/src/pages/AdminPage/components/SideBar/SideBar.styles.ts around lines
52-58: the styled p uses properties that are invalid or inaccessible and the
color fails WCAG contrast; add display: flex to make align-items effective,
change font-weight from the invalid keyword "medium" to numeric 500, and replace
the color #989898 with a color that meets WCAG AA contrast ratio ≥4.5:1 against
white (e.g., pick #6b6b6b or #666666) and verify the final value with a contrast
checker.

Comment on lines +69 to +70
font-size: 1rem;
font-weight: medium;
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

font-weight: medium는 유효하지 않습니다

의도한 Medium 웨이트는 500으로 명시해 주세요(또는 테마 토큰 사용).

적용 예시:

   transition: background-color 0.1s ease;
-  font-size: 1rem;
-  font-weight: medium;
+  font-size: 1rem;
+  font-weight: 500;

   &.active {
     background-color: rgba(255, 117, 67, 1);
     color: white;
-    font-weight: medium;
+    font-weight: 500;
   }

Also applies to: 75-75

🤖 Prompt for AI Agents
In frontend/src/pages/AdminPage/components/SideBar/SideBar.styles.ts around
lines 69-70 (and also line 75), the CSS rule uses the invalid value
`font-weight: medium`; replace it with a valid numeric weight such as `500` or
use the project's theme token (e.g., theme.typography.fontWeightMedium) to
ensure a valid, consistent font weight; update each occurrence accordingly.

Copy link
Contributor

@lepitaaar lepitaaar left a comment

Choose a reason for hiding this comment

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

수고하셨습니다! 리뷰단것들 확인해주시면 감사하겠습니다

[location.pathname],
);
const activeTab = useMemo(() => {
return tabs.map((tab) =>
Copy link
Contributor

Choose a reason for hiding this comment

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

계층구조로 바뀜에 따라 로직변경 잘해주셨네요

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

감사합니다ㅏ~

const handleTabClick = (tab: (typeof tabs)[number]) => {
if (tab.label === '계정 관리') {
alert('계정 관리 기능은 아직 준비 중이에요. ☺️');
const handleTabClick = (item: (typeof tabs)[number]['items'][number]) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

interface Tabs {
  category: string;
  items: Tab[];
}

interface Tab {
  label: string;
  path: string;
}

const tabs: Tabs[] = [
  {
    category: '기본 정보',
    items: [{ label: '기본 정보 수정', path: '/admin/club-info' }],
  },
  ...
}

const handleTabClick = (item: Tab) => {
  ...
}

현재 handleTabClick 파라미터 타입의 가독성이 떨어진다 생각이듭니다.
따라서 현재 tabs 타입을 Interface로 추상화하여 관리하는것 어떨까요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

넵 수정하겠습니다ㅏ

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.

사이드바 변경 수고많으셨습니다~

리뷰를 까먹고 여기에 달아요. tabs상수에 타입 추가하면 좀 더 가독성을 높일 수 있을 것 같아요

padding: 9px 11px;
padding-left: 10px;
transition: background-color 0.1s ease;
font-size: 1rem;
Copy link
Member

Choose a reason for hiding this comment

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

왜 rem을 쓰셨나요?

Copy link
Contributor

Choose a reason for hiding this comment

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

혹시 왜쓰나요?

Copy link
Member

Choose a reason for hiding this comment

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

rem은 상위태그 기준에 따라(기본 16px) 크기가 정해지는 반응형 방식인데 해당 페이지에는 반응형이 필요없기에 쓴 이유가 궁금했어요

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

특별한 이유가 있었던 것은 아니고 기존 코드 컨벤션을 따랐습니다!
참고로 찾아보니 rem은 루트 글꼴 크기 기준이라 반응형 스케일 관리에 유리하다고 합니다!

다만 현재 rem과 px 사용이 혼재되어 있어, 다음 리택토링 때 기준을 정해 통일하면 좋을 것 같습니다!

},
{
category: '모집 정보',
items: [
Copy link
Member

Choose a reason for hiding this comment

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

items보다 더 나은 이름은 없을까요?

Copy link
Collaborator 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: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
frontend/src/pages/AdminPage/components/SideBar/SideBar.styles.ts (1)

4-7: align-items: left → flex-start로 교정 필요

CSS 값 left는 유효하지 않습니다. 의도대로 좌측 정렬하려면 flex-start를 사용하세요.

 export const SidebarWrapper = styled.aside`
   display: flex;
   flex-direction: column;
-  align-items: left;
+  align-items: flex-start;
   word-wrap: break-word;
♻️ Duplicate comments (6)
frontend/src/pages/AdminPage/components/SideBar/SideBar.styles.ts (3)

37-43: hr 색상 지정 방식 교정(브라우저 간 일관성)

border: 1px solid + color 조합은 hr 색이 의도와 다를 수 있습니다. border-top에 색을 명시하세요.

 export const Divider = styled.hr`
   width: 100%;
-  border: 1px solid;
-  color: #C5C5C5;
-  height: 0;
-  margin: 16px 0px 16px 0px;
+  border: 0;
+  border-top: 1px solid #C5C5C5;
+  height: 0;
+  margin: 16px 0;
 `;

52-58: CSS 유효성 및 대비 개선: display/weight/color 수정

align-items가 동작하지 않고, font-weight: medium은 무효입니다. 텍스트 대비(#989898 on #fff)도 낮습니다.

 export const SidebarCategoryTitle = styled.p`
-  align-items: center;
+  display: flex;
+  align-items: center;
   padding: 6px 0px 6px 10px;
   font-size: 0.75rem;
-  font-weight: medium;
-  color: #989898;
+  font-weight: 500;
+  color: #6B6B6B; /* AA 대비 확보용(예시) */
 `;

73-75: font-weight: medium → 500으로 교체

유효하지 않은 값입니다. 숫자 웨이트 또는 테마 토큰을 사용하세요.

-  font-weight: medium;
+  font-weight: 500;
@@
-    font-weight: medium;
+    font-weight: 500;

Also applies to: 81-81

frontend/src/pages/AdminPage/components/SideBar/SideBar.tsx (3)

13-21: 탭 모델 확장: 안정 키/상태 메타 추가

라벨 문자열 의존을 없애려면 id/disabled/disabledMessage 속성을 타입에 포함하세요.

-interface TabItem {
-  label: string;
-  path: string;
-}
+interface TabItem {
+  id: string;              // stable key
+  label: string;
+  path: string;
+  disabled?: boolean;
+  disabledMessage?: string;
+}
 
-interface TabCategory {
+interface TabCategory {
   category: string;
   items: TabItem[];
 }

23-49: 데이터에 동작을 담아 문자열 분기 제거

비활성 항목을 데이터로 표기하고, 키도 안정적인 값으로 교체하세요.

-const tabs: TabCategory[] = [
+const tabs: TabCategory[] = [
   {
     category: '기본 정보',
-    items: [{ label: '기본 정보 수정', path: '/admin/club-info' }],
+    items: [{ id: 'club-info', label: '기본 정보 수정', path: '/admin/club-info' }],
   },
   {
     category: '모집 정보',
     items: [
-      { label: '모집 정보 수정', path: '/admin/recruit-edit' },
-      { label: '활동 사진 수정', path: '/admin/photo-edit' },
+      { id: 'recruit-edit', label: '모집 정보 수정', path: '/admin/recruit-edit' },
+      { id: 'photo-edit', label: '활동 사진 수정', path: '/admin/photo-edit' },
     ],
   },
   {
     category: '지원 관리',
     items: [
-      { label: '지원서 관리', path: '/admin/application-edit' },
-      { label: '지원자 현황', path: '/admin/applicants' },
+      { id: 'application-edit', label: '지원서 관리', path: '/admin/application-edit' },
+      { id: 'applicants', label: '지원자 현황', path: '/admin/applicants' },
     ],
   },
   {
     category: '계정 관리',
     items: [
-      { label: '아이디/비밀번호 수정', path: '/admin/account-edit'},
-      { label: '회원탈퇴', path: '/admin/user-delete'},
+      { id: 'account-edit', label: '아이디/비밀번호 수정', path: '/admin/account-edit', disabled: true, disabledMessage: '아이디/비밀번호 수정 기능은 아직 준비 중이에요. ☺️' },
+      { id: 'user-delete', label: '회원탈퇴', path: '/admin/user-delete', disabled: true, disabledMessage: '회원탈퇴 기능은 아직 준비 중이에요. ☺️' },
     ],
   },
 ];

61-71: 라벨 문자열 비교 제거

렌더 데이터의 disabled/disabledMessage로 제어하면 라벨 변경에 안전합니다.

-  const handleTabClick = (item: TabItem) => {
-    if (item.label === '아이디/비밀번호 수정') {
-      alert('아이디/비밀번호 수정 기능은 아직 준비 중이에요. ☺️');
-      return;
-    }
-    if (item.label === '회원탈퇴') {
-      alert('회원탈퇴 기능은 아직 준비 중이에요. ☺️');
-      return;
-    }
-    navigate(item.path);
-  };
+  const handleTabClick = (item: TabItem) => {
+    if (item.disabled) {
+      alert(item.disabledMessage ?? '아직 준비 중이에요. ☺️');
+      return;
+    }
+    navigate(item.path);
+  };
🧹 Nitpick comments (3)
frontend/src/pages/AdminPage/components/SideBar/SideBar.styles.ts (1)

18-18: 19px는 매직 넘버로 보입니다

디자인 스케일(예: 4px 그리드) 또는 토큰으로 정렬하는 편이 유지보수에 유리합니다. 20px 혹은 테마 토큰으로 정규화 권장.

-  margin-bottom: 19px;
+  margin-bottom: 20px; /* or ${({ theme }) => theme.space[5]} */
frontend/src/pages/AdminPage/components/SideBar/SideBar.tsx (2)

55-59: 활성 판별 가독성/정확도 개선(matchPath 활용)

startsWith는 경로 접두 일치로 오탐 가능. react-router matchPath로 명시적으로 매칭하세요.

-  const activeTab = useMemo(() => { 
-    return tabs.map((tab) =>
-      tab.items.findIndex((item) => location.pathname.startsWith(item.path)),
-    );
-  }, [location.pathname]);
+  const activeTab = useMemo(() => {
+    return tabs.map((tab) =>
+      tab.items.findIndex((item) =>
+        // eslint-disable-next-line @typescript-eslint/no-explicit-any
+        (require('react-router-dom') as any).matchPath({ path: item.path, end: false }, location.pathname)
+      )
+    );
+  }, [location.pathname]);

118-120: 로그아웃 버튼에도 type="button" 지정

폼 내부 편입 시 의도치 않은 submit 방지.

-      <Styled.SidebarButton onClick={handleLogout}>
+      <Styled.SidebarButton type="button" onClick={handleLogout}>
         로그아웃
       </Styled.SidebarButton>
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear 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 4038a0e and fc4acba.

📒 Files selected for processing (2)
  • frontend/src/pages/AdminPage/components/SideBar/SideBar.styles.ts (2 hunks)
  • frontend/src/pages/AdminPage/components/SideBar/SideBar.tsx (2 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
frontend/**/*.{ts,tsx}

📄 CodeRabbit inference engine (frontend/.cursorrules)

frontend/**/*.{ts,tsx}: Replace magic numbers with named constants for clarity.
Replace complex or nested ternary operators with if/else statements or IIFEs for readability.
Assign complex boolean conditions to named variables.
Use consistent return types for similar functions and hooks.
Avoid hidden side effects; functions should only perform actions implied by their signature (Single Responsibility Principle).
Use unique, descriptive names for custom wrappers and functions to avoid ambiguity.
Define constants near related logic or ensure names link them clearly.

Files:

  • frontend/src/pages/AdminPage/components/SideBar/SideBar.styles.ts
  • frontend/src/pages/AdminPage/components/SideBar/SideBar.tsx
frontend/**/*.tsx

📄 CodeRabbit inference engine (frontend/.cursorrules)

frontend/**/*.tsx: Abstract complex logic/interactions into dedicated components or higher-order 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.
Break down broad state management into smaller, focused hooks or contexts.
Use component composition instead of props drilling.

Files:

  • frontend/src/pages/AdminPage/components/SideBar/SideBar.tsx
🧠 Learnings (2)
📚 Learning: 2025-07-19T05:09:10.702Z
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.702Z
Learning: Applies to frontend/**/*.tsx : Separate significantly different conditional UI/logic into distinct components.

Applied to files:

  • frontend/src/pages/AdminPage/components/SideBar/SideBar.styles.ts
📚 Learning: 2025-03-19T05:18:07.818Z
Learnt from: seongwon030
PR: Moadong/moadong#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

Comment on lines +60 to 65
export const SidebarButton = styled.button`
all: unset;
box-sizing: border-box;
display: flex;
align-items: center;
cursor: pointer;
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

all: unset으로 포커스 표시 제거됨 → 키보드 접근성 추가

키보드 사용자를 위해 최소한의 :focus-visible 스타일을 제공하세요.

 export const SidebarButton = styled.button`
   all: unset;
   box-sizing: border-box;
   display: flex;
   align-items: center;
   cursor: pointer;
+  &:focus-visible {
+    outline: 2px solid rgba(255, 117, 67, 0.9);
+    outline-offset: 2px;
+    border-radius: 10px;
+  }
📝 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.

Suggested change
export const SidebarButton = styled.button`
all: unset;
box-sizing: border-box;
display: flex;
align-items: center;
cursor: pointer;
export const SidebarButton = styled.button`
all: unset;
box-sizing: border-box;
display: flex;
align-items: center;
cursor: pointer;
&:focus-visible {
outline: 2px solid rgba(255, 117, 67, 0.9);
outline-offset: 2px;
border-radius: 10px;
}
`;
🤖 Prompt for AI Agents
In frontend/src/pages/AdminPage/components/SideBar/SideBar.styles.ts around
lines 60-65, the SidebarButton currently uses "all: unset" which removes focus
indication and breaks keyboard accessibility; add a :focus-visible rule that
restores a visible, minimal focus style (e.g., a 2px solid or box-shadow ring
using the theme or a contrast color and a small border-radius) so keyboard users
can see focus without affecting mouse interactions, and keep this rule limited
to :focus-visible (not :focus) to avoid visual changes for pointer users.


&.active {
background-color: rgba(255, 84, 20, 0.8);
background-color: rgba(255, 117, 67, 1);
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

활성 배경색 대비 확인 요청(화이트 텍스트 가독성)

#FF7543 배경에 흰 텍스트 대비가 AA(4.5:1) 미달일 수 있습니다. 아래 스크립트로 확인 후 필요시 색상/톤 조정 제안드립니다.


🏁 Script executed:

#!/bin/bash
python - << 'PY'
def rel_lum(hex_):
    h=hex_.lstrip('#')
    r,g,b=[int(h[i:i+2],16)/255 for i in (0,2,4)]
    def f(c): return c/12.92 if c<=0.04045 else ((c+0.055)/1.055)**2.4
    R,G,B=map(f,[r,g,b]); return 0.2126*R+0.7152*G+0.0722*B
bg='#FF7543'; fg='#FFFFFF'
Lb, Lf = rel_lum(bg), rel_lum(fg)
ratio=(max(Lb,Lf)+0.05)/(min(Lb,Lf)+0.05)
print(f"contrast ratio (white on {bg}): {ratio:.2f}:1 (needs ≥4.5:1)")
PY

Length of output: 67


배경색 대비 개선 필요: frontend/src/pages/AdminPage/components/SideBar/SideBar.styles.ts:79에서 rgba(255,117,67,1) 배경에 흰 텍스트 대비비가 2.67:1로 WCAG AA 기준(≥4.5:1) 미만입니다. 대비비 ≥4.5:1을 만족하도록 배경색을 더 어두운 톤으로 조정하거나 텍스트 색상을 변경하세요.

🤖 Prompt for AI Agents
In frontend/src/pages/AdminPage/components/SideBar/SideBar.styles.ts at line 79,
the background-color rgba(255,117,67,1) yields a contrast ratio of ~2.67:1
against white text and fails WCAG AA; either choose a darker background color
(adjust hue/value until contrast ≥4.5:1) or change the text color to a
sufficiently dark color so the combination meets a 4.5:1 contrast ratio, then
update the style and verify the final color pair with a contrast checker across
normal and focused states.

Comment on lines 101 to 116
<Styled.SidebarButtonContainer>
{tabs.map((tab, index) => (
<Styled.SidebarButton
key={tab.label}
className={activeTab === index ? 'active' : ''}
onClick={() => handleTabClick(tab)}
>
{tab.label}
</Styled.SidebarButton>
{tabs.map((tab, tabIndex) => (
<li key={tab.category}>
<Styled.SidebarCategoryTitle>{tab.category}</Styled.SidebarCategoryTitle>
{tab.items.map((item, itemIndex) => (
<Styled.SidebarButton
key={item.label}
className={activeTab[tabIndex] === itemIndex ? 'active' : ''}
onClick={() => handleTabClick(item)}
>
{item.label}
</Styled.SidebarButton>
))}
</li>
))}
</Styled.SidebarButtonContainer>
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

리스트/버튼 접근성 보강(type/aria, 안정 키 사용)

  • 버튼에 type="button" 및 상태 표기(aria-current/aria-disabled) 추가
  • 아이템 키는 label 대신 안정적인 id/path 사용
  • 카테고리 타이틀은 시맨틱 헤딩 권장
-      <Styled.SidebarButtonContainer>
+      <Styled.SidebarButtonContainer aria-label="사이드바">
         {tabs.map((tab, tabIndex) => (
           <li key={tab.category}>
-            <Styled.SidebarCategoryTitle>{tab.category}</Styled.SidebarCategoryTitle>
+            <Styled.SidebarCategoryTitle as="h3">{tab.category}</Styled.SidebarCategoryTitle>
             {tab.items.map((item, itemIndex) => (
               <Styled.SidebarButton
-                key={item.label}
-                className={activeTab[tabIndex] === itemIndex ? 'active' : ''}
-                onClick={() => handleTabClick(item)}
+                key={item.id ?? item.path}
+                type="button"
+                aria-current={activeTab[tabIndex] === itemIndex ? 'page' : undefined}
+                aria-disabled={item.disabled || undefined}
+                className={activeTab[tabIndex] === itemIndex ? 'active' : ''}
+                onClick={() => handleTabClick(item)}
               >
                 {item.label}
               </Styled.SidebarButton>
             ))}
           </li>
         ))}
       </Styled.SidebarButtonContainer>

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In frontend/src/pages/AdminPage/components/SideBar/SideBar.tsx around lines 101
to 116, the list items and buttons need accessibility and stability
improvements: change Styled.SidebarButton to render as a button element with
explicit type="button", add aria-current="true" when the button is the active
tab and aria-disabled="true" when an item is disabled (or remove onClick if
disabled), replace key={item.label} with a stable key such as item.id or
item.path, and render the category title with a semantic heading element (e.g.,
h3) instead of a plain span/div to improve screen reader navigation.

@suhyun113 suhyun113 merged commit 28b1471 into develop-fe Sep 1, 2025
5 checks passed
@lepitaaar lepitaaar deleted the feature/#709-update-sidebar-structure-MOA-205 branch October 21, 2025 08:07
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.

3 participants