Conversation
- 응답에 유효한 데이터베이스 ID가 있는 경우 선택한 Notion 데이터베이스 ID를 설정
- Google Calendar 관련 쿼리 키 및 훅 추가 - Google Calendar 캘린더 및 이벤트 조회 기능 구현 - 선택된 캘린더 관리 및 연결 해제 기능 추가 - 기존 코드 리팩토링 및 상태 관리 개선
- 요청 ID를 사용하여 이전 요청의 응답을 무효화하는 로직 추가 - 페이지 로드 및 데이터베이스 페이지 적용 시 요청 ID 확인 추가
- useRef를 사용하여 초기화 상태를 추적하는 로직 추가 - notionCalendarEvents가 비어있거나 초기화가 완료된 경우 useEffect에서 조기 반환하도록 수정
- 오류 발생 시 사용자에게 명확한 메시지 제공 - 세션 스토리지에서 상태 키 제거 및 URL에서 OAuth 매개변수 정리 로직 추가
- useRef를 사용하여 초기화 상태를 추적하는 로직 추가 - allUnifiedEvents가 비어있거나 초기화가 완료된 경우 useEffect에서 조기 반환하도록 수정
- activeTab 상태를 useMemo를 사용하여 계산하도록 변경 - SCHEDULE 탭에서 캘린더 이벤트가 없을 경우 기본 탭으로 돌아가는 useEffect 추가 - 코드 가독성을 높이기 위해 불필요한 초기화 로직 제거
- useRef를 사용하여 초기화 상태를 추적하는 로직 추가 - parsedEvents가 비어있거나 초기화가 완료된 경우 useEffect에서 조기 반환하도록 수정 - 캘린더의 첫 번째 이벤트에 따라 기본 선택 날짜 및 표시 월 설정 로직 추가
- 클럽 이름을 URL에 안전하게 포함하기 위해 encodeURIComponent 사용
- articles 배열에서 유효한 timestamp를 필터링하여 최대값을 반환하도록 수정 - 빈 timestamps 배열에 대한 처리를 추가하여 0을 반환하도록 변경
- globalThis.crypto.randomUUID가 지원되지 않는 경우를 대비하여 대체 로직 추가 - Uint8Array를 사용하여 안전한 난수 문자열 생성 방식으로 변경
- 날짜 배열 생성을 위한 로직을 addDays 함수를 사용하여 간결하게 변경 - 코드 가독성을 높이기 위해 불필요한 변수 제거
- 유효하지 않은 날짜 문자열에 대한 처리를 추가하여 NaN 반환 시 빈 문자열을 반환하도록 수정
- 기본 정렬 로직을 수정하여 aStart와 bStart의 차이를 반환하도록 변경하여 정렬 정확도 향상
- hasCalendarEvents를 hasCalendarConnection으로 변경하여 클럽의 캘린더 연결 상태를 정확하게 반영
- useRef를 사용하여 캘린더 데이터 로딩 상태를 추적하는 hasLoadedOnce 추가 - isInitialChecking 상태를 개선하여 초기 로딩 후에만 true로 설정되도록 수정
- clubId 또는 clubName을 key prop으로 사용하여 컴포넌트의 재렌더링 최적화
[Fix] pr review 1386
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
frontend/src/pages/ClubDetailPage/ClubDetailPage.tsx (1)
74-77:⚠️ Potential issue | 🔴 Critical이전 지적이 아직 남아 있습니다: 일정 조회에는 실제
clubId만 전달해야 합니다.Line 75의
clubNamefallback 때문에 이름 기반 URL에서는/api/club/{clubName}/calendar-events요청이 나갑니다.getClubCalendarEvents는 ID 기반 엔드포인트라 이 경우 일정 탭이 그대로 깨집니다. 최소한 여기서는clubNamefallback를 제거하고 ID만 넘겨야 하고, 이름만 있는 경로도 지원해야 한다면 먼저 실제 ID를 해석한 뒤 그 값을 사용해야 합니다.최소 수정 예시
const { data: calendarEvents = [] } = useGetClubCalendarEvents( - (clubName ?? clubId) || '', + clubId || '', { enabled: hasCalendarConnection && activeTab === TAB_TYPE.SCHEDULE }, );🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/src/pages/ClubDetailPage/ClubDetailPage.tsx` around lines 74 - 77, The calendar fetch is incorrectly falling back to clubName; update the useGetClubCalendarEvents call to pass only the numeric clubId (remove the clubName ?? clubId fallback) so the ID-based endpoint is used: change the first argument to clubId || '' and keep the options check (hasCalendarConnection && activeTab === TAB_TYPE.SCHEDULE); if routes can provide name-only, implement a preceding resolution step that maps clubName to clubId before calling useGetClubCalendarEvents (resolve the ID first, then call useGetClubCalendarEvents with the resolved ID).
🧹 Nitpick comments (3)
frontend/src/pages/AdminPage/tabs/CalendarSyncTab/hooks/useNotionCalendarData.ts (1)
124-126:loadNotionPages의존성으로 인해 불필요한 재실행 가능성이 있습니다.
loadNotionPages가onError콜백에 의존하므로, 부모 컴포넌트에서onError가 매 렌더마다 새로 생성되면 이 효과가 반복 실행될 수 있습니다. 초기 로드가 한 번만 실행되도록 보장하려면 ref 플래그를 사용하는 것을 고려해 보세요.초기 로드 한 번만 실행하도록 수정
+ const didInitialLoadRef = useRef(false); + useEffect(() => { + if (didInitialLoadRef.current) return; + didInitialLoadRef.current = true; loadNotionPages(); }, [loadNotionPages]);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/src/pages/AdminPage/tabs/CalendarSyncTab/hooks/useNotionCalendarData.ts` around lines 124 - 126, The effect currently depends on loadNotionPages which can change when its onError callback changes, causing unnecessary re-runs; modify useNotionPages usage so the initial load runs only once by adding a ref guard: inside useEffect check a useRef flag (e.g., hasLoadedRef) and call loadNotionPages() only if not set, then set the flag, and keep the effect deps minimal (empty array) or ensure loadNotionPages is stable via useCallback; reference the loadNotionPages function and the onError dependency when making the change.frontend/src/utils/calendarSyncUtils.ts (1)
168-205: Notion 속성 파싱에서 타입 안전성 개선을 권장합니다.
Object.values(properties)를Array<Record<string, unknown>>으로 캐스팅하고 있습니다.NotionSearchItem.properties가Record<string, unknown>으로 정의되어 있어 현재 코드가 동작하지만, 속성 값의 구조가 예상과 다를 경우 런타임에 조용히 실패합니다.타입 가드 함수 추출 제안
+interface NotionDateProperty { + type: 'date'; + date: { start: string; end?: string | null }; +} + +interface NotionTitleProperty { + type: 'title'; + title: Array<{ plain_text?: string }>; +} + +const isDateProperty = (prop: unknown): prop is NotionDateProperty => + typeof prop === 'object' && + prop !== null && + (prop as Record<string, unknown>).type === 'date' && + typeof ((prop as Record<string, unknown>).date as Record<string, unknown>)?.start === 'string'; + +const isTitleProperty = (prop: unknown): prop is NotionTitleProperty => + typeof prop === 'object' && + prop !== null && + (prop as Record<string, unknown>).type === 'title' && + Array.isArray((prop as Record<string, unknown>).title);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/src/utils/calendarSyncUtils.ts` around lines 168 - 205, The parsing in parseNotionCalendarEvent uses a blanket cast of Object.values(properties) to Array<Record<string, unknown>> which is unsafe; replace the cast by adding lightweight type-guard checks when locating the date and title properties (e.g., validate property.type === 'date' && typeof property.date === 'object' && typeof property.date.start === 'string' for dateProperty, and property.type === 'title' && Array.isArray(property.title) && every segment has typeof segment.plain_text === 'string' for titleProperty), ensure parseDateKey(date) is only called after these guards and return null when shapes don't match; update references to dateProperty, titleProperty, parseNotionCalendarEvent and NotionSearchItem.properties accordingly so the function fails fast and type-safely instead of relying on a cast.frontend/src/pages/AdminPage/tabs/CalendarSyncTab/hooks/useGoogleCalendarData.ts (1)
34-40:eventTimeRange가 마운트 시점에 고정됩니다.
useMemo의 의존성 배열이 비어 있어 컴포넌트가 오래 마운트되어 있으면 시간 범위가 업데이트되지 않습니다. 일반적인 사용에서는 문제가 없지만, 장기 세션에서 이벤트 조회 범위가 stale해질 수 있습니다.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/src/pages/AdminPage/tabs/CalendarSyncTab/hooks/useGoogleCalendarData.ts` around lines 34 - 40, eventTimeRange is being memoized with an empty dependency array so it never updates after mount; replace the useMemo block with a plain computed value (i.e., remove useMemo and just compute const eventTimeRange = { timeMin: ..., timeMax: ... } using a fresh new Date() on each render) so the date range reflects the current time, or alternatively add a relevant dependency (e.g., a currentTime state/prop) if you want controlled updates; update the code around the eventTimeRange constant in useGoogleCalendarData accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@frontend/.eslintignore`:
- Line 1: The repository currently uses a deprecated .eslintignore file
(frontend/.eslintignore) which ESLint 9.x ignores; replace this by adding an
eslint.config.js that exports a flat config array and include an entry with the
ignores property to match 'public/mockServiceWorker.js' (e.g. export default [{
ignores: ['public/mockServiceWorker.js'] }, ...]); remove the legacy
.eslintignore or keep it cleared so the ignore is enforced via the new
eslint.config.js and ensure your CI/linter picks up eslint.config.js.
---
Duplicate comments:
In `@frontend/src/pages/ClubDetailPage/ClubDetailPage.tsx`:
- Around line 74-77: The calendar fetch is incorrectly falling back to clubName;
update the useGetClubCalendarEvents call to pass only the numeric clubId (remove
the clubName ?? clubId fallback) so the ID-based endpoint is used: change the
first argument to clubId || '' and keep the options check (hasCalendarConnection
&& activeTab === TAB_TYPE.SCHEDULE); if routes can provide name-only, implement
a preceding resolution step that maps clubName to clubId before calling
useGetClubCalendarEvents (resolve the ID first, then call
useGetClubCalendarEvents with the resolved ID).
---
Nitpick comments:
In
`@frontend/src/pages/AdminPage/tabs/CalendarSyncTab/hooks/useGoogleCalendarData.ts`:
- Around line 34-40: eventTimeRange is being memoized with an empty dependency
array so it never updates after mount; replace the useMemo block with a plain
computed value (i.e., remove useMemo and just compute const eventTimeRange = {
timeMin: ..., timeMax: ... } using a fresh new Date() on each render) so the
date range reflects the current time, or alternatively add a relevant dependency
(e.g., a currentTime state/prop) if you want controlled updates; update the code
around the eventTimeRange constant in useGoogleCalendarData accordingly.
In
`@frontend/src/pages/AdminPage/tabs/CalendarSyncTab/hooks/useNotionCalendarData.ts`:
- Around line 124-126: The effect currently depends on loadNotionPages which can
change when its onError callback changes, causing unnecessary re-runs; modify
useNotionPages usage so the initial load runs only once by adding a ref guard:
inside useEffect check a useRef flag (e.g., hasLoadedRef) and call
loadNotionPages() only if not set, then set the flag, and keep the effect deps
minimal (empty array) or ensure loadNotionPages is stable via useCallback;
reference the loadNotionPages function and the onError dependency when making
the change.
In `@frontend/src/utils/calendarSyncUtils.ts`:
- Around line 168-205: The parsing in parseNotionCalendarEvent uses a blanket
cast of Object.values(properties) to Array<Record<string, unknown>> which is
unsafe; replace the cast by adding lightweight type-guard checks when locating
the date and title properties (e.g., validate property.type === 'date' && typeof
property.date === 'object' && typeof property.date.start === 'string' for
dateProperty, and property.type === 'title' && Array.isArray(property.title) &&
every segment has typeof segment.plain_text === 'string' for titleProperty),
ensure parseDateKey(date) is only called after these guards and return null when
shapes don't match; update references to dateProperty, titleProperty,
parseNotionCalendarEvent and NotionSearchItem.properties accordingly so the
function fails fast and type-safely instead of relying on a cast.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: c287ee46-6781-4092-a0c3-c81625867db2
📒 Files selected for processing (18)
frontend/.eslintignorefrontend/CLAUDE.mdfrontend/src/constants/queryKeys.tsfrontend/src/hooks/Queries/useGoogleCalendar.tsfrontend/src/pages/AdminPage/tabs/CalendarSyncTab/CalendarSyncTab.styles.tsfrontend/src/pages/AdminPage/tabs/CalendarSyncTab/hooks/useGoogleCalendarData.tsfrontend/src/pages/AdminPage/tabs/CalendarSyncTab/hooks/useNotionCalendarData.tsfrontend/src/pages/AdminPage/tabs/CalendarSyncTab/hooks/useNotionCalendarUiState.tsfrontend/src/pages/AdminPage/tabs/CalendarSyncTab/hooks/useNotionOAuth.tsfrontend/src/pages/AdminPage/tabs/CalendarSyncTab/hooks/useUnifiedCalendarUiState.tsfrontend/src/pages/ClubDetailPage/ClubDetailPage.tsxfrontend/src/pages/ClubDetailPage/components/ClubScheduleCalendar/ClubScheduleCalendar.tsxfrontend/src/pages/PromotionPage/components/detail/PromotionClubCTA/PromotionClubCTA.tsxfrontend/src/pages/PromotionPage/utils/promotionNotification.tsfrontend/src/pages/PromotionPage/utils/sortPromotions.tsfrontend/src/types/club.tsfrontend/src/utils/calendarSyncUtils.tsfrontend/src/utils/formatKSTDateTime.ts
💤 Files with no reviewable changes (1)
- frontend/CLAUDE.md
✅ Files skipped from review due to trivial changes (3)
- frontend/src/pages/AdminPage/tabs/CalendarSyncTab/CalendarSyncTab.styles.ts
- frontend/src/pages/PromotionPage/utils/promotionNotification.ts
- frontend/src/utils/formatKSTDateTime.ts
🚧 Files skipped from review as they are similar to previous changes (6)
- frontend/src/pages/PromotionPage/components/detail/PromotionClubCTA/PromotionClubCTA.tsx
- frontend/src/types/club.ts
- frontend/src/constants/queryKeys.ts
- frontend/src/pages/PromotionPage/utils/sortPromotions.ts
- frontend/src/pages/AdminPage/tabs/CalendarSyncTab/hooks/useNotionCalendarUiState.ts
- frontend/src/pages/ClubDetailPage/components/ClubScheduleCalendar/ClubScheduleCalendar.tsx
[fix] eslintignore을 config로 이동
🚀 릴리즈 PR
📦 버전 정보
💾 BE/💻 FE🚨 MAJOR/➕ MINOR/🔧 PATCHvX.Y.Z📖 버전 라벨 선택 가이드 (Semantic Versioning)
🚨 MAJORv1.0.0→v2.0.0➕ MINORv1.0.0→v1.1.0🔧 PATCHv1.0.0→v1.0.1📋 포함된 변경사항
Summary by CodeRabbit
릴리스 노트
새로운 기능
UI 개선
문서