Conversation
[fix] 동아리 상세페이지 모집 버튼 푸터 삭제 복구
[feat] 모집예정 상태를 추가한다
[feature] 자동 스크롤 훅 추가
…e-MOA-467 [fix] 상시모집 버튼 상태가 유지되지 않는 버그를 수정한다
…OA-474 [feature] sns링크 버튼 클릭이벤트 추가
…own-MOA-462 [feature] 사용자가 모집 시작 일정을 확인할 수 있도록 지원하기 버튼을 개선한다
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning
|
| Cohort / File(s) | 변경 요약 |
|---|---|
앱 라우트 스크롤 frontend/src/App.tsx, frontend/src/hooks/ScrollToTop.tsx, frontend/src/hooks/useScrollDetection.ts, frontend/src/components/common/Header/Header.tsx |
ScrollToTop 컴포넌트 추가 및 App에 렌더링. useScroll 훅을 useScrollDetection으로 리네이밍 및 Header에서 교체. |
모집 상태 타입·유틸 frontend/src/types/club.ts, frontend/src/utils/getDeadLineText.ts, frontend/src/utils/getDeadLineText.test.ts |
RecruitmentStatus 타입 추가('OPEN' |
상태 표시 연동 frontend/src/components/ClubStateBox/ClubStateBox.tsx, frontend/src/styles/clubTags.ts |
UPCOMING('모집예정')에 대한 STATE_TEXT 및 STATUS_COLORS 매핑 추가. |
공통 컴포넌트 스타일 수정 frontend/src/components/common/Button/Button.tsx, frontend/src/components/common/InputField/InputField.styles.ts, frontend/src/styles/Global.styles.ts |
Button 크기/타이포 변경(높이·폰트), Input 필드 border-radius 및 플레이스홀더 색상/폰트 적용, 폼 요소 전반에 font-family 적용. |
ClubDetail 하단·신청 버튼 frontend/src/pages/ClubDetailPage/ClubDetailPage.tsx, frontend/src/pages/ClubDetailPage/components/ClubDetailFooter/*, frontend/src/pages/ClubDetailPage/components/ClubApplyButton/* |
ClubDetailFooter에 recruitmentStatus prop 추가 및 전달. ClubApplyButton에 recruitmentStatus 기반 비활성화 처리(UPCOMING/CLOSED), 핸들러/상태명 리네이밍 및 스타일/반응형 개선. |
프로필 카드·공유 버튼 frontend/src/pages/ClubDetailPage/components/ClubProfileCard/ClubProfileCard.tsx, frontend/src/pages/ClubDetailPage/components/ShareButton/* |
ClubProfileCard에 Mixpanel SNS 클릭 트래킹 추가. ShareButton에 디바이스 감지로 모바일 전용 아이콘 적용 및 아이콘 크기 조정. |
Admin 모집 편집(캘린더 포함) frontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.tsx, frontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.styles.ts, frontend/src/pages/AdminPage/tabs/RecruitEditTab/components/Calendar/Calendar.styles.ts |
상시 모집 판별을 문자열 비교에서 isFarFuture(end) 날짜 기반으로 변경. 스타일을 테마 컬러·media로 통합 및 border-radius/크기 조정. |
Estimated code review effort
🎯 3 (Moderate) | ⏱️ ~25 minutes
Possibly related PRs
- [feature] 사용자가 모집 시작 일정을 확인할 수 있도록 지원하기 버튼을 개선한다 #986: getDeadlineText 시그니처·ClubDetailFooter/ClubApplyButton 연관 변경을 함께 포함하고 있음.
- [HotFix] 모집예정 상태를 추가한다 #981: ClubStateBox 및 clubTags에서 UPCOMING/모집예정 상태 매핑 추가와 중복됨.
- [fix] 상시모집 버튼 상태가 유지되지 않는 버그를 수정한다 #990: RecruitEditTab의 상시 모집(isFarFuture) 전환 및 스타일/캘린더 변경과 겹침.
Suggested reviewers
- oesnuj
- suhyun113
Pre-merge checks and finishing touches
❌ Failed checks (1 inconclusive)
| Check name | Status | Explanation | Resolution |
|---|---|---|---|
| Title check | ❓ Inconclusive | PR 제목이 너무 일반적이고 모호합니다. '[release] FE 릴리즈'는 실제 변경 사항을 구체적으로 설명하지 않으며, 본질적으로 무엇이 변경되었는지 알 수 없습니다. | 제목을 더 구체적으로 변경하세요. 예: '[release] FE: 모집 상태 관리 개선 및 UI 스타일 리팩토링' 또는 주요 변경 사항을 명시하는 형태로 수정 권장합니다. |
✅ Passed checks (2 passed)
| Check name | Status | Explanation |
|---|---|---|
| Description Check | ✅ Passed | Check skipped - CodeRabbit’s high-level summary is enabled. |
| Docstring Coverage | ✅ Passed | No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. |
✨ 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: 2
🧹 Nitpick comments (4)
frontend/src/pages/ClubDetailPage/components/ClubProfileCard/ClubProfileCard.tsx (1)
13-20: recruitmentStatus prop의 타입을 RecruitmentStatus로 변경하세요.
RecruitmentStatus타입이club.ts에 정의되어 있으므로, Props 인터페이스에서도 동일한 타입을 사용하면 타입 안전성을 확보할 수 있습니다. 코딩 가이드라인에 따라 유사한 데이터에 대해 일관된 타입을 사용해야 합니다.🔎 제안된 수정
+import { RecruitmentStatus, SNSPlatform } from '@/types/club'; -import { SNSPlatform } from '@/types/club'; interface Props { name: string; logo?: string; cover?: string; - recruitmentStatus: string; + recruitmentStatus: RecruitmentStatus; socialLinks: Record<SNSPlatform, string>; introDescription: string; }frontend/src/pages/ClubDetailPage/components/ClubDetailFooter/ClubDetailFooter.styles.ts (1)
8-9: 매직 넘버를 명명된 상수로 추출하는 것을 고려하세요.패딩 값들(10px, 24px, 16px)이 하드코딩되어 있습니다. 이러한 값들을 디자인 토큰이나 명명된 상수로 추출하면 유지보수성과 일관성이 향상됩니다.
🔎 제안하는 리팩터링
+const FOOTER_SPACING = { + VERTICAL: '10px', + BOTTOM_DESKTOP: '24px', + BOTTOM_MOBILE: '16px', +} as const; + export const ClubDetailFooterContainer = styled.div` position: sticky; bottom: 0; width: 100%; z-index: 1040; // TODO: Portal로 모달 분리 후 header보다 낮게 재조정 - padding: 10px 0px 24px 0px; + padding: ${FOOTER_SPACING.VERTICAL} 0px ${FOOTER_SPACING.BOTTOM_DESKTOP} 0px; display: flex; align-items: center; justify-content: space-between; gap: 16px; background-color: white; border-top: 1px solid #cdcdcd; ${media.mobile} { - padding: 10px 0px 16px 0px; + padding: ${FOOTER_SPACING.VERTICAL} 0px ${FOOTER_SPACING.BOTTOM_MOBILE} 0px; } `;Based on learnings, 매직 넘버는 명명된 상수로 교체하여 명확성을 높여야 합니다.
Also applies to: 19-21
frontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.styles.ts (1)
14-45: 매직 넘버를 명명된 상수로 추출하는 것을 고려하세요.버튼 크기 관련 값들(60px, 273px, 44px, 16px 등)이 하드코딩되어 있습니다. 이러한 값들을 명명된 상수로 추출하면 의미가 명확해지고 유지보수가 용이해집니다.
🔎 제안하는 리팩터링
+const BUTTON_SIZES = { + DESKTOP: { + WIDTH: '517px', + HEIGHT: '60px', + FONT_SIZE: '20px', + FONT_WEIGHT: 700, + }, + MOBILE: { + WIDTH: '273px', + HEIGHT: '44px', + FONT_SIZE: '16px', + FONT_WEIGHT: 500, + }, +} as const; + export const ApplyButton = styled.button` display: flex; align-items: center; justify-content: center; border: none; border-radius: 10px; cursor: ${({disabled}) => (disabled ? 'default' : 'pointer')}; transition: transform 0.2s ease-in-out; background-color: ${({ disabled }) => disabled ? colors.gray[500] : colors.primary[800]}; padding: 10px 40px; - width: 517px; - height: 60px; - font-size: 20px; + width: ${BUTTON_SIZES.DESKTOP.WIDTH}; + height: ${BUTTON_SIZES.DESKTOP.HEIGHT}; + font-size: ${BUTTON_SIZES.DESKTOP.FONT_SIZE}; font-style: normal; - font-weight: 700; + font-weight: ${BUTTON_SIZES.DESKTOP.FONT_WEIGHT}; color: ${colors.gray[200]}; text-align: center; img { font-size: 12px; font-weight: 600; } ${media.mobile} { - width: 273px; - height: 44px; - font-size: 16px; - font-weight: 500; + width: ${BUTTON_SIZES.MOBILE.WIDTH}; + height: ${BUTTON_SIZES.MOBILE.HEIGHT}; + font-size: ${BUTTON_SIZES.MOBILE.FONT_SIZE}; + font-weight: ${BUTTON_SIZES.MOBILE.FONT_WEIGHT}; background-color: ${({ disabled }) => disabled ? colors.gray[500] : colors.gray[900]}; } `;Based on learnings, 매직 넘버는 명명된 상수로 교체하여 명확성을 높여야 합니다.
frontend/src/utils/getDeadLineText.ts (1)
13-13:recruitmentStatus파라미터에 타입 안전성을 강화하세요.
recruitmentStatus가string으로 타입이 지정되어 있지만,RecruitmentStatus타입이 이미 정의되어 있습니다. 타입 안전성을 위해 구체적인 타입을 사용하는 것이 좋습니다.🔎 제안하는 수정
파일 상단에 타입을 import하세요:
import { format, differenceInCalendarDays } from 'date-fns'; import { ko } from 'date-fns/locale'; +import { RecruitmentStatus } from '@/types/club';그런 다음 파라미터 타입을 업데이트하세요:
const getDeadlineText = ( recruitmentStart: Date | null, recruitmentEnd: Date | null, - recruitmentStatus: string, + recruitmentStatus: RecruitmentStatus, today: Date = new Date(), ): string => {
📜 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 ignored due to path filters (2)
frontend/src/assets/images/icons/share_icon.svgis excluded by!**/*.svgfrontend/src/assets/images/icons/share_icon_mobile.svgis excluded by!**/*.svg
📒 Files selected for processing (23)
frontend/src/App.tsxfrontend/src/components/ClubStateBox/ClubStateBox.tsxfrontend/src/components/common/Button/Button.tsxfrontend/src/components/common/Header/Header.tsxfrontend/src/components/common/InputField/InputField.styles.tsfrontend/src/hooks/ScrollToTop.tsxfrontend/src/hooks/useScrollDetection.tsfrontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.styles.tsfrontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.tsxfrontend/src/pages/AdminPage/tabs/RecruitEditTab/components/Calendar/Calendar.styles.tsfrontend/src/pages/ClubDetailPage/ClubDetailPage.tsxfrontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.styles.tsfrontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsxfrontend/src/pages/ClubDetailPage/components/ClubDetailFooter/ClubDetailFooter.styles.tsfrontend/src/pages/ClubDetailPage/components/ClubDetailFooter/ClubDetailFooter.tsxfrontend/src/pages/ClubDetailPage/components/ClubProfileCard/ClubProfileCard.tsxfrontend/src/pages/ClubDetailPage/components/ShareButton/ShareButton.styles.tsfrontend/src/pages/ClubDetailPage/components/ShareButton/ShareButton.tsxfrontend/src/styles/Global.styles.tsfrontend/src/styles/clubTags.tsfrontend/src/types/club.tsfrontend/src/utils/getDeadLineText.test.tsfrontend/src/utils/getDeadLineText.ts
🧰 Additional context used
📓 Path-based instructions (3)
frontend/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (frontend/.cursorrules)
frontend/**/*.{ts,tsx,js,jsx}: Replace magic numbers with named constants for clarity
Replace complex/nested ternaries 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/types/club.tsfrontend/src/pages/ClubDetailPage/components/ShareButton/ShareButton.tsxfrontend/src/hooks/ScrollToTop.tsxfrontend/src/components/ClubStateBox/ClubStateBox.tsxfrontend/src/App.tsxfrontend/src/pages/ClubDetailPage/components/ClubProfileCard/ClubProfileCard.tsxfrontend/src/styles/Global.styles.tsfrontend/src/pages/ClubDetailPage/ClubDetailPage.tsxfrontend/src/hooks/useScrollDetection.tsfrontend/src/pages/AdminPage/tabs/RecruitEditTab/components/Calendar/Calendar.styles.tsfrontend/src/utils/getDeadLineText.test.tsfrontend/src/utils/getDeadLineText.tsfrontend/src/pages/ClubDetailPage/components/ClubDetailFooter/ClubDetailFooter.tsxfrontend/src/styles/clubTags.tsfrontend/src/pages/ClubDetailPage/components/ShareButton/ShareButton.styles.tsfrontend/src/components/common/Header/Header.tsxfrontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsxfrontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.tsxfrontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.styles.tsfrontend/src/components/common/Button/Button.tsxfrontend/src/components/common/InputField/InputField.styles.tsfrontend/src/pages/ClubDetailPage/components/ClubDetailFooter/ClubDetailFooter.styles.tsfrontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.styles.ts
frontend/**/*.{ts,tsx}
📄 CodeRabbit inference engine (frontend/.cursorrules)
Use consistent return types for similar functions/hooks
Files:
frontend/src/types/club.tsfrontend/src/pages/ClubDetailPage/components/ShareButton/ShareButton.tsxfrontend/src/hooks/ScrollToTop.tsxfrontend/src/components/ClubStateBox/ClubStateBox.tsxfrontend/src/App.tsxfrontend/src/pages/ClubDetailPage/components/ClubProfileCard/ClubProfileCard.tsxfrontend/src/styles/Global.styles.tsfrontend/src/pages/ClubDetailPage/ClubDetailPage.tsxfrontend/src/hooks/useScrollDetection.tsfrontend/src/pages/AdminPage/tabs/RecruitEditTab/components/Calendar/Calendar.styles.tsfrontend/src/utils/getDeadLineText.test.tsfrontend/src/utils/getDeadLineText.tsfrontend/src/pages/ClubDetailPage/components/ClubDetailFooter/ClubDetailFooter.tsxfrontend/src/styles/clubTags.tsfrontend/src/pages/ClubDetailPage/components/ShareButton/ShareButton.styles.tsfrontend/src/components/common/Header/Header.tsxfrontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsxfrontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.tsxfrontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.styles.tsfrontend/src/components/common/Button/Button.tsxfrontend/src/components/common/InputField/InputField.styles.tsfrontend/src/pages/ClubDetailPage/components/ClubDetailFooter/ClubDetailFooter.styles.tsfrontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.styles.ts
frontend/**/*.{tsx,jsx}
📄 CodeRabbit inference engine (frontend/.cursorrules)
frontend/**/*.{tsx,jsx}: Abstract complex logic/interactions into dedicated components/HOCs
Separate significantly different conditional UI/logic into distinct components
Colocate simple, localized logic or use inline definitions to reduce context switching
Choose field-level or form-level cohesion based on form requirements when using form libraries like react-hook-form
Use Component Composition instead of Props Drilling to reduce coupling
Files:
frontend/src/pages/ClubDetailPage/components/ShareButton/ShareButton.tsxfrontend/src/hooks/ScrollToTop.tsxfrontend/src/components/ClubStateBox/ClubStateBox.tsxfrontend/src/App.tsxfrontend/src/pages/ClubDetailPage/components/ClubProfileCard/ClubProfileCard.tsxfrontend/src/pages/ClubDetailPage/ClubDetailPage.tsxfrontend/src/pages/ClubDetailPage/components/ClubDetailFooter/ClubDetailFooter.tsxfrontend/src/components/common/Header/Header.tsxfrontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsxfrontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.tsxfrontend/src/components/common/Button/Button.tsx
🧠 Learnings (11)
📚 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.tsxfrontend/src/pages/ClubDetailPage/components/ShareButton/ShareButton.styles.ts
📚 Learning: 2025-03-19T05:18:07.818Z
Learnt from: seongwon030
Repo: Moadong/moadong PR: 195
File: frontend/src/pages/AdminPage/AdminPage.tsx:7-7
Timestamp: 2025-03-19T05:18:07.818Z
Learning: AdminPage.tsx에서 현재 하드코딩된 클럽 ID('67d2e3b9b15c136c6acbf20b')는 로그인 기능 구현 후 동적으로 가져오는 방식으로 수정될 예정입니다.
Applied to files:
frontend/src/pages/ClubDetailPage/components/ShareButton/ShareButton.tsxfrontend/src/components/ClubStateBox/ClubStateBox.tsxfrontend/src/pages/ClubDetailPage/components/ClubProfileCard/ClubProfileCard.tsxfrontend/src/pages/ClubDetailPage/ClubDetailPage.tsxfrontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsxfrontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.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/hooks/ScrollToTop.tsxfrontend/src/App.tsxfrontend/src/components/common/Header/Header.tsxfrontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.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/components/ClubStateBox/ClubStateBox.tsxfrontend/src/styles/clubTags.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/App.tsxfrontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.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/App.tsx
📚 Learning: 2025-11-25T14:08:23.253Z
Learnt from: CR
Repo: Moadong/moadong PR: 0
File: frontend/.cursorrules:0-0
Timestamp: 2025-11-25T14:08:23.253Z
Learning: Applies to frontend/**/*.{tsx,jsx} : Use Component Composition instead of Props Drilling to reduce coupling
Applied to files:
frontend/src/App.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/styles/Global.styles.tsfrontend/src/pages/AdminPage/tabs/RecruitEditTab/components/Calendar/Calendar.styles.tsfrontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsxfrontend/src/components/common/InputField/InputField.styles.ts
📚 Learning: 2025-11-25T14:08:23.253Z
Learnt from: CR
Repo: Moadong/moadong PR: 0
File: frontend/.cursorrules:0-0
Timestamp: 2025-11-25T14:08:23.253Z
Learning: Applies to frontend/**/*.{ts,tsx,js,jsx} : Define constants near related logic or ensure names link them clearly to avoid silent failures
Applied to files:
frontend/src/styles/Global.styles.tsfrontend/src/utils/getDeadLineText.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} : Separate significantly different conditional UI/logic into distinct components
Applied to files:
frontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsx
📚 Learning: 2025-09-30T05:26:41.788Z
Learnt from: alsdddk
Repo: Moadong/moadong PR: 765
File: backend/src/main/java/moadong/club/service/ClubApplyService.java:431-435
Timestamp: 2025-09-30T05:26:41.788Z
Learning: In the Moadong codebase's club application feature (backend/src/main/java/moadong/club/), multiple ClubApplicationForm entities can have ACTIVE status for the same clubId, semesterYear, and semesterTerm simultaneously. There is no uniqueness constraint requiring only one ACTIVE form per semester.
Applied to files:
frontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsx
🧬 Code graph analysis (11)
frontend/src/App.tsx (1)
frontend/src/hooks/ScrollToTop.tsx (1)
ScrollToTop(4-12)
frontend/src/pages/AdminPage/tabs/RecruitEditTab/components/Calendar/Calendar.styles.ts (1)
frontend/src/styles/theme/colors.ts (1)
colors(1-81)
frontend/src/pages/ClubDetailPage/components/ClubDetailFooter/ClubDetailFooter.tsx (2)
frontend/src/types/club.ts (1)
RecruitmentStatus(3-7)frontend/src/utils/recruitmentDateParser.ts (1)
recruitmentDateParser(3-18)
frontend/src/styles/clubTags.ts (1)
frontend/src/styles/theme/colors.ts (1)
colors(1-81)
frontend/src/pages/ClubDetailPage/components/ShareButton/ShareButton.styles.ts (1)
frontend/src/styles/mediaQuery.ts (1)
media(8-14)
frontend/src/components/common/Header/Header.tsx (1)
frontend/src/hooks/useScrollDetection.ts (1)
useScrollDetection(3-16)
frontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsx (2)
frontend/src/types/application.ts (1)
ApplicationForm(65-68)frontend/src/constants/eventName.ts (1)
USER_EVENT(1-38)
frontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.styles.ts (1)
frontend/src/styles/theme/colors.ts (1)
colors(1-81)
frontend/src/components/common/InputField/InputField.styles.ts (1)
frontend/src/styles/theme/colors.ts (1)
colors(1-81)
frontend/src/pages/ClubDetailPage/components/ClubDetailFooter/ClubDetailFooter.styles.ts (1)
frontend/src/styles/mediaQuery.ts (1)
media(8-14)
frontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.styles.ts (2)
frontend/src/styles/theme/colors.ts (1)
colors(1-81)frontend/src/styles/mediaQuery.ts (1)
media(8-14)
🔇 Additional comments (21)
frontend/src/components/common/InputField/InputField.styles.ts (1)
1-1: 테마 기반 스타일링 적용 및 모바일 UX 개선 확인테마 색상 사용으로 일관성이 향상되었고, placeholder의
font-size: 16px는 iOS Safari에서 포커스 시 자동 확대를 방지하는 좋은 선택입니다.Also applies to: 39-39, 70-71
frontend/src/styles/Global.styles.ts (1)
9-11: 폼 요소 전반에 일관된 폰트 적용 확인
button,input,select까지 확장하여 모든 폼 컨트롤에 일관된 폰트가 적용되도록 개선되었습니다.frontend/src/hooks/ScrollToTop.tsx (1)
1-12: 브라우저 뒤로/앞으로 이동 시 스크롤 복원 동작을 확인하세요모든 경로 변경 시 최상단으로 스크롤하는 구현은 정상적으로 작동하지만, 브라우저의 뒤로/앞으로 버튼 사용 시에도 스크롤 위치가 복원되지 않고 최상단으로 이동합니다.
일반적으로 사용자는 브라우저 네비게이션 시 이전 스크롤 위치가 복원되기를 기대합니다. 이것이 의도된 동작인지 확인하고, 필요하다면
useNavigationType을 활용하여POP액션(뒤로/앞으로)일 때는 스크롤하지 않도록 개선할 수 있습니다.브라우저 네비게이션 시 스크롤 복원을 위한 제안
import { useEffect } from 'react'; -import { useLocation } from 'react-router-dom'; +import { useLocation, useNavigationType } from 'react-router-dom'; export const ScrollToTop = () => { const { pathname } = useLocation(); + const navigationType = useNavigationType(); useEffect(() => { - window.scrollTo(0, 0); + // POP (뒤로/앞으로 버튼)이 아닐 때만 스크롤 + if (navigationType !== 'POP') { + window.scrollTo(0, 0); + } }, [pathname]); return null; };frontend/src/App.tsx (1)
7-7: ScrollToTop 컴포넌트 통합 확인
BrowserRouter내부에 적절히 배치되었으며,GlobalStyles이후에 렌더링하여 전역 라우트 변경 감지가 올바르게 동작합니다.Also applies to: 29-29
frontend/src/components/ClubStateBox/ClubStateBox.tsx (1)
8-8: 모집예정 상태 지원 추가 확인새로운
UPCOMING상태를 지원하여 모집 상태 시스템이 확장되었습니다.STATUS_COLORS의 '모집예정' 매핑과 일관되게 작동합니다.frontend/src/pages/ClubDetailPage/components/ShareButton/ShareButton.tsx (1)
2-2: 디바이스별 아이콘 분기 처리 확인
useDevice훅을 활용하여 모바일과 데스크톱에 맞는 공유 아이콘을 선택적으로 렌더링하도록 개선되었습니다. 간결하고 명확한 구현입니다.Also applies to: 6-6, 18-18, 60-63
frontend/src/components/common/Button/Button.tsx (1)
21-21: 버튼 크기 및 폰트 사이즈 조정 확인높이와 폰트 크기가 조정되어 다른 컴포넌트들과 시각적 일관성이 개선되었습니다.
font-size: 16px는 모바일 포커스 시 자동 확대를 방지합니다.Also applies to: 25-25
frontend/src/pages/ClubDetailPage/components/ShareButton/ShareButton.styles.ts (1)
1-1: 반응형 아이콘 크기 조정 확인데스크톱에서 60x60, 모바일에서 44x44로 아이콘 크기가 디바이스별로 최적화되었습니다.
media헬퍼를 사용하여 프로젝트의 반응형 스타일 패턴과 일관성을 유지하고 있습니다.Also applies to: 11-17
frontend/src/components/common/Header/Header.tsx (1)
9-9: LGTM! 훅 이름 변경이 적절합니다.
useScroll→useScrollDetection리네이밍은 훅의 역할(스크롤 감지)을 더 명확하게 표현합니다. 관련 훅 파일의 변경과 일관성 있게 적용되었습니다.Also applies to: 18-18
frontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.styles.ts (1)
1-1: LGTM! 테마 색상 적용이 잘 되었습니다.하드코딩된 색상값을
colors테마 토큰으로 교체하여 유지보수성과 일관성이 향상되었습니다.colors.primary[800],colors.gray[700]등 다른 컴포넌트들과 동일한 테마 시스템을 사용하고 있습니다.Also applies to: 26-28
frontend/src/pages/ClubDetailPage/ClubDetailPage.tsx (1)
83-87: LGTM! recruitmentStatus prop 추가가 적절합니다.
ClubDetailFooter에recruitmentStatusprop을 전달하여 상태 기반 마감 텍스트 렌더링이 가능해졌습니다. 이는 PR의 모집 상태 시스템 개선 목표와 일치합니다.frontend/src/styles/clubTags.ts (1)
17-22: 모집중과 모집예정이 동일한 색상을 사용합니다.
모집중과모집예정모두colors.accent[1][900](#3DBBFF)을 사용하고 있습니다. 이는 의도된 디자인일 수 있지만, 두 상태를 시각적으로 구분하지 못할 수 있습니다.디자인 의도가 맞는지 확인해 주세요. 만약 구분이 필요하다면
모집예정에 다른 색상(예:colors.accent[1][800])을 고려해 볼 수 있습니다.frontend/src/types/club.ts (2)
3-7: LGTM! 타입 안전성이 개선되었습니다.
RecruitmentStatus유니온 타입 도입으로 모집 상태 처리의 타입 안전성이 향상되었습니다.'OPEN' | 'CLOSED' | 'UPCOMING' | 'ALWAYS'리터럴 타입은 컴파일 타임에 유효하지 않은 상태값을 방지합니다.Also applies to: 15-15
78-83: 참고: ClubApiResponse의 recruitmentStatus는 string으로 유지됩니다.
ClubApiResponse.recruitmentStatus가string으로 남아있고Club.recruitmentStatus는RecruitmentStatus로 변경되었습니다. API 응답을 도메인 모델로 변환하는 과정에서 적절한 타입 매핑이 이루어지는지 확인해 주세요.frontend/src/pages/ClubDetailPage/components/ClubProfileCard/ClubProfileCard.tsx (1)
8-9: LGTM! Mixpanel 추적 통합이 잘 되었습니다.SNS 링크 클릭 이벤트 추적이 기존 패턴을 따르고 있으며,
platform과clubName컨텍스트를 포함하여 분석에 유용한 정보를 제공합니다.Also applies to: 30-30, 96-101
frontend/src/hooks/useScrollDetection.ts (1)
3-16: LGTM! 훅 리네이밍과 구현이 적절합니다.
useScrollDetection이라는 이름이 훅의 역할을 더 명확하게 표현합니다. 이벤트 리스너 정리(cleanup)가 올바르게 구현되어 있습니다.frontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.tsx (1)
17-17: LGTM! 날짜 기반 상시모집 판단 로직이 적절합니다.
FAR_FUTURE_YEAR상수와isFarFuture헬퍼 함수를 사용한 상시모집 판단은 코딩 가이드라인(매직 넘버 상수화)을 따르며,handleUpdateClub의 저장 로직과 일관성이 있습니다.Also applies to: 39-40, 52-52
frontend/src/pages/ClubDetailPage/components/ClubDetailFooter/ClubDetailFooter.tsx (1)
5-22: LGTM!
recruitmentStatusprop이 깔끔하게 추가되었으며, 타입 안전성이 유지되고 있습니다.getDeadlineText호출이 날짜 기반 계산에서 상태 기반 로직으로 올바르게 업데이트되었습니다.frontend/src/utils/getDeadLineText.test.ts (1)
3-82: LGTM!테스트 케이스들이 새로운
recruitmentStatus파라미터 기반 로직을 포괄적으로 검증하고 있습니다. OPEN, CLOSED, UPCOMING, ALWAYS 상태에 대한 모든 시나리오가 잘 커버되어 있습니다.frontend/src/pages/AdminPage/tabs/RecruitEditTab/components/Calendar/Calendar.styles.ts (1)
3-222: LGTM!하드코딩된 컬러 값들을 테마 기반 토큰으로 성공적으로 마이그레이션했습니다. 이는 디자인 시스템의 일관성을 높이고 유지보수성을 향상시킵니다.
frontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsx (1)
23-24: LGTM!코드 리팩터링이 잘 되어 있습니다:
- 상태 및 함수 이름이 더 명확하고 설명적으로 변경되었습니다.
- 모집 상태 관련 불리언 변수들(Lines 82-85)이 명확하게 명명되어 조건 로직의 의도가 분명합니다.
renderButtonContent함수가 복잡한 삼항 연산자 대신 if/else를 사용하여 가독성이 향상되었습니다.- Disabled 상태가 적절하게 처리되고 있습니다.
As per coding guidelines, 복잡한 불리언 조건을 명명된 변수에 할당하고 if/else 구조를 사용하여 가독성을 높였습니다.
Also applies to: 28-52, 54-80, 82-103, 105-120
| const days = differenceInCalendarDays(recruitmentEnd, today); | ||
|
|
||
| if (days > 365) return '상시 모집'; | ||
| if (days > 365) return RECRUITMENT_STATUS.ALWAYS; // D-day가 의미 없을 정도로 긴 경우 '상시 모집'으로 표시 |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
매직 넘버 365를 명명된 상수로 추출하세요.
365일 임계값이 하드코딩되어 있습니다. 이 값을 명명된 상수로 추출하면 의도가 명확해지고 향후 조정이 용이해집니다.
🔎 제안하는 리팩터링
+const ALWAYS_RECRUITING_THRESHOLD_DAYS = 365;
+
const RECRUITMENT_STATUS = {
CLOSED: '모집 마감',
ALWAYS: '상시 모집',
UPCOMING: '모집 시작',
};
const getDeadlineText = (
recruitmentStart: Date | null,
recruitmentEnd: Date | null,
recruitmentStatus: string,
today: Date = new Date(),
): string => {
console.log(recruitmentStart, recruitmentEnd, recruitmentStatus, today);
if (recruitmentStatus === 'CLOSED') {
return RECRUITMENT_STATUS.CLOSED;
}
if (recruitmentStatus === 'UPCOMING') {
if (!recruitmentStart) return RECRUITMENT_STATUS.CLOSED;
const hour = recruitmentStart.getHours();
const minute = recruitmentStart.getMinutes();
let formatStr =
hour === 0 && minute === 0
? 'M월 d일'
: 'M월 d일 HH:mm';
return `${format(recruitmentStart, formatStr, { locale: ko })} ${RECRUITMENT_STATUS.UPCOMING}`;
}
if (!recruitmentEnd) return RECRUITMENT_STATUS.CLOSED;
const days = differenceInCalendarDays(recruitmentEnd, today);
- if (days > 365) return RECRUITMENT_STATUS.ALWAYS; // D-day가 의미 없을 정도로 긴 경우 '상시 모집'으로 표시
+ if (days > ALWAYS_RECRUITING_THRESHOLD_DAYS) return RECRUITMENT_STATUS.ALWAYS; // D-day가 의미 없을 정도로 긴 경우 '상시 모집'으로 표시
return days > 0 ? `D-${days}` : 'D-Day';
};Based on learnings, 매직 넘버는 명명된 상수로 교체하여 명확성을 높이고, 로직 근처에 상수를 정의하여 연결이 명확하도록 해야 합니다.
📝 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 (days > 365) return RECRUITMENT_STATUS.ALWAYS; // D-day가 의미 없을 정도로 긴 경우 '상시 모집'으로 표시 | |
| const ALWAYS_RECRUITING_THRESHOLD_DAYS = 365; | |
| const RECRUITMENT_STATUS = { | |
| CLOSED: '모집 마감', | |
| ALWAYS: '상시 모집', | |
| UPCOMING: '모집 시작', | |
| }; | |
| const getDeadlineText = ( | |
| recruitmentStart: Date | null, | |
| recruitmentEnd: Date | null, | |
| recruitmentStatus: string, | |
| today: Date = new Date(), | |
| ): string => { | |
| console.log(recruitmentStart, recruitmentEnd, recruitmentStatus, today); | |
| if (recruitmentStatus === 'CLOSED') { | |
| return RECRUITMENT_STATUS.CLOSED; | |
| } | |
| if (recruitmentStatus === 'UPCOMING') { | |
| if (!recruitmentStart) return RECRUITMENT_STATUS.CLOSED; | |
| const hour = recruitmentStart.getHours(); | |
| const minute = recruitmentStart.getMinutes(); | |
| let formatStr = | |
| hour === 0 && minute === 0 | |
| ? 'M월 d일' | |
| : 'M월 d일 HH:mm'; | |
| return `${format(recruitmentStart, formatStr, { locale: ko })} ${RECRUITMENT_STATUS.UPCOMING}`; | |
| } | |
| if (!recruitmentEnd) return RECRUITMENT_STATUS.CLOSED; | |
| const days = differenceInCalendarDays(recruitmentEnd, today); | |
| if (days > ALWAYS_RECRUITING_THRESHOLD_DAYS) return RECRUITMENT_STATUS.ALWAYS; // D-day가 의미 없을 정도로 긴 경우 '상시 모집'으로 표시 | |
| return days > 0 ? `D-${days}` : 'D-Day'; | |
| }; |
🤖 Prompt for AI Agents
In frontend/src/utils/getDeadLineText.ts around line 37, the literal 365 is a
magic number; extract it into a clearly named constant (e.g., const
ALWAYS_RECRUITMENT_DAYS = 365) defined near the function/logic so its intent is
obvious, replace the hardcoded 365 in the if (days > 365) check with that
constant, and export the constant only if it must be reused elsewhere (otherwise
keep it module-local).
…down-MOA-462 [fix] 불필요한 로그 제거
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (1)
frontend/src/utils/getDeadLineText.ts (1)
35-35: 매직 넘버 365를 명명된 상수로 추출하세요.365일 임계값이 하드코딩되어 있습니다. 이 값을 명명된 상수로 추출하면 의도가 명확해지고 향후 조정이 용이해집니다.
🔎 제안하는 리팩터링
+const ALWAYS_RECRUITING_THRESHOLD_DAYS = 365; + const RECRUITMENT_STATUS = { CLOSED: '모집 마감', ALWAYS: '상시 모집', UPCOMING: '모집 시작', }; const getDeadlineText = ( recruitmentStart: Date | null, recruitmentEnd: Date | null, recruitmentStatus: string, today: Date = new Date(), ): string => { if (recruitmentStatus === 'CLOSED') { return RECRUITMENT_STATUS.CLOSED; } if (recruitmentStatus === 'UPCOMING') { if (!recruitmentStart) return RECRUITMENT_STATUS.CLOSED; const hour = recruitmentStart.getHours(); const minute = recruitmentStart.getMinutes(); let formatStr = hour === 0 && minute === 0 ? 'M월 d일' : 'M월 d일 HH:mm'; return `${format(recruitmentStart, formatStr, { locale: ko })} ${RECRUITMENT_STATUS.UPCOMING}`; } if (!recruitmentEnd) return RECRUITMENT_STATUS.CLOSED; const days = differenceInCalendarDays(recruitmentEnd, today); - if (days > 365) return RECRUITMENT_STATUS.ALWAYS; // D-day가 의미 없을 정도로 긴 경우 '상시 모집'으로 표시 + if (days > ALWAYS_RECRUITING_THRESHOLD_DAYS) return RECRUITMENT_STATUS.ALWAYS; // D-day가 의미 없을 정도로 긴 경우 '상시 모집'으로 표시 return days > 0 ? `D-${days}` : 'D-Day'; };학습된 내용과 코딩 가이드라인에 따라, 매직 넘버는 명명된 상수로 교체하여 명확성을 높이고, 로직 근처에 상수를 정의하여 연결이 명확하도록 해야 합니다.
🧹 Nitpick comments (2)
frontend/src/utils/getDeadLineText.ts (2)
13-13: 타입 안정성을 위해RecruitmentStatus타입을 사용하세요.
recruitmentStatus매개변수가string으로 타입되어 있어 타입 안정성이 부족합니다. AI 요약에 따르면frontend/src/types/club.ts에RecruitmentStatus타입이 정의되어 있으므로, 이를 임포트하여 사용하면 컴파일 타임에 유효하지 않은 상태 값을 감지할 수 있습니다.🔎 제안하는 리팩터링
파일 상단에 타입 임포트를 추가하세요:
+import type { RecruitmentStatus } from '@/types/club'; import { format, differenceInCalendarDays } from 'date-fns'; import { ko } from 'date-fns/locale';그 다음 함수 시그니처를 업데이트하세요:
const getDeadlineText = ( recruitmentStart: Date | null, recruitmentEnd: Date | null, - recruitmentStatus: string, + recruitmentStatus: RecruitmentStatus, today: Date = new Date(), ): string => {코딩 가이드라인에 따라, 일관된 타입 사용으로 유사 함수/훅 간 타입 일관성을 유지해야 합니다.
25-28: 가독성을 위해 삼항 연산자를if/else로 변경하세요.포맷 문자열을 결정하는 복잡한 삼항 연산자를
if/else문으로 변경하면 가독성이 향상됩니다.🔎 제안하는 리팩터링
- let formatStr = - hour === 0 && minute === 0 - ? 'M월 d일' - : 'M월 d일 HH:mm'; + let formatStr: string; + if (hour === 0 && minute === 0) { + formatStr = 'M월 d일'; + } else { + formatStr = 'M월 d일 HH:mm'; + } return `${format(recruitmentStart, formatStr, { locale: ko })} ${RECRUITMENT_STATUS.UPCOMING}`;코딩 가이드라인에 따라, 복잡하거나 중첩된 삼항 연산자는 가독성을 위해
if/else로 교체해야 합니다.
📜 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 (1)
frontend/src/utils/getDeadLineText.ts
🧰 Additional context used
📓 Path-based instructions (2)
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/utils/getDeadLineText.ts
frontend/**/*.{ts,tsx}
📄 CodeRabbit inference engine (frontend/.cursorrules)
Use consistent return types for similar functions/hooks
Files:
frontend/src/utils/getDeadLineText.ts
🧠 Learnings (3)
📚 Learning: 2025-08-24T17:33:23.092Z
Learnt from: suhyun113
Repo: Moadong/moadong PR: 698
File: frontend/src/hooks/queries/applicants/useDeleteApplicants.ts:10-16
Timestamp: 2025-08-24T17:33:23.092Z
Learning: The codebase has inconsistent console logging patterns across files - mixing console.log/console.error for errors, using both Korean and English messages, and various formatting styles (template literals vs separate arguments, with/without emojis). A unified logging format like `console.error('[ModuleName] Operation failed:', error)` should be established for better debugging consistency.
Applied to files:
frontend/src/utils/getDeadLineText.ts
📚 Learning: 2025-11-25T14:08:23.253Z
Learnt from: CR
Repo: Moadong/moadong PR: 0
File: frontend/.cursorrules:0-0
Timestamp: 2025-11-25T14:08:23.253Z
Learning: Applies to frontend/**/*.{ts,tsx,js,jsx} : Define constants near related logic or ensure names link them clearly to avoid silent failures
Applied to files:
frontend/src/utils/getDeadLineText.ts
📚 Learning: 2025-11-25T14:08:23.253Z
Learnt from: CR
Repo: Moadong/moadong PR: 0
File: frontend/.cursorrules:0-0
Timestamp: 2025-11-25T14:08:23.253Z
Learning: Applies to frontend/**/*.{ts,tsx,js,jsx} : Replace magic numbers with named constants for clarity
Applied to files:
frontend/src/utils/getDeadLineText.ts
🔇 Additional comments (1)
frontend/src/utils/getDeadLineText.ts (1)
1-8: LGTM! 명확한 상수 정의와 적절한 임포트입니다.date-fns 라이브러리와 한국어 로케일을 사용한 임포트가 적절하며,
RECRUITMENT_STATUS상수 객체를 통해 한글 라벨을 명확하게 정의했습니다.
#️⃣연관된 이슈
📝작업 내용
중점적으로 리뷰받고 싶은 부분(선택)
논의하고 싶은 부분(선택)
🫡 참고사항
Summary by CodeRabbit
New Features
UI/Style Improvements
Bug Fixes
✏️ Tip: You can customize this high-level summary in your review settings.