Conversation
Compound 패턴으로 보다 확장성있게 추가하였습니다
ApplicatnsTab에서 ALL 속성을 사용하기위해 변경했습니다.
useRef를 array 로 초기화하여 ref를 여러개 선언하지않고 한개의 ref로 여러개의 엘리먼트를 핸들 할 수 있습니다.
- onToggle에 isOpen인자를 주어 여닫을시 변경된 상태에 의존하지않고 그시점의 값을 보존하여 독립적으로 관리하게 변경하였습니다. - onToggle을 Trigger를 클릭시에 실행되게 변경하여 Trigger 컴포넌트 추가시 추가적인 event 핸들러 없이 여닫는게 가능하도록 재설계하였습니다
- 드롭다운 메뉴는 open 상태변화가 잦기때문에 메모이제이션을 하는것은 불필요한 계산을 야기함
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning
|
| Cohort / File(s) | 변경 사항 |
|---|---|
라우팅 및 사이드바 frontend/src/App.tsx, frontend/src/pages/AdminPage/components/SideBar/SideBar.tsx |
관리자 라우트 application-edit → application-list로 변경, application-list/edit 중첩 경로 추가 |
ApplicationListTab 추가 frontend/src/pages/AdminPage/tabs/ApplicationListTab/ApplicationListTab.tsx, .../ApplicationListTab.styles.ts, .../ApplicationMenu.tsx |
Admin용 ApplicationListTab 컴포넌트·스타일·메뉴 신규 추가(목록, More 메뉴, 외부클릭 닫기 등) |
CustomDropDown 리팩토링 frontend/src/components/common/CustomDropDown/CustomDropDown.tsx, .../CustomDropDown.styles.ts |
내부 open 상태 제거 → 외부 제어(open/onToggle), 제네릭 값 지원, Trigger/Menu/Item 복합 API 도입, 스타일 prop(transient) 변경 |
CustomDropDown 사용처 마이그레이션 frontend/src/pages/AdminPage/components/QuestionBuilder/QuestionBuilder.tsx, frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.tsx |
기존 CustomDropdown → 새 CustomDropDown 사용으로 오픈/선택 제어 방식 변경 및 UI 구조 조정 |
Header 통합 및 훅 변경 frontend/src/components/common/Header/Header.tsx, .../Header.styles.ts, frontend/src/hooks/Header/useHeaderNavigation.ts, (삭제)frontend/src/services/header/useMobileMenu.ts |
Desktop/Mobile 헤더 통합, useHeaderNavigation 도입(추적 이벤트 변경), useMobileMenu 훅 제거, useScroll 사용 |
Banner → Swiper 및 네비게이터 frontend/src/constants/banners.ts, frontend/src/pages/MainPage/components/Banner/Banner.tsx, .../Banner.styles.ts, frontend/src/hooks/useNavigator.ts, frontend/src/pages/IntroducePage/components/.../IntroSection.tsx |
배너에 linkTo 필드 추가, Swiper 기반 캐러셀로 전환(autoplay·pagination), useNavigator 훅 추가, 카드 포지션 소폭 조정 |
getClubList / 훅·타입·MainPage 반영 frontend/src/apis/getClubList.ts, frontend/src/hooks/queries/club/useGetCardList.ts, frontend/src/types/club.responses.ts, frontend/src/pages/MainPage/MainPage.tsx, .../MainPage.styles.ts |
API 반환을 { clubs, totalCount }로 변경, ClubSearchResponse 타입 추가, 훅 시그니처·select 변경, MainPage에 totalCount 표시 추가 |
ApplicantsTab & 스타일 변경 frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.tsx, .../ApplicantsTab.styles.ts |
여러 드롭다운을 CustomDropDown으로 통합, 필터·정렬 로직 추가, ApplicantFilterSelect를 select → div로 변경 |
RecruitEditTab 및 Calendar 변경 frontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.tsx, .../RecruitEditTab.styles.ts, .../Calendar/Calendar.tsx,.../Calendar/Calendar.styles.ts |
상시모집(FAR_FUTURE_YEAR) 모드 추가, AlwaysRecruitButton 추가, Calendar에 disabledEnd prop 도입 및 스타일 보완 |
추가 훅·추적·상수 변경 frontend/src/hooks/useScroll.ts, frontend/src/hooks/useTrackPageView.ts, frontend/src/constants/eventName.ts |
useScroll 추가, useTrackPageView에 atomic guard 추가(중복 Duration 방지), EVENT_NAME에 PATCH_NOTE_BUTTON_CLICKED 추가 |
다수 스타일·컴포넌트 소폭 조정 frontend/src/components/ClubLogo/ClubLogo.tsx, .../ClubStateBox/ClubStateBox.tsx, .../ClubTag/ClubTag.tsx, .../SearchField/SearchField.styles.ts, frontend/src/styles/PageContainer.styles.ts, frontend/src/pages/MainPage/components/ClubCard/* |
크기·간격·미디어쿼리 임계치 조정, 상수화 및 media helper 사용, ClubCard 레이아웃·export 목록 수정 등 |
ClubDetailPage 소소 변경 frontend/src/pages/ClubDetailPage/ClubDetailPage.tsx, .../ShareButton/*, .../ClubApplyButton/*, .../InfoBox/InfoBox.tsx, .../PhotoList.styles.ts, .../ClubDetailFooter.styles.ts |
RecommendedClubs 주석 처리, Share 아이콘 경로/렌더링 변경, ApplyButton 스타일·Separator 도입, 모집기간에 상시모집 표시 반영, Footer 높이 제거 등 |
스타일 컴포넌트 추가/제거 등 frontend/src/components/common/Header/Header.styles.ts, frontend/src/pages/AdminPage/components/QuestionBuilder/QuestionBuilder.styles.ts, 기타 스타일 파일들 |
Header 스타일 컴포넌트명·prop 변경, QuestionBuilder에 Selected/Icon 추가, CustomDropDown.styles에서 Selected/Icon 제거 등 |
Sequence Diagram(s)
sequenceDiagram
participant User
participant Header
participant useHeaderNavigation
participant SearchStore
participant Analytics
rect rgb(235,245,255)
Note over User,Analytics: 홈 버튼 클릭 흐름
User->>Header: 클릭(Home)
Header->>useHeaderNavigation: handleHomeClick()
useHeaderNavigation->>SearchStore: resetSearch()
useHeaderNavigation->>Analytics: trackEvent('HOME_BUTTON_CLICKED', { device_type })
end
sequenceDiagram
participant User
participant Banner
participant Swiper
participant useNavigator
rect rgb(235,245,255)
Note over User,Banner: 배너 클릭 → 링크 분기
User->>Banner: 클릭(banner)
Banner->>Banner: IF banner.linkTo
Banner->>useNavigator: handleLink(linkTo)
alt 시작 with "https://"
useNavigator-->>User: window.open(linkTo, '_blank','noopener,noreferrer')
else
useNavigator->>Swiper: navigate internally (useNavigate)
end
end
sequenceDiagram
participant Parent
participant CustomDropDown
participant Context
participant Trigger
participant Menu
participant Item
rect rgb(235,245,255)
Note over Parent,Item: CustomDropDown 외부 제어 흐름
Parent->>CustomDropDown: render(props: open,onToggle,onSelect,options...)
CustomDropDown->>Context: provide({ open,onToggle,onSelect })
CustomDropDown->>Trigger: render (consumes Context)
CustomDropDown->>Menu: render (maps Item)
Trigger->>Context: onToggle() (via Context)
Item->>Context: onSelect(value)
Context->>Parent: callbacks invoked (onToggle/onSelect)
end
Estimated code review effort
🎯 4 (Complex) | ⏱️ ~60 minutes
주의가 필요한 영역:
- CustomDropDown 제네릭·외부 제어 전환에 따른 소비지(QuestionBuilder, ApplicantsTab 등) 호환성 점검
- Header 통합으로 인한 모바일 메뉴 행위·이벤트 추적 변경 (useMobileMenu 삭제 영향)
- Banner의 Swiper 전환과 linkTo 내/외부 네비게이션 분기 검증
- getClubList → useGetCardList 반환 형태 변경({clubs,totalCount})에 따른 소비지 일괄 업데이트 확인
- RecruitEditTab 상시모집 토글과 Calendar.disabledEnd 상호작용
Possibly related PRs
- PR
#788: Banner Swiper 마이그레이션,linkTo추가 및 useNavigator 도입과 연관 — 동일 코드경로 변경 - PR
#466: CustomDropdown → CustomDropDown 제네릭·컴포지션 리팩터와 QuestionBuilder 소비지 변경 관련 - PR
#789: getClubList 반환 구조 변경({clubs,totalCount}) 및 useGetCardList/MainPage 반영과 직접 연관
Suggested labels
📈 release
Suggested reviewers
- lepitaaar
- seongje973
- suhyun113
Pre-merge checks and finishing touches
❌ Failed checks (1 warning)
| Check name | Status | Explanation | Resolution |
|---|---|---|---|
| Docstring Coverage | Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. | You can run @coderabbitai generate docstrings to improve docstring coverage. |
✅ Passed checks (2 passed)
| Check name | Status | Explanation |
|---|---|---|
| Description Check | ✅ Passed | Check skipped - CodeRabbit’s high-level summary is enabled. |
| Title check | ✅ Passed | PR 제목은 '[release] FE v1.1.3 배포'로 배포 릴리스임을 명확히 나타내며, 버전 정보(v1.1.3)를 포함하고 있습니다. 광범위한 변경사항을 다루고 있는 changeset의 주요 목적(배포)을 잘 요약하고 있습니다. |
✨ 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
develop-fe
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: 10
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
frontend/src/hooks/useTrackPageView.ts (1)
10-43: 재방문 시 Duration 이벤트가 더 이상 발송되지 않습니다.
경로가 바뀌면 effect가 다시 실행되지만 ref 값은 초기화되지 않아trackPageDuration이 한 번만 실행되고 이후에는 guard 때문에 Duration 이벤트가 아예 찍히지 않습니다.startTime도 초기 마운트 시각으로 고정돼 체류 시간이 왜곡되고, visibilitychange 리스너는 익명 함수를 등록한 뒤 다른 핸들러로 해제하고 있어 누수 위험이 있습니다. effect 진입 시 ref를 리셋하고 동일한 핸들러로 등록/해제하도록 정리해 주세요.useEffect(() => { + startTime.current = Date.now(); + isTracked.current = false; + mixpanel.track(`${pageName} Visited`, { url: window.location.href, timestamp: startTime.current, @@ - const trackPageDuration = () => { + const trackPageDuration = () => { // 레이스 컨디션 방지: 체크와 설정을 원자적으로 처리 if (isTracked.current) return; isTracked.current = true; @@ - window.addEventListener('beforeunload', trackPageDuration); - document.addEventListener('visibilitychange', () => { - if (document.hidden) { - trackPageDuration(); - } - }); + const handleVisibilityChange = () => { + if (document.hidden) { + trackPageDuration(); + } + }; + + window.addEventListener('beforeunload', trackPageDuration); + document.addEventListener('visibilitychange', handleVisibilityChange); @@ trackPageDuration(); window.removeEventListener('beforeunload', trackPageDuration); - document.removeEventListener('visibilitychange', trackPageDuration); + document.removeEventListener('visibilitychange', handleVisibilityChange); }; }, [location.pathname]);frontend/src/pages/AdminPage/components/QuestionBuilder/QuestionBuilder.tsx (1)
29-38: 훅은 조건부로 호출할 수 없습니다.
type검증에서 조기return으로 빠지면 그 아래의useState훅들이 호출되지 않아 렌더마다 훅 호출 순서가 달라집니다. React 훅 규칙 위반으로 런타임 경고/오작동이 발생하니, 훅들을 항상 호출한 뒤에if (!isValidType) return null;패턴으로 가드를 옮겨 주세요.
🧹 Nitpick comments (11)
frontend/src/pages/ClubDetailPage/components/PhotoList/PhotoList.styles.ts (1)
18-18: 매직 넘버를 명명된 상수로 치환하세요.
margin-bottom: 60px는 의미를 명확히 하기 위해 명명된 상수로 정의해야 합니다. 코딩 가이드라인에 따라 매직 넘버는 피하고 의미 있는 상수명을 사용해야 합니다.다음과 같이 상수를 정의하고 사용하도록 리팩토링하는 것을 추천합니다:
+ const PHOTO_LIST_BOTTOM_SPACING = 60; + export const PhotoListContainer = styled.div` display: flex; flex-direction: column; margin-top: 34px; width: 100%; border-radius: 18px; border: 1px solid #dcdcdc; padding: 30px; gap: 30px; - margin-bottom: 60px; + margin-bottom: ${PHOTO_LIST_BOTTOM_SPACING}px; ${media.mobile} { margin-top: 0; width: 100%; border: none; border-radius: 0; padding: 20px; } `;frontend/src/utils/mapStatusToGroup.ts (2)
8-19: 매직 스트링을 명명된 상수로 추출해주세요.함수 내에서 '서류검토', '면접예정', '합격', '불합', '전체' 등의 레이블이 매직 스트링으로 하드코딩되어 있습니다. 코딩 가이드라인에 따라 이러한 매직 값들을 명명된 상수로 추출하는 것이 좋습니다.
다음과 같이 리팩토링할 수 있습니다:
+const STATUS_LABELS = { + SUBMITTED: '서류검토', + INTERVIEW_SCHEDULED: '면접예정', + ACCEPTED: '합격', + DECLINED: '불합', + ALL: '전체', +} as const; + const mapStatusToGroup = ( status: ApplicationStatus, ): { status: ApplicationStatus; label: string } => { switch (status) { case ApplicationStatus.SUBMITTED: - return { status: ApplicationStatus.SUBMITTED, label: '서류검토' }; + return { status: ApplicationStatus.SUBMITTED, label: STATUS_LABELS.SUBMITTED }; case ApplicationStatus.INTERVIEW_SCHEDULED: return { status: ApplicationStatus.INTERVIEW_SCHEDULED, - label: '면접예정', + label: STATUS_LABELS.INTERVIEW_SCHEDULED, }; case ApplicationStatus.ACCEPTED: - return { status: ApplicationStatus.ACCEPTED, label: '합격' }; + return { status: ApplicationStatus.ACCEPTED, label: STATUS_LABELS.ACCEPTED }; case ApplicationStatus.DECLINED: - return { status: ApplicationStatus.DECLINED, label: '불합' }; + return { status: ApplicationStatus.DECLINED, label: STATUS_LABELS.DECLINED }; default: - return { status: ApplicationStatus.SUBMITTED, label: '전체' }; + return { status: ApplicationStatus.SUBMITTED, label: STATUS_LABELS.ALL }; } };As per coding guidelines.
6-20: Exhaustive type checking 패턴 고려해보세요.현재 default case를 사용하고 있는데, TypeScript의 exhaustive checking을 활용하면 새로운
ApplicationStatus값이 추가될 때 컴파일 타임에 누락된 케이스를 감지할 수 있습니다.예시:
const mapStatusToGroup = ( status: ApplicationStatus, ): { status: ApplicationStatus; label: string } => { switch (status) { case ApplicationStatus.SUBMITTED: return { status: ApplicationStatus.SUBMITTED, label: '서류검토' }; case ApplicationStatus.INTERVIEW_SCHEDULED: return { status: ApplicationStatus.INTERVIEW_SCHEDULED, label: '면접예정', }; case ApplicationStatus.ACCEPTED: return { status: ApplicationStatus.ACCEPTED, label: '합격' }; case ApplicationStatus.DECLINED: return { status: ApplicationStatus.DECLINED, label: '불합' }; default: - return { status: ApplicationStatus.SUBMITTED, label: '전체' }; + const exhaustiveCheck: never = status; + throw new Error(`Unhandled status: ${exhaustiveCheck}`); } };이렇게 하면
ApplicationStatus에 새로운 값이 추가되었을 때 TypeScript 컴파일러가 오류를 발생시킵니다.frontend/src/pages/ClubDetailPage/ClubDetailPage.tsx (1)
70-70: 주석 처리된 코드 제거 또는 문서화 고려주석 처리된 코드가 프로덕션에 남아있습니다. 이 컴포넌트를 일시적으로 비활성화한 것이라면 TODO 주석이나 feature flag를 추가하여 의도를 명확히 하거나, 향후 사용 계획이 없다면 완전히 제거하는 것을 권장합니다.
frontend/src/pages/ClubDetailPage/components/InfoBox/InfoBox.tsx (1)
27-30: IIFE 패턴을 단순한 삼항 연산자로 단순화 권장현재 IIFE와 중간 변수를 사용한 로직은 단순한 조건문에 비해 불필요하게 복잡합니다.
clubDetail.recruitmentStatus === '상시모집'은 복잡한 조건이 아니므로 직접적인 삼항 연산자가 더 읽기 쉽습니다.다음과 같이 단순화할 수 있습니다:
- const recruitmentPeriodDisplay = (() => { - const isAlways = clubDetail.recruitmentStatus === '상시모집'; - return isAlways ? '상시모집' : clubDetail.recruitmentPeriod; - })(); + const recruitmentPeriodDisplay = + clubDetail.recruitmentStatus === '상시모집' + ? '상시모집' + : clubDetail.recruitmentPeriod;frontend/src/components/ClubTag/ClubTag.tsx (1)
40-44: 높이 값도 상수로 관리하는 것을 고려하세요.모바일
padding과borderRadius는TAG_STYLES.mobile에서 관리하고 있지만,height: 25px는 직접 하드코딩되어 있습니다. 일관성을 위해 높이 값도TAG_STYLES.mobile에 포함시키는 것을 고려해보세요.const TAG_STYLES = { desktop: { padding: '4px 8px', borderRadius: '8px', }, mobile: { + height: '25px', padding: '4px 6px', borderRadius: '6px', }, } as const;@media (max-width: 500px) { - height: 25px; + height: ${TAG_STYLES.mobile.height}; padding: ${TAG_STYLES.mobile.padding}; border-radius: ${TAG_STYLES.mobile.borderRadius}; }Based on coding guidelines
frontend/src/components/common/Header/Header.styles.ts (1)
96-98: 햄버거 메뉴 토글 시 이동이 순간적으로 보입니다
transition이opacity에만 적용되어 실제로 변경되는transform에는 애니메이션이 걸리지 않아, 메뉴 토글 때 요소가 바로 위치를 바꾸며 부자연스럽게 보입니다.transform을 대상으로 transition을 지정하면 슬라이드 애니메이션 의도에 더 가깝게 동작합니다.- transform: translateY(${({ isOpen }) => (isOpen ? '0' : '-200%')}); - transition: opacity 0.3s ease-in-out; + transform: translateY(${({ isOpen }) => (isOpen ? '0' : '-200%')}); + transition: transform 0.3s ease-in-out;frontend/src/constants/banners.ts (1)
9-29: 배너 링크를 상수로 추출하는 것을 고려해보세요.모든 배너가 동일한 경로('/introduce')를 사용하고 있습니다. 이를 상수로 추출하면 유지보수가 더 쉬워집니다.
다음과 같이 리팩토링할 수 있습니다:
+const BANNER_LINK_PATH = '/introduce'; + export const DesktopBannerImageList = [ { backgroundImage: FirstSlideDesktopImage, - linkTo: '/introduce', + linkTo: BANNER_LINK_PATH, }, { backgroundImage: SecondSlideDesktopImage, - linkTo: '/introduce', + linkTo: BANNER_LINK_PATH, }, ]; export const MobileBannerImageList = [ { backgroundImage: FirstSlideMobileImage, - linkTo: '/introduce', + linkTo: BANNER_LINK_PATH, }, { backgroundImage: SecondSlideMobileImage, - linkTo: '/introduce', + linkTo: BANNER_LINK_PATH, }, ];frontend/src/pages/MainPage/MainPage.tsx (1)
59-71: 새로운 레이아웃 구조가 잘 구현되었습니다.
SectionBar로 탭과 총 개수를 그룹화한 구조가 깔끔하고,role="status"를 통한 접근성 고려도 좋습니다.로딩 중 "0개"를 표시하는 부분은 의도적인 설계로 보이지만, 원한다면 로딩 중에는 "로딩 중..." 같은 텍스트를 보여주는 것도 고려해볼 수 있습니다.
frontend/src/pages/AdminPage/tabs/ApplicationListTab/ApplicationMenu.tsx (1)
12-37: 컴포넌트 구조는 잘 구현되었으나, 핸들러 구현이 필요합니다.메뉴 아이템의 구조와 조건부 렌더링이 깔끔하게 구현되었습니다. 현재 핸들러 함수들이
console.log만 수행하는 플레이스홀더 상태인데, 실제 기능 구현이 필요합니다.기본 지원서 설정, 제목 수정, 삭제 기능의 실제 구현이 필요하면 도움을 드릴 수 있습니다. 이슈를 생성하거나 구현 코드를 작성해 드릴까요?
frontend/src/pages/AdminPage/tabs/ApplicationListTab/ApplicationListTab.tsx (1)
52-58: 불필요한 상태 제거 제안Line 52에서 정적 목업 데이터를
useState로 감싸지만setData를 호출하지 않아 상태 관리가 불필요합니다. 단순 상수로 유지하면 의도 전달이 명확해지고 쓸데없는 재렌더 트리거를 방지할 수 있습니다.- const [data, setData] = useState(initialdata); + const data = initialdata;
📜 Review details
Configuration used: CodeRabbit 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 ignored due to path filters (10)
frontend/src/assets/images/icons/Delete_applicant.svgis excluded by!**/*.svgfrontend/src/assets/images/icons/Morebutton.svgis excluded by!**/*.svgfrontend/src/assets/images/icons/Plus.svgis excluded by!**/*.svgfrontend/src/assets/images/icons/check_inactive.svgis excluded by!**/*.svgfrontend/src/assets/images/icons/kakaotalk_sharing_btn_small.pngis excluded by!**/*.pngfrontend/src/assets/images/icons/next_button_icon.svgis excluded by!**/*.svgfrontend/src/assets/images/icons/pencil_icon_3.svgis excluded by!**/*.svgfrontend/src/assets/images/icons/prev_button_icon.svgis excluded by!**/*.svgfrontend/src/assets/images/icons/share_filled_icon.svgis excluded by!**/*.svgfrontend/src/assets/images/icons/share_icon.svgis excluded by!**/*.svg
📒 Files selected for processing (48)
frontend/src/App.tsx(2 hunks)frontend/src/apis/getClubList.ts(1 hunks)frontend/src/components/ClubLogo/ClubLogo.tsx(2 hunks)frontend/src/components/ClubStateBox/ClubStateBox.tsx(1 hunks)frontend/src/components/ClubTag/ClubTag.tsx(1 hunks)frontend/src/components/common/CustomDropDown/CustomDropDown.styles.ts(1 hunks)frontend/src/components/common/CustomDropDown/CustomDropDown.tsx(1 hunks)frontend/src/components/common/Header/Header.styles.ts(1 hunks)frontend/src/components/common/Header/Header.tsx(1 hunks)frontend/src/components/common/SearchField/SearchField.styles.ts(4 hunks)frontend/src/constants/banners.ts(1 hunks)frontend/src/constants/eventName.ts(1 hunks)frontend/src/hooks/Header/useHeaderNavigation.ts(2 hunks)frontend/src/hooks/queries/club/useGetCardList.ts(2 hunks)frontend/src/hooks/useNavigator.ts(1 hunks)frontend/src/hooks/useScroll.ts(1 hunks)frontend/src/hooks/useTrackPageView.ts(1 hunks)frontend/src/pages/AdminPage/components/QuestionBuilder/QuestionBuilder.styles.ts(1 hunks)frontend/src/pages/AdminPage/components/QuestionBuilder/QuestionBuilder.tsx(4 hunks)frontend/src/pages/AdminPage/components/SideBar/SideBar.tsx(3 hunks)frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.styles.ts(2 hunks)frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.tsx(5 hunks)frontend/src/pages/AdminPage/tabs/ApplicationListTab/ApplicationListTab.styles.ts(1 hunks)frontend/src/pages/AdminPage/tabs/ApplicationListTab/ApplicationListTab.tsx(1 hunks)frontend/src/pages/AdminPage/tabs/ApplicationListTab/ApplicationMenu.tsx(1 hunks)frontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.styles.ts(1 hunks)frontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.tsx(6 hunks)frontend/src/pages/AdminPage/tabs/RecruitEditTab/components/Calendar/Calendar.styles.ts(2 hunks)frontend/src/pages/AdminPage/tabs/RecruitEditTab/components/Calendar/Calendar.tsx(4 hunks)frontend/src/pages/ClubDetailPage/ClubDetailPage.tsx(1 hunks)frontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.styles.ts(2 hunks)frontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsx(1 hunks)frontend/src/pages/ClubDetailPage/components/ClubDetailFooter/ClubDetailFooter.styles.ts(0 hunks)frontend/src/pages/ClubDetailPage/components/InfoBox/InfoBox.tsx(1 hunks)frontend/src/pages/ClubDetailPage/components/PhotoList/PhotoList.styles.ts(1 hunks)frontend/src/pages/ClubDetailPage/components/ShareButton/ShareButton.styles.ts(1 hunks)frontend/src/pages/ClubDetailPage/components/ShareButton/ShareButton.tsx(2 hunks)frontend/src/pages/MainPage/MainPage.styles.ts(4 hunks)frontend/src/pages/MainPage/MainPage.tsx(2 hunks)frontend/src/pages/MainPage/components/Banner/Banner.styles.ts(5 hunks)frontend/src/pages/MainPage/components/Banner/Banner.tsx(2 hunks)frontend/src/pages/MainPage/components/CategoryButtonList/CategoryButtonList.styles.ts(2 hunks)frontend/src/pages/MainPage/components/ClubCard/ClubCard.styles.ts(2 hunks)frontend/src/pages/MainPage/components/ClubCard/ClubCard.tsx(1 hunks)frontend/src/services/header/useMobileMenu.ts(0 hunks)frontend/src/styles/PageContainer.styles.ts(1 hunks)frontend/src/types/club.responses.ts(1 hunks)frontend/src/utils/mapStatusToGroup.ts(1 hunks)
💤 Files with no reviewable changes (2)
- frontend/src/pages/ClubDetailPage/components/ClubDetailFooter/ClubDetailFooter.styles.ts
- frontend/src/services/header/useMobileMenu.ts
🧰 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/constants/eventName.tsfrontend/src/pages/AdminPage/tabs/RecruitEditTab/components/Calendar/Calendar.styles.tsfrontend/src/hooks/useScroll.tsfrontend/src/hooks/useNavigator.tsfrontend/src/types/club.responses.tsfrontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.styles.tsfrontend/src/hooks/useTrackPageView.tsfrontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsxfrontend/src/pages/AdminPage/tabs/ApplicationListTab/ApplicationListTab.tsxfrontend/src/pages/ClubDetailPage/components/InfoBox/InfoBox.tsxfrontend/src/hooks/Header/useHeaderNavigation.tsfrontend/src/pages/MainPage/components/Banner/Banner.tsxfrontend/src/pages/AdminPage/components/QuestionBuilder/QuestionBuilder.styles.tsfrontend/src/pages/AdminPage/components/QuestionBuilder/QuestionBuilder.tsxfrontend/src/pages/ClubDetailPage/components/ShareButton/ShareButton.styles.tsfrontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.styles.tsfrontend/src/pages/AdminPage/tabs/ApplicationListTab/ApplicationMenu.tsxfrontend/src/components/common/Header/Header.tsxfrontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.tsxfrontend/src/pages/MainPage/components/CategoryButtonList/CategoryButtonList.styles.tsfrontend/src/pages/ClubDetailPage/components/PhotoList/PhotoList.styles.tsfrontend/src/components/ClubStateBox/ClubStateBox.tsxfrontend/src/pages/MainPage/components/ClubCard/ClubCard.styles.tsfrontend/src/components/ClubTag/ClubTag.tsxfrontend/src/pages/ClubDetailPage/ClubDetailPage.tsxfrontend/src/pages/MainPage/MainPage.tsxfrontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.tsxfrontend/src/pages/AdminPage/components/SideBar/SideBar.tsxfrontend/src/apis/getClubList.tsfrontend/src/pages/AdminPage/tabs/RecruitEditTab/components/Calendar/Calendar.tsxfrontend/src/styles/PageContainer.styles.tsfrontend/src/utils/mapStatusToGroup.tsfrontend/src/App.tsxfrontend/src/components/ClubLogo/ClubLogo.tsxfrontend/src/pages/ClubDetailPage/components/ShareButton/ShareButton.tsxfrontend/src/constants/banners.tsfrontend/src/components/common/CustomDropDown/CustomDropDown.styles.tsfrontend/src/hooks/queries/club/useGetCardList.tsfrontend/src/components/common/SearchField/SearchField.styles.tsfrontend/src/pages/MainPage/components/Banner/Banner.styles.tsfrontend/src/pages/MainPage/components/ClubCard/ClubCard.tsxfrontend/src/components/common/Header/Header.styles.tsfrontend/src/pages/AdminPage/tabs/ApplicationListTab/ApplicationListTab.styles.tsfrontend/src/components/common/CustomDropDown/CustomDropDown.tsxfrontend/src/pages/MainPage/MainPage.styles.tsfrontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.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/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsxfrontend/src/pages/AdminPage/tabs/ApplicationListTab/ApplicationListTab.tsxfrontend/src/pages/ClubDetailPage/components/InfoBox/InfoBox.tsxfrontend/src/pages/MainPage/components/Banner/Banner.tsxfrontend/src/pages/AdminPage/components/QuestionBuilder/QuestionBuilder.tsxfrontend/src/pages/AdminPage/tabs/ApplicationListTab/ApplicationMenu.tsxfrontend/src/components/common/Header/Header.tsxfrontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.tsxfrontend/src/components/ClubStateBox/ClubStateBox.tsxfrontend/src/components/ClubTag/ClubTag.tsxfrontend/src/pages/ClubDetailPage/ClubDetailPage.tsxfrontend/src/pages/MainPage/MainPage.tsxfrontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.tsxfrontend/src/pages/AdminPage/components/SideBar/SideBar.tsxfrontend/src/pages/AdminPage/tabs/RecruitEditTab/components/Calendar/Calendar.tsxfrontend/src/App.tsxfrontend/src/components/ClubLogo/ClubLogo.tsxfrontend/src/pages/ClubDetailPage/components/ShareButton/ShareButton.tsxfrontend/src/pages/MainPage/components/ClubCard/ClubCard.tsxfrontend/src/components/common/CustomDropDown/CustomDropDown.tsx
🧠 Learnings (12)
📚 Learning: 2025-07-19T05:09:10.702Z
Learnt from: CR
Repo: Moadong/moadong PR: 0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.702Z
Learning: Applies to frontend/**/*.{ts,tsx} : Replace magic numbers with named constants for clarity.
Applied to files:
frontend/src/constants/eventName.tsfrontend/src/components/common/Header/Header.tsxfrontend/src/components/ClubStateBox/ClubStateBox.tsxfrontend/src/pages/MainPage/components/ClubCard/ClubCard.styles.tsfrontend/src/components/ClubTag/ClubTag.tsxfrontend/src/pages/MainPage/MainPage.tsxfrontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.tsxfrontend/src/styles/PageContainer.styles.tsfrontend/src/utils/mapStatusToGroup.tsfrontend/src/components/ClubLogo/ClubLogo.tsxfrontend/src/components/common/SearchField/SearchField.styles.tsfrontend/src/pages/MainPage/components/Banner/Banner.styles.tsfrontend/src/components/common/Header/Header.styles.tsfrontend/src/pages/AdminPage/tabs/ApplicationListTab/ApplicationListTab.styles.tsfrontend/src/pages/MainPage/MainPage.styles.tsfrontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.styles.ts
📚 Learning: 2025-07-19T05:09:10.702Z
Learnt from: CR
Repo: Moadong/moadong PR: 0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.702Z
Learning: Applies to frontend/**/*.{ts,tsx} : Define constants near related logic or ensure names link them clearly.
Applied to files:
frontend/src/constants/eventName.tsfrontend/src/components/common/Header/Header.styles.tsfrontend/src/pages/AdminPage/tabs/ApplicationListTab/ApplicationListTab.styles.ts
📚 Learning: 2025-07-19T05:09:10.702Z
Learnt from: CR
Repo: Moadong/moadong PR: 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/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.styles.tsfrontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsxfrontend/src/pages/AdminPage/tabs/ApplicationListTab/ApplicationListTab.tsxfrontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.styles.tsfrontend/src/pages/AdminPage/tabs/ApplicationListTab/ApplicationMenu.tsxfrontend/src/components/common/Header/Header.tsxfrontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.tsxfrontend/src/pages/MainPage/components/ClubCard/ClubCard.tsxfrontend/src/components/common/Header/Header.styles.tsfrontend/src/pages/AdminPage/tabs/ApplicationListTab/ApplicationListTab.styles.tsfrontend/src/pages/MainPage/MainPage.styles.ts
📚 Learning: 2025-07-19T05:09:10.702Z
Learnt from: CR
Repo: Moadong/moadong PR: 0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.702Z
Learning: Applies to frontend/**/*.tsx : Colocate simple, localized logic or use inline definitions to reduce context switching.
Applied to files:
frontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsxfrontend/src/components/common/Header/Header.tsx
📚 Learning: 2025-07-19T05:09:10.702Z
Learnt from: CR
Repo: Moadong/moadong PR: 0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.702Z
Learning: Applies to frontend/**/*.tsx : Abstract complex logic/interactions into dedicated components or higher-order components (HOCs).
Applied to files:
frontend/src/pages/AdminPage/tabs/ApplicationListTab/ApplicationListTab.tsxfrontend/src/components/common/Header/Header.tsxfrontend/src/components/common/Header/Header.styles.ts
📚 Learning: 2025-07-19T05:09:10.702Z
Learnt from: CR
Repo: Moadong/moadong PR: 0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.702Z
Learning: Applies to frontend/**/*.tsx : Break down broad state management into smaller, focused hooks or contexts.
Applied to files:
frontend/src/pages/AdminPage/tabs/ApplicationListTab/ApplicationListTab.tsxfrontend/src/components/common/Header/Header.tsxfrontend/src/components/ClubStateBox/ClubStateBox.tsxfrontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.tsxfrontend/src/components/common/CustomDropDown/CustomDropDown.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/ClubDetailPage/components/InfoBox/InfoBox.tsxfrontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.tsxfrontend/src/pages/ClubDetailPage/ClubDetailPage.tsxfrontend/src/pages/MainPage/MainPage.tsxfrontend/src/hooks/queries/club/useGetCardList.tsfrontend/src/pages/MainPage/components/ClubCard/ClubCard.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/ClubDetailPage/components/InfoBox/InfoBox.tsxfrontend/src/pages/ClubDetailPage/ClubDetailPage.tsxfrontend/src/pages/MainPage/MainPage.tsx
📚 Learning: 2025-09-21T02:23:27.796Z
Learnt from: seongwon030
Repo: Moadong/moadong PR: 744
File: frontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsx:47-48
Timestamp: 2025-09-21T02:23:27.796Z
Learning: ClubApplyButton 컴포넌트에서 ShareButton은 항상 렌더링되어야 하므로 정적 import를 사용하는 것이 적절함. 동적 import는 불필요함.
Applied to files:
frontend/src/pages/ClubDetailPage/components/ShareButton/ShareButton.styles.tsfrontend/src/pages/ClubDetailPage/components/ShareButton/ShareButton.tsx
📚 Learning: 2025-07-19T05:09:10.702Z
Learnt from: CR
Repo: Moadong/moadong PR: 0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.702Z
Learning: Applies to frontend/**/*.{ts,tsx} : Use consistent return types for similar functions and hooks.
Applied to files:
frontend/src/components/common/Header/Header.tsxfrontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.tsxfrontend/src/hooks/queries/club/useGetCardList.ts
📚 Learning: 2025-07-19T05:09:10.702Z
Learnt from: CR
Repo: Moadong/moadong PR: 0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.702Z
Learning: Applies to frontend/**/*.tsx : Use component composition instead of props drilling.
Applied to files:
frontend/src/components/common/Header/Header.tsx
📚 Learning: 2025-05-01T08:32:09.398Z
Learnt from: Due-IT
Repo: Moadong/moadong PR: 329
File: backend/src/main/java/moadong/club/controller/ClubSearchController.java:0-0
Timestamp: 2025-05-01T08:32:09.398Z
Learning: ClubSearchController의 searchClubsByKeyword 메서드와 ClubSearchService의 searchClubsByKeyword 메서드 사이에 파라미터 순서 일치가 중요하다. 현재 컨트롤러의 매개변수 순서는 'keyword, recruitmentStatus, category, division'인 반면, 서비스 메서드의 순서는 'keyword, recruitmentStatus, division, category'이다. 컨트롤러에서 서비스 호출 시 'keyword, recruitmentStatus, division, category' 순서로 전달하면 category와 division 값이 바뀌어 검색 결과가 잘못될 수 있다.
Applied to files:
frontend/src/pages/MainPage/MainPage.tsxfrontend/src/hooks/queries/club/useGetCardList.ts
🧬 Code graph analysis (13)
frontend/src/types/club.responses.ts (1)
frontend/src/types/club.ts (1)
Club(3-12)
frontend/src/hooks/Header/useHeaderNavigation.ts (2)
frontend/src/store/useSearchStore.ts (1)
useSearchStore(14-24)frontend/src/constants/eventName.ts (1)
EVENT_NAME(1-21)
frontend/src/pages/MainPage/components/Banner/Banner.tsx (2)
frontend/src/pages/MainPage/components/Banner/Banner.styles.ts (1)
SlideButton(66-88)frontend/src/constants/banners.ts (1)
SlideButton(31-31)
frontend/src/pages/AdminPage/components/QuestionBuilder/QuestionBuilder.tsx (3)
frontend/src/constants/APPLICATION_FORM.ts (1)
DROPDOWN_OPTIONS(40-43)frontend/src/components/common/CustomDropDown/CustomDropDown.tsx (1)
CustomDropDown(94-115)frontend/src/types/application.ts (1)
QuestionType(3-3)
frontend/src/components/common/Header/Header.tsx (3)
frontend/src/components/common/Header/Header.styles.ts (1)
Header(4-23)frontend/src/hooks/useScroll.ts (1)
useScroll(3-16)frontend/src/constants/eventName.ts (1)
EVENT_NAME(1-21)
frontend/src/pages/MainPage/MainPage.tsx (1)
frontend/src/hooks/queries/club/useGetCardList.ts (1)
useGetCardList(6-24)
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.tsx (5)
frontend/src/constants/status.ts (1)
AVAILABLE_STATUSES(3-8)frontend/src/hooks/queries/applicants/useDeleteApplicants.ts (1)
useDeleteApplicants(4-17)frontend/src/hooks/queries/applicants/useUpdateApplicant.ts (1)
useUpdateApplicant(5-18)frontend/src/types/applicants.ts (1)
Applicant(18-24)frontend/src/components/common/CustomDropDown/CustomDropDown.tsx (1)
CustomDropDown(94-115)
frontend/src/styles/PageContainer.styles.ts (1)
frontend/src/styles/mediaQuery.ts (1)
media(8-14)
frontend/src/hooks/queries/club/useGetCardList.ts (2)
frontend/src/types/club.responses.ts (1)
ClubSearchResponse(3-6)frontend/src/apis/getClubList.ts (1)
getClubList(3-30)
frontend/src/components/common/SearchField/SearchField.styles.ts (1)
frontend/src/styles/mediaQuery.ts (1)
media(8-14)
frontend/src/pages/MainPage/components/Banner/Banner.styles.ts (1)
frontend/src/styles/mediaQuery.ts (1)
media(8-14)
frontend/src/components/common/Header/Header.styles.ts (1)
frontend/src/styles/mediaQuery.ts (1)
media(8-14)
frontend/src/pages/MainPage/MainPage.styles.ts (1)
frontend/src/styles/mediaQuery.ts (1)
media(8-14)
🪛 Biome (2.1.2)
frontend/src/pages/AdminPage/components/QuestionBuilder/QuestionBuilder.tsx
[error] 37-37: This hook is being called conditionally, but all hooks must be called in the exact same order in every component render.
Hooks should not be called after an early return.
For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level
(lint/correctness/useHookAtTopLevel)
🔇 Additional comments (14)
frontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsx (1)
59-59: LGTM! 스타일 컴포넌트로의 일관성 있는 전환인라인 span을 Styled.Separator로 교체하여 스타일 관리를 개선했습니다. 기능적으로 동일하면서 코드의 일관성과 유지보수성이 향상되었습니다.
frontend/src/pages/ClubDetailPage/components/ShareButton/ShareButton.styles.ts (1)
9-12: LGTM! 명확한 스타일 컴포넌트 추가ShareButtonIcon이 고정된 크기로 명확하게 정의되어 있습니다. 구현이 깔끔하고 적절합니다.
frontend/src/pages/ClubDetailPage/components/ShareButton/ShareButton.tsx (1)
4-4: LGTM! 아이콘 경로 업데이트 및 스타일 컴포넌트 적용아이콘 import 경로가 업데이트되고 styled component를 사용하도록 변경되어 PR 전반의 스타일 리팩토링과 일관성을 유지합니다. 기능적으로 동일하며 문제가 없습니다.
Also applies to: 57-57
frontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.styles.ts (2)
20-27: LGTM! 버튼 스타일 업데이트ApplyButton의 스타일이 더 크고 눈에 띄게 업데이트되었습니다 (배경색, 크기, 폰트). 변경 사항이 적절하고 일관성 있습니다.
41-46: LGTM! Separator 컴포넌트 추가구분선을 위한 Separator 컴포넌트가 명확하게 정의되어 있습니다. ClubApplyButton.tsx에서 올바르게 사용되고 있습니다.
frontend/src/components/ClubTag/ClubTag.tsx (1)
20-29: 코드 개선이 좋습니다!코딩 가이드라인에 따라 매직 넘버를 명명된 상수로 교체했습니다.
TAG_STYLES상수와as const타입 단언을 통해 타입 안정성과 유지보수성이 향상되었습니다.Based on coding guidelines
frontend/src/components/ClubLogo/ClubLogo.tsx (1)
12-13: 일관된 브레이크포인트 사용이 좋습니다!
main프리셋의 크기 업데이트와 함께 미디어 쿼리 브레이크포인트가500px로 통일되어 다른 컴포넌트들(ClubTag, ClubStateBox)과 일관성을 유지하고 있습니다. 이는 유지보수성을 향상시킵니다.Also applies to: 35-35
frontend/src/components/ClubStateBox/ClubStateBox.tsx (2)
3-16: 훌륭한 리팩토링입니다!
STATE_COLORS상수를 도입하여 매직 넘버를 제거하고 색상 값을 중앙화했습니다.as const타입 단언과 함께 타입 안정성과 유지보수성이 크게 향상되었습니다.Based on coding guidelines
Also applies to: 22-37
39-48: 차원 값 중앙화가 잘 되었습니다!
BOX_DIMENSIONS상수를 통해 반응형 차원 값을 중앙화하고, 다른 컴포넌트들과 일관된500px브레이크포인트를 사용하고 있습니다. 코딩 가이드라인을 잘 따르고 있습니다.Based on coding guidelines
Also applies to: 55-56, 63-66
frontend/src/types/club.responses.ts (1)
1-6: LGTM!새로운
ClubSearchResponse인터페이스가 깔끔하게 정의되었습니다. 클럽 목록과 총 개수를 함께 반환하는 구조가 명확하고, 타입 안정성을 잘 보장합니다.frontend/src/pages/AdminPage/components/QuestionBuilder/QuestionBuilder.styles.ts (1)
78-100: LGTM!새로 추가된
Selected와Icon스타일 컴포넌트가 잘 구현되었습니다. 트랜지션 효과와 사용자 경험을 위한 속성들(user-select: none,pointer-events: none)이 적절하게 적용되어 있습니다.frontend/src/pages/MainPage/MainPage.tsx (1)
31-38: LGTM!데이터 구조화와 파생 상태 로직이 깔끔하게 구현되었습니다.
totalCount를 기반으로 한isEmpty와hasData계산이 명확하고, 폴백 처리도 적절합니다.frontend/src/App.tsx (1)
66-72: LGTM!라우팅 구조가 깔끔하게 개선되었습니다.
application-list를 부모 경로로 하고 그 아래에edit을 중첩시킨 구조가 직관적이며, 기존ApplicationEditTab기능도 잘 보존되었습니다.frontend/src/pages/MainPage/components/ClubCard/ClubCard.tsx (1)
38-57: LGTM!레이아웃 리팩토링이 깔끔하게 수행되었습니다.
ClubInfo로 클럽 이름과 소개를 그룹화하고,StateBoxTagContainer로 상태와 태그를 함께 배치한 구조가 직관적입니다. 빈 태그를 필터링하는 로직(line 50)도 적절합니다.
| border-radius: ${TAG_STYLES.mobile.borderRadius}; | ||
| } | ||
| `; | ||
| ``; |
There was a problem hiding this comment.
불필요한 코드를 제거하세요.
빈 템플릿 리터럴(``)이 남아있습니다. 이는 의도하지 않은 코드로 보이며 제거해야 합니다.
`;
-``;📝 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.
| ``; | |
| `; |
🤖 Prompt for AI Agents
In frontend/src/components/ClubTag/ClubTag.tsx around line 46, there's an
unintended empty template literal (``) that should be removed; delete the ``
token from that line (and any leftover whitespace), then run the
linter/formatter to ensure no style issues remain.
| const selectedType = type === 'MULTI_CHOICE' ? 'CHOICE' : type; | ||
| const selectedLabel = DROPDOWN_OPTIONS.find( | ||
| (option) => option.value === selectedType, | ||
| )?.label; | ||
|
|
There was a problem hiding this comment.
다중선택 타입이 드롭다운에서 잘못 표시됩니다.
type === 'MULTI_CHOICE'인 경우에도 selectedType을 'CHOICE'로 덮어써서 드롭다운이 항상 단일선택 라벨을 보여 줍니다. 사용자는 현재 질문 타입을 정확히 확인할 수 없고 선택 항목 하이라이트도 어긋납니다. selectedType은 그대로 type을 사용하도록 수정해 주세요.
🤖 Prompt for AI Agents
In frontend/src/pages/AdminPage/components/QuestionBuilder/QuestionBuilder.tsx
around lines 124 to 128, the code overwrites MULTI_CHOICE to 'CHOICE' causing
the dropdown to always show the single-choice label; revert this mapping so
selectedType uses the original type value (i.e., set selectedType = type) and
let DROPDOWN_OPTIONS.find match against the actual type string, ensuring the
correct label and highlight are shown for MULTI_CHOICE.
| if (keyword.trim()) { | ||
| applicants = applicants.filter((user: Applicant) => | ||
| user.answers?.[0]?.value | ||
| ?.toLowerCase() | ||
| .includes(keyword.trim().toLowerCase()), | ||
| ); | ||
| } |
There was a problem hiding this comment.
검색 필터에서 null 안전성 부족
user.answers?.[0]?.value?.toLowerCase() 뒤에 바로 .includes(...)가 이어지면서, 값이 없을 때 undefined.includes가 호출되어 런타임 오류가 발생합니다. 모든 지원자에게 이름 답변이 있다는 보장이 없어 발생할 수 있는 치명적인 예외입니다. includes 호출 전에 문자열 여부를 확인하거나 기본값을 사용하도록 수정해 주세요. 예를 들어 아래처럼 키워드를 미리 정규화하고 값이 존재할 때만 비교하도록 바꿀 수 있습니다.(developer.mozilla.org)
- if (keyword.trim()) {
- applicants = applicants.filter((user: Applicant) =>
- user.answers?.[0]?.value
- ?.toLowerCase()
- .includes(keyword.trim().toLowerCase()),
- );
- }
+ const normalizedKeyword = keyword.trim().toLowerCase();
+ if (normalizedKeyword) {
+ applicants = applicants.filter((user: Applicant) => {
+ const answer = user.answers?.[0]?.value;
+ return answer ? answer.toLowerCase().includes(normalizedKeyword) : false;
+ });
+ }📝 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.
| if (keyword.trim()) { | |
| applicants = applicants.filter((user: Applicant) => | |
| user.answers?.[0]?.value | |
| ?.toLowerCase() | |
| .includes(keyword.trim().toLowerCase()), | |
| ); | |
| } | |
| const normalizedKeyword = keyword.trim().toLowerCase(); | |
| if (normalizedKeyword) { | |
| applicants = applicants.filter((user: Applicant) => { | |
| const answer = user.answers?.[0]?.value; | |
| return answer ? answer.toLowerCase().includes(normalizedKeyword) : false; | |
| }); | |
| } |
🤖 Prompt for AI Agents
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.tsx around lines
76-82: the current filter calls .toLowerCase().includes(...) on a potentially
undefined value which can throw; normalize the search keyword once, safely read
the candidate answer into a string (use optional chaining and a default empty
string) or check typeof value === 'string' before calling toLowerCase/ includes,
then perform the lowercase includes comparison only when a real string is
present so no runtime exception occurs.
- swipe/css 초기화 코드를 앱진입점으로 위치변경
…OA-307 [fix] swipe/css를 RecommendedClubs에서 App으로 이동
…s-MOA-309 [fix] 소개페이지 카드 위치 조정
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
frontend/src/pages/IntroducePage/components/sections/1.IntroSection/IntroSection.tsx (1)
22-23: 카드 위치 조정 변경사항 확인 완료첫 번째 카드는 20px 위로, 두 번째 카드는 30px 아래로 이동하여 두 카드 간 수직 간격이 조정되었습니다. 레이아웃 미세 조정으로 보이며 코드상 문제는 없습니다.
선택적 개선사항: 향후 유지보수성을 위해 각 위치가 어떤 카드에 대응되는지 주석을 추가하는 것을 고려해보세요.
const cardPositions = [ - { top: '280px', left: '-300px' }, - { top: '330px', left: '-630px' }, + { top: '280px', left: '-300px' }, // 왼쪽 상단 카드 + { top: '330px', left: '-630px' }, // 왼쪽 하단 카드 { top: '280px', right: '-220px' }, { top: '310px', right: '-580px' }, ];
📜 Review details
Configuration used: CodeRabbit 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 (4)
frontend/src/App.tsx(2 hunks)frontend/src/pages/ClubDetailPage/ClubDetailPage.tsx(1 hunks)frontend/src/pages/ClubDetailPage/components/RecommendedClubs/RecommendedClubs.tsx(0 hunks)frontend/src/pages/IntroducePage/components/sections/1.IntroSection/IntroSection.tsx(1 hunks)
💤 Files with no reviewable changes (1)
- frontend/src/pages/ClubDetailPage/components/RecommendedClubs/RecommendedClubs.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
- frontend/src/App.tsx
🧰 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/IntroducePage/components/sections/1.IntroSection/IntroSection.tsxfrontend/src/pages/ClubDetailPage/ClubDetailPage.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/IntroducePage/components/sections/1.IntroSection/IntroSection.tsxfrontend/src/pages/ClubDetailPage/ClubDetailPage.tsx
🧠 Learnings (2)
📚 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/ClubDetailPage/ClubDetailPage.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/ClubDetailPage/ClubDetailPage.tsx
외부 링크(패치노트) 열기 시 보안 및 프라이버시 보호를 위해 noopener,noreferrer 옵션을 추가했습니다. - noopener: 새 창이 window.opener에 접근할 수 없도록 방지 - noreferrer: HTTP 리퍼러 헤더를 전송하지 않음
…rer-MOA-311 [fix] window.open에 noopener,noreferrer 옵션 추가
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
frontend/src/hooks/Header/useHeaderNavigation.ts (1)
30-37: 보안 플래그가 올바르게 적용되었습니다.이전 리뷰 코멘트에서 지적된
noopener,noreferrer플래그가 Line 34에 정확히 추가되어 reverse tabnabbing 위험이 해결되었습니다. 구현이 안전하고 올바릅니다.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Jira integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
frontend/src/hooks/Header/useHeaderNavigation.ts(2 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
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/hooks/Header/useHeaderNavigation.ts
🧬 Code graph analysis (1)
frontend/src/hooks/Header/useHeaderNavigation.ts (2)
frontend/src/store/useSearchStore.ts (1)
useSearchStore(14-24)frontend/src/constants/eventName.ts (1)
EVENT_NAME(1-21)
| const handleHomeClick = useCallback(() => { | ||
| navigate('/'); | ||
| useSearchStore.getState().resetSearch(); | ||
| // 속성으로 관리 | ||
| trackEvent(EVENT_NAME.HOME_BUTTON_CLICKED, { | ||
| device_type: window.innerWidth <= 700 ? 'mobile' : 'desktop', | ||
| }); | ||
| }, [navigate, trackEvent]); |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
매직 넘버를 명명된 상수로 교체해주세요.
Line 16의 700은 모바일 breakpoint를 나타내는 매직 넘버입니다. 코딩 가이드라인에 따라 명명된 상수로 분리하면 의도가 명확해지고 재사용성이 높아집니다.
다음과 같이 수정을 권장합니다:
파일 상단에 상수를 정의:
const MOBILE_BREAKPOINT = 700;그리고 해당 라인을 수정:
trackEvent(EVENT_NAME.HOME_BUTTON_CLICKED, {
- device_type: window.innerWidth <= 700 ? 'mobile' : 'desktop',
+ device_type: window.innerWidth <= MOBILE_BREAKPOINT ? 'mobile' : 'desktop',
});As per coding guidelines.
🤖 Prompt for AI Agents
In frontend/src/hooks/Header/useHeaderNavigation.ts around lines 11 to 18,
replace the magic number 700 with a named constant to clarify intent and enable
reuse: add a file-level constant (e.g., MOBILE_BREAKPOINT = 700) near the top of
the module and update the comparison window.innerWidth <= 700 to use
MOBILE_BREAKPOINT instead; keep the rest of the handler unchanged and ensure the
constant is exported or moved to a shared constants file if it will be reused
elsewhere.
파라미터 순서 오류 방지 및 가독성 향상을 위해 useGetCardList의 파라미터를 객체 형태로 변경했습니다. - UseGetCardListProps 인터페이스 추가 - 파라미터를 객체로 받도록 변경 - category와 division 순서 수정 (queryKey, queryFn 일치) - MainPage 호출부를 객체 형태로 변경
…CardList [refactor] useGetCardList 파라미터를 객체로 변경
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
frontend/src/pages/MainPage/MainPage.tsx (1)
68-70: 로딩 중 총 개수 표시 개선을 고려해보세요.현재 로딩 중일 때 "전체 0개의 동아리"로 표시되는데, 사용자에게 약간 혼란을 줄 수 있습니다. 로딩 중임을 명확히 표시하는 것도 고려해볼 수 있습니다.
예시:
<Styled.TotalCountResult role="status"> - {`전체 ${isLoading ? 0 : totalCount}개의 동아리`} + {isLoading ? '로딩 중...' : `전체 ${totalCount}개의 동아리`} </Styled.TotalCountResult>
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Jira integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
frontend/src/hooks/queries/club/useGetCardList.ts(1 hunks)frontend/src/pages/MainPage/MainPage.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/MainPage/MainPage.tsxfrontend/src/hooks/queries/club/useGetCardList.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/MainPage/MainPage.tsx
🧠 Learnings (6)
📚 Learning: 2025-07-19T05:09:10.702Z
Learnt from: CR
Repo: Moadong/moadong PR: 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/MainPage/MainPage.tsx
📚 Learning: 2025-07-19T05:09:10.702Z
Learnt from: CR
Repo: Moadong/moadong PR: 0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.702Z
Learning: Applies to frontend/**/*.{ts,tsx} : Replace magic numbers with named constants for clarity.
Applied to files:
frontend/src/pages/MainPage/MainPage.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/MainPage/MainPage.tsxfrontend/src/hooks/queries/club/useGetCardList.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/MainPage/MainPage.tsx
📚 Learning: 2025-05-01T08:32:09.398Z
Learnt from: Due-IT
Repo: Moadong/moadong PR: 329
File: backend/src/main/java/moadong/club/controller/ClubSearchController.java:0-0
Timestamp: 2025-05-01T08:32:09.398Z
Learning: ClubSearchController의 searchClubsByKeyword 메서드와 ClubSearchService의 searchClubsByKeyword 메서드 사이에 파라미터 순서 일치가 중요하다. 현재 컨트롤러의 매개변수 순서는 'keyword, recruitmentStatus, category, division'인 반면, 서비스 메서드의 순서는 'keyword, recruitmentStatus, division, category'이다. 컨트롤러에서 서비스 호출 시 'keyword, recruitmentStatus, division, category' 순서로 전달하면 category와 division 값이 바뀌어 검색 결과가 잘못될 수 있다.
Applied to files:
frontend/src/pages/MainPage/MainPage.tsxfrontend/src/hooks/queries/club/useGetCardList.ts
📚 Learning: 2025-07-19T05:09:10.702Z
Learnt from: CR
Repo: Moadong/moadong PR: 0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.702Z
Learning: Applies to frontend/**/*.{ts,tsx} : Use consistent return types for similar functions and hooks.
Applied to files:
frontend/src/hooks/queries/club/useGetCardList.ts
🧬 Code graph analysis (2)
frontend/src/pages/MainPage/MainPage.tsx (1)
frontend/src/hooks/queries/club/useGetCardList.ts (1)
useGetCardList(13-28)
frontend/src/hooks/queries/club/useGetCardList.ts (2)
frontend/src/types/club.responses.ts (1)
ClubSearchResponse(3-6)frontend/src/apis/getClubList.ts (1)
getClubList(3-30)
🔇 Additional comments (2)
frontend/src/hooks/queries/club/useGetCardList.ts (1)
13-27: 매개변수 순서 문제가 해결되었습니다!이전 리뷰에서 지적된
category와division매개변수 순서 불일치 문제가 올바르게 수정되었습니다. Line 18에서getClubList(keyword, recruitmentStatus, category, division)순서로 호출하여getClubList.ts의 함수 시그니처와 일치합니다.또한 객체 매개변수로 리팩토링한 점도 좋습니다. 여러 매개변수를 다룰 때 가독성과 유지보수성이 향상됩니다.
frontend/src/pages/MainPage/MainPage.tsx (1)
31-38: 훅 통합 및 데이터 처리가 올바르게 구현되었습니다.새로운
useGetCardList시그니처와의 통합이 정확하며, 안전한 기본값 처리(|| [],|| 0)와 로딩/빈 상태 로직이 올바르게 구현되었습니다.
#️⃣연관된 이슈
📝작업 내용
중점적으로 리뷰받고 싶은 부분(선택)
논의하고 싶은 부분(선택)
🫡 참고사항
Summary by CodeRabbit
New Features
Bug Fixes
Improvements