Conversation
- manualChunks 설정으로 라이브러리별 청크 분리 - Windows/PNPM 환경 호환을 위한 경로 정규화 추가 - react-* 패키지 매칭 순서 조정으로 정확한 청크 분류 - zustand + react-query를 state 청크로 통합 - sentry를 analytics 청크로 통합
- 테마 컬러 시스템 적용 - 회색 테두리 추가 (0.5px solid gray[400]) - 배경색을 하드코딩에서 테마 컬러로 변경 (#efefef -> colors.base.white)
…e-setting-MOA-420 [chore] vite 청크 분리로 캐시를 최적화한다
…-styling-MOA-488 [feature] 메인 동아리카드 로고 테두리 설정
- AnswerContainer에 CSS transition 기반 애니메이션 추가 - 조건부 렌더링 대신 max-height, opacity, padding transition 적용 - 부드러운 열림/닫힘 효과로 UX 개선
- TabContent에 fadeIn 애니메이션 적용 - 탭 전환 시 opacity와 translateY 효과로 부드러운 전환 - 0.3s ease-in-out transition으로 자연스러운 UX 제공
- theme/transitions.ts 추가 (duration, easing 상수) - 0.3s → transitions.duration.normal 교체
- useSearchParams로 탭 상태 관리 - TAB_TYPE 상수 분리 및 타입 안정성 향상 - onClick 핸들러 useCallback 메모이제이션
- duplicateApplication API 함수 구현 - useDuplicateApplication TanStack Query 훅 추가
- '제목 수정하기' 메뉴를 '지원서 복제하기'로 변경 - onDuplicate prop 추가 및 핸들러 연결 - 불필요한 React import 제거
- onDuplicate prop 추가 및 ApplicationMenu에 전달
…nt-animation-MOA-494 [feature] 상세페이지 콘텐츠에 애니메이션을 추가한다
- TOGGLE_BUTTON_TEXT 상수 객체로 추출 - 가독성 및 유지보수성 향상
- 재사용 가능한 유틸 함수로 추출 - 단위 테스트 추가 - ApplicationRowItem 코드 간소화
…ton-MOA-499 [feature] 지원서 복제 기능 구현
- Award 타입 import를 상대 경로에서 @/types/club으로 변경 - 라벨에서 이모지(🏆) 제거하여 일관성 유지
- FAQ 타입 import를 상대 경로에서 @/types/club으로 변경 - 서버에서 받아온 FAQ에 자동으로 ID 생성하는 로직 추가 - Type predicate를 사용하여 필터링 후 타입 안정성 보장 - 불필요한 null 체크 제거로 코드 간결화 - 라벨에서 이모지(❓) 제거하여 일관성 유지
- FAQ 인터페이스에 optional id 필드 추가하여 클라이언트 측 리스트 관리 지원 - RecruitmentStatus 타입 포맷 정리
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning
|
| 코호트 / 파일(들) | 변경 요약 |
|---|---|
지원서 복제 기능 frontend/src/apis/application/duplicateApplication.ts, frontend/src/hooks/queries/application/useDuplicateApplication.ts, frontend/src/pages/AdminPage/components/ApplicationRow/ApplicationRowItem.tsx, frontend/src/pages/AdminPage/tabs/ApplicationListTab/ApplicationListTab.tsx, frontend/src/pages/AdminPage/tabs/ApplicationListTab/ApplicationMenu.tsx |
duplicateApplication API와 useDuplicateApplication 훅 추가. ApplicationMenu의 편집 항목을 복제로 교체하고, onDuplicate 콜백을 Row/리스트에 전달해 복제 흐름(핸들러, 경고, 캐시 무효화) 구현. |
ClubDetailPage 개선 frontend/src/pages/ClubDetailPage/ClubDetailPage.tsx, .../ClubDetailPage.styles.ts, .../components/ClubIntroContent/ClubIntroContent.tsx, .../ClubIntroContent.styles.ts, .../ClubFeed.styles.ts, frontend/src/pages/ClubDetailPage/mockData.ts |
탭 상태를 URL 검색 파라미터로 관리하도록 변경(TAB_TYPE 추가/내보냄). 테마 기반 트랜지션 적용 및 FAQ AnswerContainer에 $isOpen prop으로 CSS 전환 제어. mockData 파일 삭제. |
테마 및 유틸리티 frontend/src/styles/theme/transitions.ts, frontend/src/styles/theme/index.ts, frontend/src/utils/formatRelativeDateTime.ts, frontend/src/utils/formatRelativeDateTime.test.ts |
전역 transitions 상수(기간·완화) 추가 및 테마에 포함. formatRelativeDateTime 유틸 추가 및 광범위 단위테스트 추가. |
Admin · FAQ 리팩토링 frontend/src/pages/AdminPage/tabs/ClubIntroEditTab/**, frontend/src/pages/AdminPage/tabs/ClubIntroTab/** |
ClubIntroTab → ClubIntroEditTab로 변경(경로·내보내기·레이블 수정). 기존 FAQEditor 파일 삭제 및 새 위치에 FAQEditor 컴포넌트 추가(입력 포커스·문자수 제한·추가/삭제 기능). FAQ 타입에 id?: string 추가. |
빌드·설정·무시 규칙 frontend/config/vite.config.ts, frontend/package.json, frontend/tsconfig.json, .gitignore, frontend/.cursorrules |
Vite manualChunks 전략 추가(라이브러리별 청킹). rollup-plugin-visualizer devDependency 추가(구성에서 미사용 임포트 포함). tsconfig에 jest 타입 추가. .gitignore에 .cursor/ 추가. 디자인 가이드(frontend/.cursorrules) 삭제. |
스타일·경로·소소 변경 frontend/src/pages/MainPage/MainPage.tsx, frontend/src/pages/MainPage/components/ClubCard/ClubCard.tsx, frontend/src/pages/MainPage/components/ClubLogo/ClubLogo.tsx, frontend/src/pages/ClubDetailPage/components/ClubFeed/ClubFeed.styles.ts, frontend/src/pages/ClubDetailPage/components/ClubIntroContent/ClubIntroContent.styles.ts, frontend/src/pages/ClubDetailPage/ClubDetailPage.styles.ts |
totalCount 폴백을 nullish 병합 연산자로 변경, ClubLogo 경로 및 테마 색상 적용, 여러 컴포넌트에서 하드코딩된 전환값을 테마 transitions로 대체 및 몇몇 스타일에 애니메이션 추가. |
타입·경량 변경 frontend/src/types/club.ts, frontend/src/constants/eventName.ts |
FAQ.id?: string 추가, RecruitmentStatus 표기 변경(단일 라인), PAGE_VIEW에 CLUB_INTRO_EDIT_PAGE 추가. |
Sequence Diagram(s)
sequenceDiagram
participant User as 사용자
participant ApplicationListTab as ApplicationListTab
participant ApplicationMenu as ApplicationMenu
participant Hook as useDuplicateApplication
participant API as duplicateApplication API
participant QueryClient as React Query
User->>ApplicationListTab: 복제 메뉴 열고 복제 클릭
ApplicationListTab->>ApplicationMenu: onDuplicate(formId)
ApplicationMenu->>Hook: mutate(formId)
Hook->>API: POST /applications/{formId}/duplicate
API-->>Hook: 성공 응답 (새 리소스)
Hook->>QueryClient: invalidate ['applicationForm']
QueryClient-->>Hook: 캐시 갱신 완료
Hook-->>ApplicationListTab: 성공 콜백 (메뉴 닫기, 알림)
ApplicationListTab-->>User: 성공 알림 표시
sequenceDiagram
participant User as 사용자
participant ClubDetailPage as ClubDetailPage
participant URLParams as URL 검색 파라미터
participant Analytics as Mixpanel
participant TabContent as 탭 콘텐츠
User->>ClubDetailPage: 탭 클릭 (예: photos)
ClubDetailPage->>URLParams: set tab=photos
ClubDetailPage->>Analytics: mixpanel.track(tab 변경)
URLParams-->>ClubDetailPage: 변경된 파라미터 반영
ClubDetailPage->>TabContent: 해당 탭 렌더링
TabContent-->>User: 트랜지션과 함께 콘텐츠 표시
Estimated code review effort
🎯 3 (Moderate) | ⏱️ ~25 분
Possibly related PRs
- [feature] 지원서 복제 기능 구현 #1022: duplicateApplication API, useDuplicateApplication 훅 및 ApplicationMenu/ApplicationListTab의 onDuplicate 연결과 직접적으로 코드 수준에서 겹침.
- [feature] 상세페이지 콘텐츠에 애니메이션을 추가한다 #1015: ClubDetailPage 탭/애니메이션·FAQ·테마 전환 변경과 mockData 삭제 등 유사한 ClubDetail 변경이 포함되어 있음.
- [feature] 대표 지원서 모아보기를 추가한다. #878: AdminPage의 ApplicationRow/ ApplicationMenu 관련 변경(행별 액션·핸들러 조정)과 부분적 중복이 있음.
Suggested reviewers
- lepitaaar
- oesnuj
- suhyun113
🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (1 warning, 1 inconclusive)
| Check name | Status | Explanation | Resolution |
|---|---|---|---|
| Docstring Coverage | Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. | Write docstrings for the functions missing them to satisfy the coverage threshold. | |
| Title check | ❓ Inconclusive | The PR title '[release] FE v1.1.12' is vague and generic, using a standard release tag format that does not clearly convey the specific changes included in this comprehensive release. | Consider adding more specificity to describe key changes (e.g., '[release] FE v1.1.12: App duplication, FAQ accordion, and UI improvements') or ensure PR description provides substantive details about the release scope. |
✅ Passed checks (1 passed)
| Check name | Status | Explanation |
|---|---|---|
| Description Check | ✅ Passed | Check skipped - CodeRabbit’s high-level summary is enabled. |
✏️ Tip: You can configure your own custom pre-merge checks in the settings.
✨ Finishing touches
- 📝 Generate docstrings
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.
Comment @coderabbitai help to get the list of available commands and usage tips.
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
frontend/src/pages/AdminPage/tabs/ApplicationListTab/ApplicationListTab.tsx (1)
131-141: 렌더링 내부에서 styled-components 정의 - 성능 문제.
ActiveListBody와ActiveApplicationRow가 컴포넌트 함수 내부에서 정의되어 있어, 매 렌더링마다 새로운 컴포넌트 참조가 생성됩니다. 이는 불필요한 리렌더링과 성능 저하를 유발할 수 있습니다.♻️ styled-components를 컴포넌트 외부로 이동
+const ActiveListBody = styled(Styled.ApplicationList)` + border-top-left-radius: 0; +`; + +const ActiveApplicationRow = styled(ApplicationRowItem)` + &:hover { + background-color: #f2f2f2; + &:first-child { + border-top-right-radius: 20px; + } + } +`; + const ApplicationListTab = () => { const queryClient = useQueryClient(); // ... rest of the component - const ActiveListBody = styled(Styled.ApplicationList)` - border-top-left-radius: 0; - `; - const ActiveApplicationRow = styled(ApplicationRowItem)` - &:hover { - background-color: #f2f2f2; - &:first-child { - border-top-right-radius: 20px; - } - } - `;
🤖 Fix all issues with AI agents
In @frontend/src/pages/ClubDetailPage/ClubDetailPage.tsx:
- Line 1: The import line in ClubDetailPage.tsx currently brings in useEffect
and useState which are unused; update the import from 'react' to only include
the hooks actually used (e.g., keep useCallback) or remove the entire import if
no hooks are needed, and run a quick lint/compile to ensure there are no
remaining references to useEffect/useState.
🧹 Nitpick comments (6)
frontend/src/pages/ClubDetailPage/ClubDetailPage.styles.ts (1)
71-85: 매직 넘버를 상수로 추출하는 것을 권장합니다.
translateY(10px)의10px은 매직 넘버입니다. 코딩 가이드라인에 따라 명확한 의미를 가진 상수로 추출하는 것이 좋습니다.♻️ 제안된 수정
transitions.ts에 애니메이션 거리 상수를 추가하거나, 로컬 상수를 정의할 수 있습니다:+const FADE_IN_TRANSLATE_Y = '10px'; + export const TabContent = styled.div` animation: fadeIn ${transitions.duration.normal} ${transitions.easing.easeInOut}; @keyframes fadeIn { from { opacity: 0; - transform: translateY(10px); + transform: translateY(${FADE_IN_TRANSLATE_Y}); } to { opacity: 1; transform: translateY(0); } } `;frontend/src/pages/ClubDetailPage/components/ClubIntroContent/ClubIntroContent.styles.ts (1)
191-199:max-height: 500px값에 대한 검토가 필요합니다.
max-height를 이용한 애니메이션은 일반적인 패턴이지만,500px이라는 고정값은 두 가지 잠재적 문제가 있습니다:
- FAQ 답변이 500px를 초과할 경우 내용이 잘릴 수 있습니다
- 답변이 짧을 경우 애니메이션 타이밍이 비례적으로 빨라 보일 수 있습니다
가능하다면 예상되는 최대 콘텐츠 높이를 기반으로 값을 설정하거나, 상수로 추출하여 의도를 명확히 하는 것을 권장합니다.
♻️ 상수 추출 제안
+const FAQ_MAX_HEIGHT = '500px'; + export const AnswerContainer = styled.div<{ $isOpen: boolean }>` - max-height: ${({ $isOpen }) => ($isOpen ? '500px' : '0')}; + max-height: ${({ $isOpen }) => ($isOpen ? FAQ_MAX_HEIGHT : '0')}; opacity: ${({ $isOpen }) => ($isOpen ? '1' : '0')}; // ... `;frontend/src/apis/application/duplicateApplication.ts (1)
4-21: 반환 타입 명시 및 중복 에러 로깅 검토
반환 타입 명시: 코딩 가이드라인에 따라 함수의 반환 타입을 명시적으로 선언하면 일관성과 타입 안전성이 향상됩니다.
중복 에러 로깅: 이 함수에서
console.error로 에러를 로깅하고,useDuplicateApplication훅의onError에서도 동일한 에러를 다시 로깅합니다. 의도된 것인지 확인해 주세요.♻️ 반환 타입 명시 제안
-export const duplicateApplication = async (applicationFormId: string) => { +export const duplicateApplication = async (applicationFormId: string): Promise<unknown> => {또는 API 응답 타입이 정의되어 있다면 해당 타입을 사용하세요.
frontend/config/vite.config.ts (1)
4-4: 사용되지 않는 import 제거
visualizer가 import되었지만 plugins 배열에 추가되지 않아 실제로 사용되지 않습니다. 번들 분석이 필요하지 않다면 import를 제거해 주세요.♻️ 수정 제안
import react from '@vitejs/plugin-react'; import { defineConfig } from 'vite'; import tsconfigPaths from 'vite-tsconfig-paths'; -import { visualizer } from 'rollup-plugin-visualizer';frontend/src/hooks/queries/application/useDuplicateApplication.ts (1)
7-18: mutationFn 간소화 제안훅의 구조가 적절하며 cache invalidation도 올바르게 처리됩니다.
mutationFn은 래핑 없이 직접 참조할 수 있습니다.♻️ 간소화 제안
return useMutation({ - mutationFn: (applicationFormId: string) => - duplicateApplication(applicationFormId), + mutationFn: duplicateApplication, onSuccess: () => {frontend/src/pages/AdminPage/tabs/ApplicationListTab/ApplicationMenu.tsx (1)
40-41: 아이콘 선택 및 optional 핸들러 검토 필요.
아이콘 의미:
Pencil아이콘은 일반적으로 "편집"을 의미합니다. "복제하기" 기능에는 복사 아이콘이 더 직관적일 수 있습니다.Optional 핸들러 처리:
onDuplicate이 optional이지만,onClick={onDuplicate}에서 guard 없이 직접 전달되고 있습니다.onDuplicate이undefined일 경우 클릭해도 아무 동작이 없어 사용자에게 혼란을 줄 수 있습니다.♻️ Optional 핸들러 guard 추가 제안
- <Styled.MenuItem onClick={onDuplicate}> + <Styled.MenuItem onClick={onDuplicate} disabled={!onDuplicate}>또는 조건부 렌더링:
+ {onDuplicate && ( + <> + <Styled.Separator /> <Styled.MenuItem onClick={onDuplicate}> <Styled.MenuIcon src={Pencil} /> 지원서 복제하기 </Styled.MenuItem> + </> + )}
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Jira integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (23)
.gitignorefrontend/.cursorrulesfrontend/config/vite.config.tsfrontend/package.jsonfrontend/src/apis/application/duplicateApplication.tsfrontend/src/hooks/queries/application/useDuplicateApplication.tsfrontend/src/pages/AdminPage/components/ApplicationRow/ApplicationRowItem.tsxfrontend/src/pages/AdminPage/tabs/ApplicationListTab/ApplicationListTab.tsxfrontend/src/pages/AdminPage/tabs/ApplicationListTab/ApplicationMenu.tsxfrontend/src/pages/ClubDetailPage/ClubDetailPage.styles.tsfrontend/src/pages/ClubDetailPage/ClubDetailPage.tsxfrontend/src/pages/ClubDetailPage/components/ClubFeed/ClubFeed.styles.tsfrontend/src/pages/ClubDetailPage/components/ClubIntroContent/ClubIntroContent.styles.tsfrontend/src/pages/ClubDetailPage/components/ClubIntroContent/ClubIntroContent.tsxfrontend/src/pages/ClubDetailPage/mockData.tsfrontend/src/pages/MainPage/MainPage.tsxfrontend/src/pages/MainPage/components/ClubCard/ClubCard.tsxfrontend/src/pages/MainPage/components/ClubLogo/ClubLogo.tsxfrontend/src/styles/theme/index.tsfrontend/src/styles/theme/transitions.tsfrontend/src/utils/formatRelativeDateTime.test.tsfrontend/src/utils/formatRelativeDateTime.tsfrontend/tsconfig.json
💤 Files with no reviewable changes (3)
- frontend/.cursorrules
- frontend/src/pages/ClubDetailPage/mockData.ts
- frontend/src/pages/MainPage/MainPage.tsx
🧰 Additional context used
📓 Path-based instructions (3)
frontend/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (frontend/.cursorrules)
frontend/**/*.{ts,tsx,js,jsx}: Replace magic numbers with named constants for clarity
Replace complex/nested ternaries withif/elseor IIFEs for readability
Assign complex boolean conditions to named variables for explicit meaning
Avoid hidden side effects; functions should only perform actions implied by their signature (Single Responsibility Principle)
Use unique and descriptive names for custom wrappers/functions to avoid ambiguity
Define constants near related logic or ensure names link them clearly to avoid silent failures
Break down broad state management into smaller, focused hooks/contexts to reduce coupling
Files:
frontend/src/pages/ClubDetailPage/components/ClubFeed/ClubFeed.styles.tsfrontend/src/hooks/queries/application/useDuplicateApplication.tsfrontend/src/pages/ClubDetailPage/ClubDetailPage.tsxfrontend/src/utils/formatRelativeDateTime.test.tsfrontend/src/utils/formatRelativeDateTime.tsfrontend/src/pages/ClubDetailPage/components/ClubIntroContent/ClubIntroContent.tsxfrontend/src/styles/theme/transitions.tsfrontend/src/pages/ClubDetailPage/ClubDetailPage.styles.tsfrontend/src/styles/theme/index.tsfrontend/src/pages/ClubDetailPage/components/ClubIntroContent/ClubIntroContent.styles.tsfrontend/src/pages/MainPage/components/ClubCard/ClubCard.tsxfrontend/src/pages/AdminPage/tabs/ApplicationListTab/ApplicationMenu.tsxfrontend/src/pages/MainPage/components/ClubLogo/ClubLogo.tsxfrontend/src/apis/application/duplicateApplication.tsfrontend/config/vite.config.tsfrontend/src/pages/AdminPage/components/ApplicationRow/ApplicationRowItem.tsxfrontend/src/pages/AdminPage/tabs/ApplicationListTab/ApplicationListTab.tsx
frontend/**/*.{ts,tsx}
📄 CodeRabbit inference engine (frontend/.cursorrules)
Use consistent return types for similar functions/hooks
Files:
frontend/src/pages/ClubDetailPage/components/ClubFeed/ClubFeed.styles.tsfrontend/src/hooks/queries/application/useDuplicateApplication.tsfrontend/src/pages/ClubDetailPage/ClubDetailPage.tsxfrontend/src/utils/formatRelativeDateTime.test.tsfrontend/src/utils/formatRelativeDateTime.tsfrontend/src/pages/ClubDetailPage/components/ClubIntroContent/ClubIntroContent.tsxfrontend/src/styles/theme/transitions.tsfrontend/src/pages/ClubDetailPage/ClubDetailPage.styles.tsfrontend/src/styles/theme/index.tsfrontend/src/pages/ClubDetailPage/components/ClubIntroContent/ClubIntroContent.styles.tsfrontend/src/pages/MainPage/components/ClubCard/ClubCard.tsxfrontend/src/pages/AdminPage/tabs/ApplicationListTab/ApplicationMenu.tsxfrontend/src/pages/MainPage/components/ClubLogo/ClubLogo.tsxfrontend/src/apis/application/duplicateApplication.tsfrontend/config/vite.config.tsfrontend/src/pages/AdminPage/components/ApplicationRow/ApplicationRowItem.tsxfrontend/src/pages/AdminPage/tabs/ApplicationListTab/ApplicationListTab.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/ClubDetailPage/ClubDetailPage.tsxfrontend/src/pages/ClubDetailPage/components/ClubIntroContent/ClubIntroContent.tsxfrontend/src/pages/MainPage/components/ClubCard/ClubCard.tsxfrontend/src/pages/AdminPage/tabs/ApplicationListTab/ApplicationMenu.tsxfrontend/src/pages/MainPage/components/ClubLogo/ClubLogo.tsxfrontend/src/pages/AdminPage/components/ApplicationRow/ApplicationRowItem.tsxfrontend/src/pages/AdminPage/tabs/ApplicationListTab/ApplicationListTab.tsx
🧠 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/**/*.{ts,tsx,js,jsx} : Break down broad state management into smaller, focused hooks/contexts to reduce coupling
Applied to files:
frontend/src/hooks/queries/application/useDuplicateApplication.tsfrontend/src/pages/ClubDetailPage/ClubDetailPage.tsxfrontend/config/vite.config.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/ClubDetailPage/ClubDetailPage.tsxfrontend/src/pages/MainPage/components/ClubCard/ClubCard.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/ClubDetailPage/ClubDetailPage.tsxfrontend/src/styles/theme/transitions.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/ClubDetailPage/ClubDetailPage.tsxfrontend/src/pages/MainPage/components/ClubCard/ClubCard.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/ClubDetailPage/ClubDetailPage.tsx
📚 Learning: 2025-11-15T08:13:58.854Z
Learnt from: lepitaaar
Repo: Moadong/moadong PR: 844
File: frontend/package.json:97-97
Timestamp: 2025-11-15T08:13:58.854Z
Learning: In frontend/package.json, the `tough-cookie` package is a dependency of `msw` (Mock Service Worker) used for cookie parsing and management. Both packages should be in devDependencies together.
Applied to files:
frontend/package.json
📚 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/ClubDetailPage/components/ClubIntroContent/ClubIntroContent.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} : Define constants near related logic or ensure names link them clearly to avoid silent failures
Applied to files:
frontend/src/styles/theme/transitions.ts
🧬 Code graph analysis (10)
frontend/src/pages/ClubDetailPage/components/ClubFeed/ClubFeed.styles.ts (1)
frontend/src/styles/theme/transitions.ts (1)
transitions(1-13)
frontend/src/hooks/queries/application/useDuplicateApplication.ts (1)
frontend/src/apis/application/duplicateApplication.ts (1)
duplicateApplication(4-22)
frontend/src/pages/ClubDetailPage/ClubDetailPage.tsx (2)
frontend/src/hooks/queries/club/useGetClubDetail.ts (1)
useGetClubDetail(6-20)frontend/src/constants/eventName.ts (2)
PAGE_VIEW(90-104)USER_EVENT(1-44)
frontend/src/utils/formatRelativeDateTime.test.ts (1)
frontend/src/utils/formatRelativeDateTime.ts (1)
formatRelativeDateTime(6-19)
frontend/src/pages/ClubDetailPage/ClubDetailPage.styles.ts (2)
frontend/src/styles/theme/transitions.ts (1)
transitions(1-13)frontend/src/styles/mediaQuery.ts (1)
media(8-14)
frontend/src/pages/ClubDetailPage/components/ClubIntroContent/ClubIntroContent.styles.ts (2)
frontend/src/styles/theme/transitions.ts (1)
transitions(1-13)frontend/src/styles/mediaQuery.ts (1)
media(8-14)
frontend/src/pages/MainPage/components/ClubLogo/ClubLogo.tsx (1)
frontend/src/styles/theme/colors.ts (1)
colors(1-81)
frontend/src/apis/application/duplicateApplication.ts (1)
frontend/src/apis/auth/secureFetch.ts (1)
secureFetch(3-41)
frontend/src/pages/AdminPage/components/ApplicationRow/ApplicationRowItem.tsx (1)
frontend/src/utils/formatRelativeDateTime.ts (1)
formatRelativeDateTime(6-19)
frontend/src/pages/AdminPage/tabs/ApplicationListTab/ApplicationListTab.tsx (4)
frontend/src/hooks/queries/application/useGetApplicationlist.ts (1)
useGetApplicationlist(4-10)frontend/src/hooks/queries/application/useDeleteApplication.ts (1)
useDeleteApplication(4-19)frontend/src/apis/application/duplicateApplication.ts (1)
duplicateApplication(4-22)frontend/src/hooks/queries/application/useDuplicateApplication.ts (1)
useDuplicateApplication(4-19)
🔇 Additional comments (21)
.gitignore (1)
3-3: LGTM!Cursor 에디터 관련 파일을 무시 목록에 추가한 것은 적절한 변경사항입니다. 기존의
.idea,.vscode/와 일관된 패턴을 따르고 있습니다.frontend/src/pages/MainPage/components/ClubCard/ClubCard.tsx (1)
8-8: LGTM!ClubLogo 컴포넌트의 import 경로 변경이 적절합니다. MainPage 스코프로의 리팩토링과 일관성 있게 반영되었습니다.
frontend/src/pages/ClubDetailPage/components/ClubIntroContent/ClubIntroContent.tsx (1)
127-129: LGTM!조건부 렌더링에서 항상 렌더링하는 방식으로 변경하여 CSS 트랜지션(max-height, opacity 등)이 정상적으로 동작할 수 있게 되었습니다.
$isOpenprop을 통한 가시성 제어가 적절합니다.frontend/src/pages/MainPage/components/ClubLogo/ClubLogo.tsx (1)
2-2: 테마 토큰 적용이 잘 되었습니다.하드코딩된 색상 값에서 중앙 집중화된 테마 토큰(
colors.gray[400],colors.base.white)으로 변경하여 일관성과 유지보수성이 향상되었습니다.참고: 배경색이 기존
#efefef(연회색)에서#FFFFFF(흰색)으로 변경되었는데, 이는 의도된 디자인 변경인지 확인 부탁드립니다.Also applies to: 29-31
frontend/src/styles/theme/index.ts (1)
2-2: LGTM!
transitions모듈이 중앙 테마 객체에 깔끔하게 통합되었습니다.as const단언을 통해 타입 안전성이 보장되며, 컴포넌트 전반에서 일관된 트랜지션 값을 사용할 수 있게 되었습니다.Also applies to: 8-8
frontend/src/pages/ClubDetailPage/components/ClubFeed/ClubFeed.styles.ts (1)
4-4: LGTM!하드코딩된 트랜지션 값(
0.3s ease)을 테마 토큰(transitions.duration.normal,transitions.easing.ease)으로 교체하여 코드베이스 전반의 애니메이션 일관성이 향상되었습니다. ClubIntroContent, ClubDetailPage 등 다른 컴포넌트들과 동일한 패턴을 따르고 있습니다.Also applies to: 26-26
frontend/src/pages/ClubDetailPage/ClubDetailPage.styles.ts (1)
61-63: LGTM - 테마 전환 값 활용이 좋습니다.
transitions.duration.normal과transitions.easing.easeInOut를 활용하여 일관된 애니메이션 타이밍을 적용한 점이 좋습니다.frontend/src/pages/ClubDetailPage/components/ClubIntroContent/ClubIntroContent.styles.ts (1)
185-186: LGTM - 테마 전환 값 적용이 좋습니다.ArrowIcon의 회전 애니메이션에 테마 전환 값을 적용한 점이 좋습니다.
frontend/src/styles/theme/transitions.ts (1)
1-13: LGTM - 잘 구조화된 전환 상수입니다.
as const를 사용하여 타입 안전성을 확보하고, 명확한 네이밍으로 재사용 가능한 전환 값을 제공합니다. 코드베이스 전반에 걸쳐 일관된 애니메이션을 적용할 수 있는 좋은 기반입니다.frontend/src/pages/ClubDetailPage/ClubDetailPage.tsx (3)
16-21: LGTM - 상수와 타입 정의가 좋습니다.매직 스트링을
TAB_TYPE상수로 대체하고as const를 사용하여 타입 안전성을 확보한 점이 좋습니다. 코딩 가이드라인을 잘 따르고 있습니다.
26-32: URL 기반 탭 상태 관리 구현이 좋습니다.
useSearchParams를 활용하여 탭 상태를 URL에 반영하면 다음과 같은 이점이 있습니다:
- 페이지 공유 시 특정 탭으로 직접 이동 가능
- 브라우저 히스토리 지원
유효성 검사 로직(
Object.values(TAB_TYPE).includes(tabParam))으로 잘못된 쿼리 파라미터에 대한 방어도 적절합니다.
41-49: LGTM - useCallback 메모이제이션 적용이 적절합니다.핸들러 함수들을
useCallback으로 메모이제이션하여 불필요한 리렌더링을 방지하고, 의존성 배열도 올바르게 설정되어 있습니다.frontend/package.json (1)
91-91: 사용되지 않는 의존성 확인 필요
rollup-plugin-visualizer가 devDependencies에 추가되었지만,vite.config.ts에서 import만 되어 있고 실제로 사용되지 않고 있습니다. 번들 시각화를 위해 의도적으로 남겨둔 것인지 확인해 주세요. 사용하지 않는다면 제거를 권장합니다.frontend/tsconfig.json (1)
19-19: LGTM!Jest 타입 정의를 추가하여 테스트 파일에서
jest.useFakeTimers(),jest.setSystemTime()등의 Jest 전역 함수를 TypeScript가 인식할 수 있도록 올바르게 설정되었습니다.frontend/config/vite.config.ts (1)
16-53: manualChunks 구현 승인 및 경로 매칭 관련 참고사항라이브러리별 청크 분리 전략이 잘 구성되어 있습니다. react-vendor, state, analytics 등 논리적인 그룹핑이 적절합니다.
참고: PNPM 환경에서는
node_modules/.pnpm/구조로 인해id.includes("node_modules/react/")같은 패턴이 예상대로 동작하지 않을 수 있습니다. 현재 npm/yarn을 사용 중이라면 문제없지만, PNPM 전환 시 정규식 기반 매칭을 검토해 주세요.frontend/src/utils/formatRelativeDateTime.test.ts (1)
1-99: LGTM!테스트 스위트가 잘 구성되어 있습니다:
jest.useFakeTimers()를 활용한 시간 제어- 오늘/과거/미래/엣지 케이스를 포괄하는 테스트 커버리지
- 정규식을 사용한 유연한 로케일 출력 검증
frontend/src/utils/formatRelativeDateTime.ts (1)
6-18: 구현 승인 및 잘못된 입력 처리 관련 참고사항함수 구현이 깔끔하고 JSDoc 문서화가 잘 되어 있습니다.
Intl.DateTimeFormat을 활용한 로케일 포맷팅도 적절합니다.참고: 잘못된
dateTimeString이 전달될 경우"Invalid Date"문자열이 반환됩니다. API 응답이 타입화되어 있고 항상 유효한 날짜가 보장된다면 문제없지만, 방어적 코딩이 필요하다면 유효성 검사 추가를 고려해 주세요.frontend/src/pages/AdminPage/components/ApplicationRow/ApplicationRowItem.tsx (1)
4-4: LGTM! 복제 기능이 잘 통합되었습니다.
onDuplicateprop이onDelete와 동일한 패턴으로 추가되어 일관성이 유지됩니다.formatRelativeDateTime유틸리티를 사용하여 날짜 포맷팅 로직이 적절히 분리되었습니다.ApplicationMenu로의 props 전달이 올바르게 구현되었습니다.Also applies to: 17-17, 31-31, 48-48, 65-65
frontend/src/pages/AdminPage/tabs/ApplicationListTab/ApplicationMenu.tsx (1)
7-10: LGTM! 상수 추출이 잘 되었습니다.코딩 가이드라인에 따라 매직 문자열을 명명된 상수로 추출하여 가독성과 유지보수성이 향상되었습니다.
frontend/src/pages/AdminPage/tabs/ApplicationListTab/ApplicationListTab.tsx (2)
53-63: LGTM! 복제 핸들러가 잘 구현되었습니다.
- 삭제 핸들러와 일관된 패턴으로 구현되었습니다.
- 성공/실패 시 사용자 피드백(alert)이 적절히 제공됩니다.
- 성공 시 메뉴가 닫히는 동작도 올바릅니다.
166-166: LGTM! onDuplicate prop 전달이 일관되게 적용되었습니다.활성 목록과 그룹별 목록 모두에
onDuplicate핸들러가 올바르게 전달되어, 어디서든 지원서 복제가 가능합니다.Also applies to: 223-223
| @@ -1,5 +1,5 @@ | |||
| import { useState } from 'react'; | |||
| import { useParams } from 'react-router-dom'; | |||
| import { useCallback, useEffect, useState } from 'react'; | |||
There was a problem hiding this comment.
사용하지 않는 import를 제거하세요.
useEffect와 useState가 import되었지만 현재 코드에서 사용되지 않습니다.
🧹 제안된 수정
-import { useCallback, useEffect, useState } from 'react';
+import { useCallback } from 'react';📝 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.
| import { useCallback, useEffect, useState } from 'react'; | |
| import { useCallback } from 'react'; |
🤖 Prompt for AI Agents
In @frontend/src/pages/ClubDetailPage/ClubDetailPage.tsx at line 1, The import
line in ClubDetailPage.tsx currently brings in useEffect and useState which are
unused; update the import from 'react' to only include the hooks actually used
(e.g., keep useCallback) or remove the entire import if no hooks are needed, and
run a quick lint/compile to ensure there are no remaining references to
useEffect/useState.
[fix] FAQ 수정, 삭제 버그 해결
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (6)
frontend/src/pages/ClubDetailPage/components/ClubIntroContent/ClubIntroContent.styles.ts (1)
191-204:max-height: 500px매직 넘버가 콘텐츠를 잘라낼 수 있습니다.FAQ 답변이 500px를 초과하면 콘텐츠가 잘립니다. 코딩 가이드라인에 따라 매직 넘버는 명명된 상수로 대체하는 것이 좋습니다. 또한, 긴 콘텐츠에 대응하기 위해 더 큰 값을 사용하거나 다른 접근법을 고려해 보세요.
♻️ 제안: 상수로 추출하고 충분한 여유 값 사용
+const MAX_ANSWER_HEIGHT = '1000px'; // FAQ 답변의 최대 높이 + export const AnswerContainer = styled.div<{ $isOpen: boolean }>` - max-height: ${({ $isOpen }) => ($isOpen ? '500px' : '0')}; + max-height: ${({ $isOpen }) => ($isOpen ? MAX_ANSWER_HEIGHT : '0')}; opacity: ${({ $isOpen }) => ($isOpen ? '1' : '0')};frontend/src/pages/AdminPage/tabs/ApplicationEditTab/ApplicationEditTab.tsx (1)
368-370: 힌트 텍스트에 단축 URL 안내 추가를 고려해보세요.
externalApplicationUrlAllowed상수에naver.me단축 URL도 포함되어 있지만 힌트 텍스트에는 명시되어 있지 않습니다. 사용자가 단축 URL을 사용할 수 있다는 것을 명확히 알 수 있도록 힌트를 보완하면 좋을 것 같습니다.💡 힌트 텍스트 개선 제안
<Styled.ExternalApplicationFormHint> - 현재 구글폼, 네이버폼, 에브리타임 링크만 제출가능합니다. + 현재 구글폼, 네이버폼(단축 URL 포함), 에브리타임 링크만 제출가능합니다. </Styled.ExternalApplicationFormHint>frontend/src/pages/AdminPage/tabs/ClubIntroEditTab/ClubIntroEditTab.tsx (1)
101-139:maxLength값을 명명된 상수로 추출하는 것을 고려해 보세요.코딩 가이드라인에 따라 매직 넘버를 명명된 상수로 교체하면 가독성과 유지보수성이 향상됩니다.
♻️ 제안하는 리팩토링
+const MAX_LENGTH = { + INTRO_DESCRIPTION: 200, + ACTIVITY_DESCRIPTION: 500, + IDEAL_CANDIDATE: 500, + BENEFITS: 500, +} as const; + const ClubIntroEditTab = () => { // ...그런 다음 각
CustomTextArea에서 사용:<CustomTextArea label='동아리를 소개할게요' // ... maxLength={MAX_LENGTH.INTRO_DESCRIPTION} />frontend/src/pages/AdminPage/tabs/ClubIntroEditTab/components/FAQEditor/FAQEditor.tsx (3)
15-24: ID 생성 로직을 유틸리티 함수로 추출하는 것을 고려해주세요.
Date.now()+Math.random()조합이 라인 20과 28에서 중복 사용되고 있습니다. ID 생성 로직을 별도 함수로 추출하거나, 더 안정적인crypto.randomUUID()를 사용하는 것을 권장합니다.♻️ 제안하는 리팩토링
+const generateFAQId = () => `faq-${crypto.randomUUID()}`; + const FAQEditor = ({ faqs, onChange }: FAQEditorProps) => { const [shouldFocusLast, setShouldFocusLast] = useState(false); const questionInputRefs = useRef<Record<string, HTMLInputElement | null>>({}); useEffect(() => { const hasAnyMissingId = faqs.some((faq) => !faq.id); if (hasAnyMissingId) { const faqsWithIds = faqs.map((faq) => ({ ...faq, - id: faq.id || `faq-${Date.now()}-${Math.random()}`, + id: faq.id || generateFAQId(), })); onChange(faqsWithIds); } }, [faqs, onChange]); const handleAddFAQ = () => { const newFAQ: FAQ = { - id: `faq-${Date.now()}-${Math.random()}`, + id: generateFAQId(), question: '', answer: '', };
40-52:handleUpdateQuestion과handleUpdateAnswer를 단일 함수로 통합할 수 있습니다.두 함수의 로직이 거의 동일합니다. 선택적으로 하나의 제네릭 핸들러로 통합할 수 있습니다.
♻️ 제안하는 리팩토링
- const handleUpdateQuestion = (id: string, value: string) => { - const updatedFAQs = faqs.map((faq) => - faq.id === id ? { ...faq, question: value } : faq, - ); - onChange(updatedFAQs); - }; - - const handleUpdateAnswer = (id: string, value: string) => { - const updatedFAQs = faqs.map((faq) => - faq.id === id ? { ...faq, answer: value } : faq, - ); - onChange(updatedFAQs); - }; + const handleUpdateFAQ = (id: string, field: 'question' | 'answer', value: string) => { + const updatedFAQs = faqs.map((faq) => + faq.id === id ? { ...faq, [field]: value } : faq, + ); + onChange(updatedFAQs); + };사용 시:
onChange={(event) => handleUpdateFAQ(faq.id, 'question', event.target.value)} onChange={(event) => handleUpdateFAQ(faq.id, 'answer', event.target.value)}
99-114: 매직 넘버를 명명된 상수로 추출해주세요.코딩 가이드라인에 따라, 문자 수 제한값
100과300을 명명된 상수로 추출하면 가독성과 유지보수성이 향상됩니다.♻️ 제안하는 리팩토링
컴포넌트 상단에 상수 정의:
const QUESTION_MAX_LENGTH = 100; const ANSWER_MAX_LENGTH = 300;사용 예시:
<Styled.QuestionInput ref={(element) => { questionInputRefs.current[faq.id] = element; }} placeholder='질문을 입력하세요' value={faq.question} onChange={(event) => handleUpdateQuestion(faq.id, event.target.value) } - maxLength={100} + maxLength={QUESTION_MAX_LENGTH} /> <Styled.AnswerTextArea placeholder='답변을 입력하세요' value={faq.answer} onChange={(event) => handleUpdateAnswer(faq.id, event.target.value) } - maxLength={300} + maxLength={ANSWER_MAX_LENGTH} /> <Styled.CharCount> - 질문: {faq.question.length}/100 | 답변: {faq.answer.length} - /300 + 질문: {faq.question.length}/{QUESTION_MAX_LENGTH} | 답변: {faq.answer.length} + /{ANSWER_MAX_LENGTH} </Styled.CharCount>코딩 가이드라인 기준으로, 명확성을 위해 매직 넘버를 명명된 상수로 교체해야 합니다.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Jira integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (14)
frontend/src/constants/eventName.tsfrontend/src/pages/AdminPage/AdminRoutes.tsxfrontend/src/pages/AdminPage/tabs/ApplicationEditTab/ApplicationEditTab.tsxfrontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.tsxfrontend/src/pages/AdminPage/tabs/ClubIntroEditTab/ClubIntroEditTab.styles.tsfrontend/src/pages/AdminPage/tabs/ClubIntroEditTab/ClubIntroEditTab.tsxfrontend/src/pages/AdminPage/tabs/ClubIntroEditTab/components/AwardEditor/AwardEditor.styles.tsfrontend/src/pages/AdminPage/tabs/ClubIntroEditTab/components/AwardEditor/AwardEditor.tsxfrontend/src/pages/AdminPage/tabs/ClubIntroEditTab/components/FAQEditor/FAQEditor.styles.tsfrontend/src/pages/AdminPage/tabs/ClubIntroEditTab/components/FAQEditor/FAQEditor.tsxfrontend/src/pages/AdminPage/tabs/ClubIntroTab/components/FAQEditor/FAQEditor.tsxfrontend/src/pages/ClubDetailPage/components/ClubIntroContent/ClubIntroContent.styles.tsfrontend/src/pages/ClubDetailPage/components/ClubIntroContent/ClubIntroContent.tsxfrontend/src/types/club.ts
💤 Files with no reviewable changes (1)
- frontend/src/pages/AdminPage/tabs/ClubIntroTab/components/FAQEditor/FAQEditor.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
- frontend/src/pages/ClubDetailPage/components/ClubIntroContent/ClubIntroContent.tsx
🧰 Additional context used
📓 Path-based instructions (3)
frontend/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (frontend/.cursorrules)
frontend/**/*.{ts,tsx,js,jsx}: Replace magic numbers with named constants for clarity
Replace complex/nested ternaries withif/elseor IIFEs for readability
Assign complex boolean conditions to named variables for explicit meaning
Avoid hidden side effects; functions should only perform actions implied by their signature (Single Responsibility Principle)
Use unique and descriptive names for custom wrappers/functions to avoid ambiguity
Define constants near related logic or ensure names link them clearly to avoid silent failures
Break down broad state management into smaller, focused hooks/contexts to reduce coupling
Files:
frontend/src/constants/eventName.tsfrontend/src/pages/AdminPage/tabs/ClubIntroEditTab/components/FAQEditor/FAQEditor.tsxfrontend/src/pages/AdminPage/tabs/ApplicationEditTab/ApplicationEditTab.tsxfrontend/src/pages/AdminPage/AdminRoutes.tsxfrontend/src/pages/ClubDetailPage/components/ClubIntroContent/ClubIntroContent.styles.tsfrontend/src/pages/AdminPage/tabs/ClubIntroEditTab/components/AwardEditor/AwardEditor.tsxfrontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.tsxfrontend/src/pages/AdminPage/tabs/ClubIntroEditTab/ClubIntroEditTab.tsxfrontend/src/types/club.ts
frontend/**/*.{ts,tsx}
📄 CodeRabbit inference engine (frontend/.cursorrules)
Use consistent return types for similar functions/hooks
Files:
frontend/src/constants/eventName.tsfrontend/src/pages/AdminPage/tabs/ClubIntroEditTab/components/FAQEditor/FAQEditor.tsxfrontend/src/pages/AdminPage/tabs/ApplicationEditTab/ApplicationEditTab.tsxfrontend/src/pages/AdminPage/AdminRoutes.tsxfrontend/src/pages/ClubDetailPage/components/ClubIntroContent/ClubIntroContent.styles.tsfrontend/src/pages/AdminPage/tabs/ClubIntroEditTab/components/AwardEditor/AwardEditor.tsxfrontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.tsxfrontend/src/pages/AdminPage/tabs/ClubIntroEditTab/ClubIntroEditTab.tsxfrontend/src/types/club.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/ClubIntroEditTab/components/FAQEditor/FAQEditor.tsxfrontend/src/pages/AdminPage/tabs/ApplicationEditTab/ApplicationEditTab.tsxfrontend/src/pages/AdminPage/AdminRoutes.tsxfrontend/src/pages/AdminPage/tabs/ClubIntroEditTab/components/AwardEditor/AwardEditor.tsxfrontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.tsxfrontend/src/pages/AdminPage/tabs/ClubIntroEditTab/ClubIntroEditTab.tsx
🧠 Learnings (8)
📓 Common learnings
Learnt from: seongwon030
Repo: Moadong/moadong PR: 195
File: frontend/src/pages/AdminPage/AdminPage.tsx:7-7
Timestamp: 2025-03-19T05:18:07.818Z
Learning: AdminPage.tsx에서 현재 하드코딩된 클럽 ID('67d2e3b9b15c136c6acbf20b')는 로그인 기능 구현 후 동적으로 가져오는 방식으로 수정될 예정입니다.
📚 Learning: 2025-03-19T05:18:07.818Z
Learnt from: seongwon030
Repo: Moadong/moadong PR: 195
File: frontend/src/pages/AdminPage/AdminPage.tsx:7-7
Timestamp: 2025-03-19T05:18:07.818Z
Learning: AdminPage.tsx에서 현재 하드코딩된 클럽 ID('67d2e3b9b15c136c6acbf20b')는 로그인 기능 구현 후 동적으로 가져오는 방식으로 수정될 예정입니다.
Applied to files:
frontend/src/constants/eventName.tsfrontend/src/pages/AdminPage/AdminRoutes.tsxfrontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.tsxfrontend/src/pages/AdminPage/tabs/ClubIntroEditTab/ClubIntroEditTab.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/constants/eventName.tsfrontend/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/tabs/ApplicationEditTab/ApplicationEditTab.tsxfrontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.tsx
📚 Learning: 2025-11-25T14:08:23.253Z
Learnt from: CR
Repo: Moadong/moadong PR: 0
File: frontend/.cursorrules:0-0
Timestamp: 2025-11-25T14:08:23.253Z
Learning: Applies to frontend/**/*.{ts,tsx,js,jsx} : Define constants near related logic or ensure names link them clearly to avoid silent failures
Applied to files:
frontend/src/pages/AdminPage/tabs/ApplicationEditTab/ApplicationEditTab.tsxfrontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.tsx
📚 Learning: 2025-11-25T14:08:23.253Z
Learnt from: CR
Repo: Moadong/moadong PR: 0
File: frontend/.cursorrules:0-0
Timestamp: 2025-11-25T14:08:23.253Z
Learning: Applies to frontend/**/*.{tsx,jsx} : Separate significantly different conditional UI/logic into distinct components
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} : Break down broad state management into smaller, focused hooks/contexts to reduce coupling
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
🧬 Code graph analysis (4)
frontend/src/pages/AdminPage/tabs/ClubIntroEditTab/components/FAQEditor/FAQEditor.tsx (1)
frontend/src/types/club.ts (1)
FAQ(54-58)
frontend/src/pages/ClubDetailPage/components/ClubIntroContent/ClubIntroContent.styles.ts (2)
frontend/src/styles/theme/transitions.ts (1)
transitions(1-13)frontend/src/styles/mediaQuery.ts (1)
media(8-14)
frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.tsx (2)
frontend/src/styles/clubTags.ts (1)
TAG_COLORS(4-14)frontend/src/constants/eventName.ts (1)
PAGE_VIEW(90-105)
frontend/src/pages/AdminPage/tabs/ClubIntroEditTab/ClubIntroEditTab.tsx (1)
frontend/src/constants/eventName.ts (1)
PAGE_VIEW(90-105)
🔇 Additional comments (17)
frontend/src/pages/ClubDetailPage/components/ClubIntroContent/ClubIntroContent.styles.ts (2)
181-189: LGTM!테마 기반 트랜지션 값을 사용하여 매직 넘버를 제거한 좋은 리팩토링입니다. 프로젝트 전반의 일관성을 유지하는 데 도움이 됩니다.
206-228: LGTM!
white-space: pre-line추가로 FAQ 답변의 줄바꿈이 올바르게 보존됩니다.Text컴포넌트(line 58)와 일관된 처리입니다.frontend/src/pages/AdminPage/tabs/ApplicationEditTab/ApplicationEditTab.tsx (3)
23-29: 상수 추출이 잘 되었습니다.허용된 외부 URL 목록을 모듈 레벨 상수로 추출하여 유지보수성이 향상되었습니다. 코딩 가이드라인의 "매직 넘버/문자열을 명명된 상수로 교체" 원칙을 잘 따르고 있습니다.
31-37: 훅 초기화 순서 변경은 문제없습니다.
navigate와queryClient훅은 서로 독립적이므로 순서 변경이 기능에 영향을 주지 않습니다.
137-146: URL 검증 로직이 적절합니다.중앙화된 상수를 사용한 검증 로직이 깔끔합니다.
startsWith를 사용한 접두사 매칭은 현재 사용 사례에 적합합니다.frontend/src/constants/eventName.ts (1)
100-100: LGTM!새로운 페이지 뷰 식별자가 기존 네이밍 컨벤션과 일관성 있게 추가되었습니다.
frontend/src/pages/AdminPage/tabs/ClubIntroEditTab/components/AwardEditor/AwardEditor.tsx (2)
5-5: LGTM!타입 import 경로를 중앙화된
@/types/club으로 변경하여 타입 관리 일관성이 향상되었습니다.
143-143: LGTM!이모지 제거로 다른 에디터 컴포넌트들과 레이블 스타일이 통일되었습니다.
frontend/src/pages/AdminPage/AdminRoutes.tsx (1)
12-12: LGTM!
ClubIntroTab에서ClubIntroEditTab으로 컴포넌트 교체가 적절히 이루어졌습니다. 라우트 경로(club-intro)가 유지되어 기존 URL 호환성이 보장됩니다.Also applies to: 38-38
frontend/src/types/club.ts (1)
54-58: LGTM!
FAQ인터페이스에 옵셔널id필드를 추가하여 개별 FAQ 항목 식별이 가능해졌습니다. 옵셔널로 선언되어 기존 코드와의 호환성이 유지됩니다.frontend/src/pages/AdminPage/tabs/ClubIntroEditTab/ClubIntroEditTab.tsx (2)
16-18: LGTM!컴포넌트명 변경과 페이지 뷰 트래킹 식별자 업데이트가 일관되게 적용되었습니다.
PAGE_VIEW.CLUB_INTRO_EDIT_PAGE가eventName.ts에 정의된 새 상수와 올바르게 연결됩니다.
102-102: LGTM!이모지 제거로 레이블 스타일이 통일되어 UI 일관성이 향상되었습니다.
Also applies to: 111-111, 122-122, 133-133
frontend/src/pages/AdminPage/tabs/ClubIntroEditTab/components/FAQEditor/FAQEditor.tsx (3)
1-9: LGTM!임포트와 인터페이스 정의가 명확하고 적절합니다.
54-65: LGTM!새로 추가된 FAQ에 대한 포커스 관리 로직이 적절하게 구현되어 있습니다.
78-82: LGTM!타입 가드를 사용한 필터링과
faq.id를 key로 사용하는 것이 적절합니다. JSX 구조가 깔끔하게 구성되어 있습니다.frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.tsx (2)
51-51: LGTM!
useQueryClient를 사용한 캐시 무효화 구현이 적절합니다. 업데이트 성공 후invalidateQueries를 호출하여 최신 데이터를 다시 가져오도록 보장하고 있으며, React Query v5 패턴을 올바르게 따르고 있습니다.Also applies to: 134-144
26-46: 현재 코드에서는divisions과categories상수가 ClubInfoEditTab.tsx에만 정의되어 있으며, 다른 탭에는 중복 정의가 없습니다. ClubIntroEditTab.tsx는 API에서 가져온 데이터를 사용하므로 별도의 상수 추출이 필요하지 않습니다. 현재 구현은 "관련 로직 근처에 상수 정의" 가이드라인을 이미 따르고 있습니다.Likely an incorrect or invalid review comment.
suhyun113
left a comment
There was a problem hiding this comment.
문제없는것 같습니다!
수고하셨어여~ 릴리즈 가시져
#️⃣연관된 이슈
📝작업 내용
중점적으로 리뷰받고 싶은 부분(선택)
논의하고 싶은 부분(선택)
🫡 참고사항
Summary by CodeRabbit
새 기능
스타일
✏️ Tip: You can customize this high-level summary in your review settings.