[refactor] 모집 기간 시간대(Timezone) 처리 개선 및 API 응답 형식 변경 대응#945
Conversation
- API 응답 형식이 recruitmentStart/End로 분리됨에 따라 더 이상 "시작 ~ 종료" 문자열 파싱이 필요 없음 - parseRecruitmentDateString만 사용하도록 변경
…T 처리 추가 - parseRecruitmentPeriod 함수 제거로 인해 파일명 변경 - 서버에서 KST 시간을 보내므로 파싱 시 +0900 타임존 적용 - 관련 import 경로 수정
- API 응답 형식 변경에 따른 타입 수정
- 기존: recruitmentPeriod: string ("시작 ~ 종료" 단일 문자열)
- 변경: recruitmentStart, recruitmentEnd: string (분리된 필드)
- recruitmentPeriod 파싱 대신 recruitmentStart/End 직접 사용 - correctRequestKoreanDate, correctResponseKoreanDate 함수 제거 - 시간 보정 로직을 recruitmentDateParser로 이동 - toISOString()으로 UTC 형식(Z suffix)으로 서버에 전송
- ClubDetailPage: props 전달 방식 변경 - ClubDetailHeader: interface 수정 (recruitmentPeriod → recruitmentStart/End) - ClubDetailFooter: parseRecruitmentPeriod 대신 recruitmentDateParser 사용 - InfoBox: recruitmentStart/End를 직접 조합하여 표시
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning
|
| Cohort / File(s) | 변경 요약 |
|---|---|
관리자 페이지 탭 frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.tsx, frontend/src/pages/AdminPage/tabs/PhotoEditTab/PhotoEditTab.tsx |
ClubInfoEditTab에서 기본 React import 제거 및 공백 정리. PhotoEditTab에서 인라인 주석 제거(실행 로직 미변경). |
모집 편집 로직 (관리자) frontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.tsx |
기존 parseRecruitmentPeriod 사용을 recruitmentDateParser로 교체; FAR_FUTURE_YEAR, isFarFuture, backupRangeRef 추가; useQueryClient 도입 및 업데이트 성공 시 캐시 무효화 추가; 한글 날짜 보정 헬퍼 제거; 업데이트 페이로드에 ISO 직렬화 사용. |
클럽 상세 페이지 진입 및 전달 frontend/src/pages/ClubDetailPage/ClubDetailPage.tsx |
Header/Footer에 전달하던 recruitmentPeriod/recruitmentForm 호출을 recruitmentStart 및 recruitmentEnd로 분리하여 전달하도록 변경; 모바일 헤더 감지 로직을 useDevice로 단순화. |
클럽 상세 구성요소 변경 frontend/src/pages/ClubDetailPage/components/ClubDetailHeader/ClubDetailHeader.tsx, frontend/src/pages/ClubDetailPage/components/ClubDetailFooter/ClubDetailFooter.tsx, frontend/src/pages/ClubDetailPage/components/InfoBox/InfoBox.tsx |
Header/Footer/InfoBox 인터페이스에서 recruitmentPeriod 제거. Footer·InfoBox는 recruitmentStart/recruitmentEnd를 직접 파싱해 데드라인·표시 로직으로 사용하도록 변경(ClubApplyButton 전달값 조정). |
타입 정의 변경 frontend/src/types/club.ts |
ClubDetail에서 recruitmentPeriod: string 제거, recruitmentStart: string 및 recruitmentEnd: string 추가. |
신규 파서 및 테스트 추가 frontend/src/utils/recruitmentDateParser.ts, frontend/src/utils/recruitmentDateParser.test.ts |
recruitmentDateParser 추가: YYYY.MM.DD HH:mm 형식 검증(정규식) 및 KST 기반 파싱 → UTC Date 반환; 정상/오류 케이스 단위테스트 추가. |
레거시 파서 및 테스트 제거 frontend/src/utils/recruitmentPeriodParser.ts (삭제), frontend/src/utils/parseRecruitmentPeriod.test.ts (삭제) |
기존 parseRecruitmentPeriod 유틸과 관련 테스트 파일 삭제. |
Sequence Diagram(s)
mermaid
sequenceDiagram
participant Admin as 관리자(UI)
participant RecruitTab as RecruitEditTab
participant Parser as recruitmentDateParser
participant API as 서버(API)
participant Query as ReactQuery (queryClient)
participant ClubPage as ClubDetailPage
participant Footer as ClubDetailFooter
Admin->>RecruitTab: 모집 시작/종료 입력 및 저장 트리거
RecruitTab->>Parser: 입력값 검증·KST→UTC 파싱 (recruitmentDateParser)
Parser-->>RecruitTab: Date(UTC)
RecruitTab->>API: 수정 요청 (ISO 문자열로 전송)
API-->>RecruitTab: 200 OK
RecruitTab->>Query: 관련 쿼리 무효화(invalidateQueries)
Query-->>ClubPage: 최신 데이터로 리패치
ClubPage->>Footer: recruitmentStart/recruitmentEnd 전달
Footer->>Parser: (선택적) 날짜 파싱/데드라인 계산
Footer-->>ClubPage: 데드라인/버튼 렌더링
Estimated code review effort
🎯 4 (Complex) | ⏱️ ~45분
- 추가로 주의할 파일/영역:
frontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.tsx— 초기 파싱 로직, 상시모집(FAR_FUTURE) 동작, backupRangeRef 보존/복원, 쿼리 무효화 흐름frontend/src/utils/recruitmentDateParser.ts및recruitmentDateParser.test.ts— 정규식 에지케이스와 KST→UTC 변환(테스트 기대값 일치)- 타입 전파 영향:
frontend/src/types/club.ts변경으로 인한 컴포넌트(ClubDetailPage, Header/Footer, InfoBox 등) 호환성
Possibly related issues
- MOA-433: 모집 기간 API 응답 형식 변경 및 KST/UTC 시간대 처리 개선 — recruitmentPeriod → recruitmentStart/recruitmentEnd 분리 및 KST→UTC 파서 도입 목표와 일치합니다.
- [refactor] MOA-433 모집 기간 API 응답 형식 변경 및 KST/UTC 시간대 처리 개선 #944 — recruitmentStart/recruitmentEnd 전환 및 시간대 처리 개선을 다루는 관련 이슈입니다.
Possibly related PRs
- [feature] 관리자 페이지 모집기간 선택에 상시모집 처리 버튼을 추가한다 #774 — RecruitEditTab의 상시모집(FAR_FUTURE_YEAR) 및 캘린더/버튼 로직 변경과 직접 관련.
- [fix] 모집기간 설정 시간 보정 #690 — RecruitEditTab에서 한글 시간 보정 접근 방식과 교차 영향 가능성.
- [release] v1.0.9 #607 — ClubDetailHeader/ClubDetailFooter의 모집 기간 처리·props 변경과 중복 지점 존재.
Suggested labels
📬 API
Suggested reviewers
- Zepelown
- lepitaaar
- oesnuj
- suhyun113
Pre-merge checks and finishing touches
✅ Passed checks (5 passed)
| Check name | Status | Explanation |
|---|---|---|
| Description Check | ✅ Passed | Check skipped - CodeRabbit’s high-level summary is enabled. |
| Title check | ✅ Passed | 제목이 PR의 주요 변경사항을 정확히 반영합니다: 모집 기간 시간대 처리 개선과 API 응답 형식 변경이 모두 포함되어 있습니다. |
| Linked Issues check | ✅ Passed | recruitmentPeriod를 recruitmentStart/recruitmentEnd로 분리했고, UTC 형식 전송 및 KST 파싱 로직을 구현하여 모든 요구사항을 충족합니다. |
| Out of Scope Changes check | ✅ Passed | React import 제거, 주석 삭제, 날짜 파싱 유틸리티 교체 등 모든 변경사항이 시간대 처리 개선 목표와 관련되어 있습니다. |
| Docstring Coverage | ✅ Passed | No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. |
✨ Finishing touches
- 📝 Generate docstrings
🧪 Generate unit tests (beta)
- Create PR with unit tests
- Post copyable unit tests in a comment
- Commit unit tests in branch
refactor/#944-recruitment-date-utc-kst-handling-MOA-433
📜 Recent review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Jira integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
frontend/src/pages/ClubDetailPage/ClubDetailPage.tsx(3 hunks)frontend/src/pages/ClubDetailPage/components/ClubDetailFooter/ClubDetailFooter.tsx(1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
frontend/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (frontend/.cursorrules)
frontend/**/*.{ts,tsx,js,jsx}: Replace magic numbers with named constants for clarity
Replace complex/nested ternaries 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/ClubDetailPage.tsxfrontend/src/pages/ClubDetailPage/components/ClubDetailFooter/ClubDetailFooter.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/ClubDetailFooter/ClubDetailFooter.tsx
frontend/**/*.{ts,tsx}
📄 CodeRabbit inference engine (frontend/.cursorrules)
Use consistent return types for similar functions/hooks
Files:
frontend/src/pages/ClubDetailPage/ClubDetailPage.tsxfrontend/src/pages/ClubDetailPage/components/ClubDetailFooter/ClubDetailFooter.tsx
🧠 Learnings (2)
📚 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/ClubDetailPage/components/ClubDetailFooter/ClubDetailFooter.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/ClubDetailFooter/ClubDetailFooter.tsx
🧬 Code graph analysis (1)
frontend/src/pages/ClubDetailPage/components/ClubDetailFooter/ClubDetailFooter.tsx (1)
frontend/src/utils/recruitmentDateParser.ts (1)
recruitmentDateParser(3-17)
🔇 Additional comments (5)
frontend/src/pages/ClubDetailPage/components/ClubDetailFooter/ClubDetailFooter.tsx (3)
3-4: LGTM! import 변경이 적절합니다.기존
recruitmentPeriodParser대신 새로운recruitmentDateParser유틸리티를 사용하도록 import가 올바르게 업데이트되었습니다.
6-9: LGTM! 인터페이스가 API 응답 형식 변경에 맞게 업데이트되었습니다.
recruitmentPeriod단일 문자열 대신recruitmentStart와recruitmentEnd로 분리된 필드를 사용하도록 변경되었습니다. 이전 리뷰에서 지적된 사용하지 않는recruitmentFormprop도 제거되었습니다.
21-21: LGTM! 마감일 텍스트 계산 로직이 적절합니다.파싱된
startDate와endDate를getDeadlineText에 전달하여 마감일 텍스트를 올바르게 계산합니다.frontend/src/pages/ClubDetailPage/ClubDetailPage.tsx (2)
17-17: LGTM! 헤더 표시 로직이 간소화되었습니다.이전 리뷰에서 제안된 대로
useDevice훅을 사용하여isMobile상태를 직접 계산하고, 별도의useState와useEffect없이!isMobile로 헤더 표시 여부를 결정합니다. 코드가 더 간결하고 유지보수하기 쉬워졌습니다.Also applies to: 22-22, 38-38
62-65: LGTM! API 응답 형식 변경에 맞게 props가 업데이트되었습니다.
ClubDetailFooter에recruitmentStart와recruitmentEnd를 전달하여 새로운 API 응답 형식과 일관성을 유지합니다.
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: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
frontend/src/pages/ClubDetailPage/components/ClubDetailFooter/ClubDetailFooter.tsx (1)
12-28:recruitmentDateParser호출 시 에러 처리 필수
recruitmentDateParser는 유효하지 않은 날짜 형식에서 예외를 throw하며, ClubDetailFooter에서 렌더 단계 중에 이를 try-catch 없이 호출합니다. 현재 null 체크만 있을 뿐 형식 검증이 없으므로, API가 잘못된 형식을 반환하면 컴포넌트가 충돌합니다. 전체 앱에 에러 바운더리가 없으므로 전체 React 트리가 언마운트될 수 있습니다.다음 중 하나를 선택하여 구현하세요:
recruitmentDateParser호출을 try-catch로 감싸고 유효하지 않은 경우 null 또는 기본값 반환- 데이터 검증 유틸리티를 추가하여 parser 호출 전 형식 확인
- 앱 최상위에 Error Boundary 추가 (라우트 레벨)
🧹 Nitpick comments (2)
frontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.tsx (1)
17-17: 상수에 대한 설명 주석 추가 권장
FAR_FUTURE_YEAR = 2999가 '상시모집' 상태를 나타내기 위한 특수 값임을 명시하는 주석을 추가하면 코드 이해도가 높아집니다. 코딩 가이드라인에 따라 상수와 관련 로직의 연결을 명확히 해주세요.🔎 주석 추가 제안
+/** 상시모집 상태를 나타내기 위한 먼 미래 연도 값 */ const FAR_FUTURE_YEAR = 2999;frontend/src/pages/ClubDetailPage/components/ClubDetailHeader/ClubDetailHeader.tsx (1)
10-14: 불필요한 props 정의 제거 필요
recruitmentStart,recruitmentEnd,recruitmentForm,presidentPhoneNumberprops가 인터페이스에 정의되어 있지만 컴포넌트 내에서 destructuring되거나 사용되지 않으며, 자식 컴포넌트(ClubProfile)에 전달되지도 않습니다. Props Drilling 패턴을 방지하기 위해 사용되지 않는 props는 인터페이스에서 제거하는 것이 좋습니다.
📜 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 (12)
frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.tsx(1 hunks)frontend/src/pages/AdminPage/tabs/PhotoEditTab/PhotoEditTab.tsx(0 hunks)frontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.tsx(7 hunks)frontend/src/pages/ClubDetailPage/ClubDetailPage.tsx(3 hunks)frontend/src/pages/ClubDetailPage/components/ClubDetailFooter/ClubDetailFooter.tsx(1 hunks)frontend/src/pages/ClubDetailPage/components/ClubDetailHeader/ClubDetailHeader.tsx(1 hunks)frontend/src/pages/ClubDetailPage/components/InfoBox/InfoBox.tsx(1 hunks)frontend/src/types/club.ts(1 hunks)frontend/src/utils/parseRecruitmentPeriod.test.ts(0 hunks)frontend/src/utils/recruitmentDateParser.test.ts(1 hunks)frontend/src/utils/recruitmentDateParser.ts(1 hunks)frontend/src/utils/recruitmentPeriodParser.ts(0 hunks)
💤 Files with no reviewable changes (3)
- frontend/src/utils/parseRecruitmentPeriod.test.ts
- frontend/src/utils/recruitmentPeriodParser.ts
- frontend/src/pages/AdminPage/tabs/PhotoEditTab/PhotoEditTab.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/InfoBox/InfoBox.tsxfrontend/src/utils/recruitmentDateParser.tsfrontend/src/utils/recruitmentDateParser.test.tsfrontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.tsxfrontend/src/pages/ClubDetailPage/components/ClubDetailHeader/ClubDetailHeader.tsxfrontend/src/pages/ClubDetailPage/components/ClubDetailFooter/ClubDetailFooter.tsxfrontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.tsxfrontend/src/types/club.tsfrontend/src/pages/ClubDetailPage/ClubDetailPage.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/components/InfoBox/InfoBox.tsxfrontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.tsxfrontend/src/pages/ClubDetailPage/components/ClubDetailHeader/ClubDetailHeader.tsxfrontend/src/pages/ClubDetailPage/components/ClubDetailFooter/ClubDetailFooter.tsxfrontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.tsxfrontend/src/pages/ClubDetailPage/ClubDetailPage.tsx
frontend/**/*.{ts,tsx}
📄 CodeRabbit inference engine (frontend/.cursorrules)
Use consistent return types for similar functions/hooks
Files:
frontend/src/pages/ClubDetailPage/components/InfoBox/InfoBox.tsxfrontend/src/utils/recruitmentDateParser.tsfrontend/src/utils/recruitmentDateParser.test.tsfrontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.tsxfrontend/src/pages/ClubDetailPage/components/ClubDetailHeader/ClubDetailHeader.tsxfrontend/src/pages/ClubDetailPage/components/ClubDetailFooter/ClubDetailFooter.tsxfrontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.tsxfrontend/src/types/club.tsfrontend/src/pages/ClubDetailPage/ClubDetailPage.tsx
🧠 Learnings (7)
📚 Learning: 2025-03-19T05:18:07.818Z
Learnt from: seongwon030
Repo: Moadong/moadong PR: 195
File: frontend/src/pages/AdminPage/AdminPage.tsx:7-7
Timestamp: 2025-03-19T05:18:07.818Z
Learning: AdminPage.tsx에서 현재 하드코딩된 클럽 ID('67d2e3b9b15c136c6acbf20b')는 로그인 기능 구현 후 동적으로 가져오는 방식으로 수정될 예정입니다.
Applied to files:
frontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.tsxfrontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.tsxfrontend/src/pages/ClubDetailPage/ClubDetailPage.tsx
📚 Learning: 2025-11-25T14:08:23.253Z
Learnt from: CR
Repo: Moadong/moadong PR: 0
File: frontend/.cursorrules:0-0
Timestamp: 2025-11-25T14:08:23.253Z
Learning: Applies to frontend/**/*.{tsx,jsx} : Colocate simple, localized logic or use inline definitions to reduce context switching
Applied to files:
frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.tsx
📚 Learning: 2025-11-25T14:08:23.253Z
Learnt from: CR
Repo: Moadong/moadong PR: 0
File: frontend/.cursorrules:0-0
Timestamp: 2025-11-25T14:08:23.253Z
Learning: Applies to frontend/**/*.{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} : Abstract complex logic/interactions into dedicated components/HOCs
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} : Use Component Composition instead of Props Drilling 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} : Choose field-level or form-level cohesion based on form requirements when using form libraries like react-hook-form
Applied to files:
frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.tsx
📚 Learning: 2025-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
🧬 Code graph analysis (3)
frontend/src/utils/recruitmentDateParser.test.ts (1)
frontend/src/utils/recruitmentDateParser.ts (1)
recruitmentDateParser(3-17)
frontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.tsx (4)
frontend/src/types/club.ts (1)
ClubDetail(16-29)frontend/src/apis/updateClubDescription.ts (1)
updateClubDescription(5-37)frontend/src/hooks/queries/club/useUpdateClubDescription.ts (1)
useUpdateClubDescription(5-14)frontend/src/utils/recruitmentDateParser.ts (1)
recruitmentDateParser(3-17)
frontend/src/pages/ClubDetailPage/components/ClubDetailFooter/ClubDetailFooter.tsx (1)
frontend/src/utils/recruitmentDateParser.ts (1)
recruitmentDateParser(3-17)
🔇 Additional comments (9)
frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.tsx (1)
1-1: LGTM!React 19의 새로운 JSX 런타임에 맞게 기본 React import를 제거하고 named import만 사용하도록 변경되었습니다.
frontend/src/pages/ClubDetailPage/components/InfoBox/InfoBox.tsx (1)
27-32:recruitmentStart또는recruitmentEnd가 빈 값일 때 예외 처리 검토 필요
recruitmentStatus가 '상시모집'이 아닐 때,recruitmentStart나recruitmentEnd가 빈 문자열이거나 undefined인 경우 "undefined ~ undefined" 또는 " ~ " 같은 잘못된 문자열이 표시될 수 있습니다.🔎 방어적 처리 제안
const recruitmentPeriodDisplay = (() => { const isAlways = clubDetail.recruitmentStatus === '상시모집'; - return isAlways - ? '상시모집' - : clubDetail.recruitmentStart + ' ~ ' + clubDetail.recruitmentEnd; + if (isAlways) return '상시모집'; + if (!clubDetail.recruitmentStart || !clubDetail.recruitmentEnd) { + return '모집기간 미정'; + } + return `${clubDetail.recruitmentStart} ~ ${clubDetail.recruitmentEnd}`; })();frontend/src/utils/recruitmentDateParser.ts (1)
1-17: LGTM - KST → UTC 변환 로직 올바르게 구현됨
+0900타임존 오프셋을 추가하여 date-fns로 파싱하는 방식이 적절합니다. regex 검증과isValid체크로 이중 검증을 수행하여 안정성이 확보되었습니다.frontend/src/types/club.ts (1)
23-24: LGTM - API 응답 형식 변경에 맞춘 타입 정의
recruitmentPeriod단일 문자열에서recruitmentStart와recruitmentEnd분리 필드로 타입이 올바르게 업데이트되었습니다.frontend/src/utils/recruitmentDateParser.test.ts (1)
4-14: LGTM - KST → UTC 변환 테스트 케이스 검증 완료자정(00:00 KST → 전날 15:00 UTC) 케이스를 포함하여 타임존 변환 로직이 올바르게 테스트되고 있습니다.
frontend/src/pages/ClubDetailPage/ClubDetailPage.tsx (1)
60-61: LGTM -recruitmentStart/recruitmentEndprops 전달 변경 완료
ClubDetailHeader와ClubDetailFooter에 분리된 모집 기간 props가 올바르게 전달되고 있습니다.Also applies to: 79-80
frontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.tsx (2)
116-123:toISOString()호출 시 null 케이스 처리 확인 필요
startForSave와endForSave가 null일 경우toISOString()은 호출되지 않고undefined가 됩니다. 서버 API가 이 값들을 필수로 요구하는지 확인이 필요합니다. null인 경우 요청 자체를 막거나, 기본값을 제공하는 방어 로직을 추가하는 것이 안전합니다.🔎 방어 로직 제안
const handleUpdateClub = async () => { trackEvent(ADMIN_EVENT.UPDATE_RECRUIT_BUTTON_CLICKED); if (!clubDetail) return; + if (!recruitmentStart) { + alert('모집 시작일을 설정해주세요.'); + return; + } + let startForSave: Date | null = recruitmentStart; let endForSave: Date | null = recruitmentEnd;
34-40: LGTM - 상시모집 토글 시 날짜 백업/복원 로직 잘 구현됨
backupRangeRef를 사용하여 상시모집 전환 시 이전 날짜 범위를 보존하고, 비활성화 시 복원하는 로직이 적절합니다. 백업 자체가 상시모집이었던 엣지 케이스도isFarFuture헬퍼로 처리하고 있습니다.Also applies to: 73-101
frontend/src/pages/ClubDetailPage/components/ClubDetailFooter/ClubDetailFooter.tsx (1)
1-4: LGTM! 임포트 변경이 올바르게 적용되었습니다.새로운
recruitmentDateParser유틸리티로의 마이그레이션이 PR 목표와 일치하며, 코드가 명확합니다.
frontend/src/pages/ClubDetailPage/components/ClubDetailFooter/ClubDetailFooter.tsx
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 2
📜 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 (3)
frontend/src/pages/ClubDetailPage/ClubDetailPage.tsx(3 hunks)frontend/src/pages/ClubDetailPage/components/ClubDetailFooter/ClubDetailFooter.tsx(1 hunks)frontend/src/pages/ClubDetailPage/components/ClubDetailHeader/ClubDetailHeader.tsx(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- frontend/src/pages/ClubDetailPage/ClubDetailPage.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/ClubDetailHeader/ClubDetailHeader.tsxfrontend/src/pages/ClubDetailPage/components/ClubDetailFooter/ClubDetailFooter.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/components/ClubDetailHeader/ClubDetailHeader.tsxfrontend/src/pages/ClubDetailPage/components/ClubDetailFooter/ClubDetailFooter.tsx
frontend/**/*.{ts,tsx}
📄 CodeRabbit inference engine (frontend/.cursorrules)
Use consistent return types for similar functions/hooks
Files:
frontend/src/pages/ClubDetailPage/components/ClubDetailHeader/ClubDetailHeader.tsxfrontend/src/pages/ClubDetailPage/components/ClubDetailFooter/ClubDetailFooter.tsx
🧠 Learnings (1)
📚 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/ClubDetailFooter/ClubDetailFooter.tsx
🧬 Code graph analysis (1)
frontend/src/pages/ClubDetailPage/components/ClubDetailFooter/ClubDetailFooter.tsx (1)
frontend/src/utils/recruitmentDateParser.ts (1)
recruitmentDateParser(3-17)
🔇 Additional comments (2)
frontend/src/pages/ClubDetailPage/components/ClubDetailFooter/ClubDetailFooter.tsx (2)
6-9: 이전 리뷰 코멘트가 해결되었습니다.이전 리뷰에서 지적되었던 사용되지 않는
recruitmentFormprop이 제거되었고, 새로운recruitmentStart와recruitmentEndprop으로 대체되었습니다. 두 prop 모두 컴포넌트 본문에서 실제로 사용되고 있어 문제가 해결되었습니다.
20-20:getDeadlineText함수가null파라미터를 올바르게 처리합니다.함수의 타입 시그니처에서 파라미터가
Date | null로 명시되어 있으며, 첫 번째 줄에서if (!recruitmentStart || !recruitmentEnd) return '모집 마감';으로null값을 처리합니다. 테스트 코드에서도getDeadlineText(null, null)에 대해 올바르게'모집 마감'을 반환하는 것이 확인되었습니다.Likely an incorrect or invalid review comment.
frontend/src/pages/ClubDetailPage/components/ClubDetailFooter/ClubDetailFooter.tsx
Show resolved
Hide resolved
frontend/src/pages/ClubDetailPage/components/ClubDetailHeader/ClubDetailHeader.tsx
Outdated
Show resolved
Hide resolved
- 사용자 관점의 테스트명으로 변경 - "모집 마감 날짜 입력 처리" describe 블록으로 통일
- ClubDetailPage.ts - ClubDetailHeader
- 하드코딩된 `MobileWindowWidth` 상수와 `window.innerWidth` 직접 비교 로직 제거 - `useDevice` 훅의 `isMobile`을 사용하여 일관된 모바일 환경 감지 적용 - 불필요한 코드 정리 및 가독성 향상
oesnuj
left a comment
There was a problem hiding this comment.
드디어 해결되었네요...! 수고하셨습니다
기존: recruitmentPeriod: "2025.12.07 10:00 ~ 2025.12.07 22:00" (단일 문자열)
변경: recruitmentStart, recruitmentEnd (분리된 필드)
- 불필요한 resize 이벤트 리스너 및 local state(`showHeader`) 제거 - useDevice 훅의 isMobile값을 직접 사용하여 헤더 렌더링 조건
#️⃣연관된 이슈
📝작업 내용
문제
사용자가 22시 설정했을 때 → 13시로 저장되었습니다.
이는 프론트-서버 간 시간대 처리가 일치하지 않았기 때문입니다.
1. API 응답 형식 변경 대응
2. 시간대 처리 개선
3. 파일 리팩토링
recruitmentPeriodParser.ts→recruitmentDateParser.ts파일명 변경parseRecruitmentPeriod함수 제거 (더 이상 불필요)correctRequestKoreanDate,correctResponseKoreanDate함수 제거중점적으로 리뷰받고 싶은 부분(선택)
논의하고 싶은 부분(선택)
🫡 참고사항
Summary by CodeRabbit
리팩토링
신규 기능
테스트
✏️ Tip: You can customize this high-level summary in your review settings.