Skip to content

[유저] 유효하지 않은 시간표 frame id로 인한 버그 수정#1225

Merged
ff1451 merged 3 commits intodevelopfrom
codex/fix-pc-timetable-empty-frame
Apr 1, 2026
Merged

[유저] 유효하지 않은 시간표 frame id로 인한 버그 수정#1225
ff1451 merged 3 commits intodevelopfrom
codex/fix-pc-timetable-empty-frame

Conversation

@ff1451
Copy link
Copy Markdown
Contributor

@ff1451 ff1451 commented Mar 31, 2026

What is this PR? 🔍

Changes 📝

  • timetable_frame_id0 또는 null 상태일 때 시간표 상세 조회가 나가지 않도록 공통 가드를 추가했습니다.
  • /timetable 페이지에서 invalid query/main frame 값을 직접 state 로 밀어넣지 않도록 정리해 클라이언트 예외 경로를 제거했습니다.
  • 시간표가 실제로 없는 경우 수정/다운로드 액션이 잘못된 frame 기준으로 진행되지 않도록 체크를 강화했습니다.
  • 메인 홈 우측 시간표 위젯은 연회색 박스 대신 빈 시간표 셸을 렌더하도록 바꿨습니다.

ScreenShot 📷

  • 없음

Test CheckList ✅

  • yarn eslint src/api/timetable/queries.ts src/components/IndexComponents/IndexTimetable/index.tsx src/components/TimetablePage/components/MainTimetable/index.tsx src/pages/timetable/index.tsx
  • yarn tsc --noEmit
  • 유효한 frame id 가 없을 때 홈 위젯과 /timetable 페이지가 안전하게 렌더되도록 코드 경로 점검

Precaution

  • 작업 범위와 무관한 src/assets/images/ 미추적 파일은 PR에 포함하지 않았습니다.
  • caniuse-lite is outdated 경고는 기존 환경 경고로, 이번 수정 범위와는 별개입니다.

✔️ Please check if the PR fulfills these requirements

  • It's submitted to the correct branch, not the develop branch unconditionally?
  • If on a hotfix branch, ensure it targets main?
  • There are no warning message when you run yarn lint

Summary by CodeRabbit

릴리스 노트

  • 버그 수정

    • 유효하지 않은 시간표 ID에 대한 조회가 null로 안정화되어 잘못된 데이터 호출 방지
    • 다운로드/편집 등 동작에서 유효성 검증을 추가해 잘못된 상태 진행 차단
  • 개선사항

    • 유효한 프레임 ID가 없을 때 일관된 자리표시자(플레이스홀더)를 표시하도록 UI 개선
    • 초기 프레임 선택 및 렌더링 결정 로직을 개선해 시간표 표시 신뢰도 향상
  • 신규

    • 자리표시자 형태의 스켈레톤 컴포넌트 추가로 비어있는 상태 시 가시성 향상

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 31, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: ee9abdf3-92bf-4811-b2d5-e7875cc04f09

📥 Commits

Reviewing files that changed from the base of the PR and between b3f98fb and d7bf3a7.

📒 Files selected for processing (5)
  • src/api/timetable/queries.ts
  • src/components/IndexComponents/IndexTimetable/index.tsx
  • src/components/TimetablePage/components/MainTimetable/index.tsx
  • src/components/TimetablePage/components/TimetableGridPlaceholder/index.tsx
  • src/pages/timetable/index.tsx
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/api/timetable/queries.ts
  • src/components/TimetablePage/components/MainTimetable/index.tsx
  • src/components/TimetablePage/components/TimetableGridPlaceholder/index.tsx
  • src/pages/timetable/index.tsx

Walkthrough

유효하지 않은 timetable frame ID(예: 0)를 차단하는 타입가드 isValidTimetableFrameId를 추가하고, 서버/클라이언트 쿼리 및 관련 컴포넌트에서 이를 적용해 잘못된 ID로의 조회와 렌더링을 방지하도록 변경했습니다.

Changes

Cohort / File(s) Summary
검증 유틸리티 및 쿼리 변경
src/api/timetable/queries.ts
isValidTimetableFrameId(timetableFrameId) 타입가드 추가. timetableQueries.lectureInfoqueryFnauthorization과 가드 통과 시에만 getTimetableLectureInfo 호출하고, 아니면 null 반환하도록 변경.
인덱스 위젯 및 페이지 렌더링 변경
src/components/IndexComponents/IndexTimetable/index.tsx, src/pages/timetable/index.tsx
isValidTimetableFrameId로 프레임 ID 검증 도입. 유효하지 않은 ID일 때 기본 0 사용 제거, 초기화/효과 로직 단순화, resolvedCurrentFrameIndex 도입으로 존재 확인 후 렌더링.
메인 시간표 구조 재배치
src/components/TimetablePage/components/MainTimetable/index.tsx
MainTimetable을 레이아웃과 ValidMainTimetable/InvalidMainTimetable로 분리. 시간표 존재 검사 로직을 ID 유효성 기반으로 변경하고, 유효하지 않을 때 다운로드·편집 액션을 차단하는 흐름 추가.
플레이스홀더 컴포넌트 추가
src/components/TimetablePage/components/TimetableGridPlaceholder/index.tsx
빈 시간표 스켈레톤 컴포넌트 추가(헤더, 시간열, 요일 칸, 높이/너비 계산 및 aria-hidden 처리). 여러 위치에서 유효하지 않은 ID에 대해 안전하게 렌더링하도록 사용됨.

Sequence Diagram(s)

sequenceDiagram
    participant Browser as 브라우저 (Client)
    participant NextServer as Next.js 서버 (getServerSideProps / SSR)
    participant Queries as Timetable Queries (timetableQueries)
    participant Service as Timetable 서비스 / DB

    Browser->>NextServer: /timetable 요청 (query 포함 가능)
    NextServer->>NextServer: parse frameId, call isValidTimetableFrameId
    alt valid frameId
        NextServer->>Queries: 요청 (authorization & frameId)
        Queries->>Service: getTimetableLectureInfo(frameId)
        Service-->>Queries: lecture data
        Queries-->>NextServer: lecture data
    else invalid or missing
        NextServer-->>NextServer: set validatedFrameId = null
        NextServer-->>Browser: render page with placeholder state
    end
    Note right of Browser: 클라이언트에서도 router.query를 검증하여<br>Timetable 또는 TimetableGridPlaceholder 렌더링 결정
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested labels

🐞 BugFix

Suggested reviewers

  • hyejun0228
  • ParkSungju01
  • kongwoojin
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목이 PR의 주요 변경사항을 명확하게 요약하고 있으며, 유효하지 않은 시간표 frame id로 인한 버그 수정이라는 핵심 내용을 정확하게 전달합니다.
Linked Issues check ✅ Passed 모든 코딩 관련 요구사항이 충족되었습니다: 유효하지 않은 timetable_frame_id 조회 방지, 클라이언트 예외 경로 제거, 편집/다운로드 작업 강화, 홈 위젯의 빈 시간표 표시 등이 모두 구현되었습니다.
Out of Scope Changes check ✅ Passed 모든 변경사항은 유효하지 않은 frame id 처리 및 관련 UI 개선에 집중하고 있으며, 링크된 이슈의 요구사항 범위 내에 있습니다.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/fix-pc-timetable-empty-frame

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions Bot requested review from dooohun and hyejun0228 March 31, 2026 16:40
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
src/components/TimetablePage/components/MainTimetable/index.tsx (1)

24-31: timetableFrameId prop에 대한 방어적 검증 고려.

이 컴포넌트는 timetableFrameId를 prop으로 받아 useMyLectures(timetableFrameId) (line 31)에 직접 전달합니다. 부모 컴포넌트(src/pages/timetable/index.tsx)에서 resolvedCurrentFrameIndex로 검증된 값을 전달하지만, 만약 0이나 유효하지 않은 값이 전달될 경우 useTotalGrades 등에서 문제가 발생할 수 있습니다.

현재 부모에서 mainFrameId의 기본값이 0이므로, frame이 전혀 없는 경우 이 컴포넌트에 0이 전달될 수 있습니다. 이 경우에 대한 방어 로직이나 early return을 고려해 보세요.

 function MainTimetable({ timetableFrameId }: { timetableFrameId: number }) {
+  if (!isValidTimetableFrameId(timetableFrameId)) {
+    return <div className={styles['page__timetable-wrap']}>시간표를 선택해주세요.</div>;
+  }
+
   const [isModalOpen, openModal, closeModal] = useBooleanState(false);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/TimetablePage/components/MainTimetable/index.tsx` around lines
24 - 31, MainTimetable receives timetableFrameId and passes it directly to
useMyLectures, so add defensive validation for timetableFrameId (e.g., ensure
it's a positive non-zero id) at the top of MainTimetable before calling hooks;
if the id is invalid (0, null, undefined), return an early fallback (null,
loading skeleton, or error state) or bypass hooks that require a valid frame id
(useMyLectures, useTotalGrades), and ensure any downstream use of
timetableFrameId is guarded to avoid passing an invalid id into
useMyLectures/useTotalGrades.
src/components/IndexComponents/IndexTimetable/index.tsx (1)

31-39: timetableFrameId={0}을 전달하면 불필요한 쿼리 인스턴스가 생성됩니다.

Timetable 컴포넌트는 내부적으로 useMyLectures(timetableFrameId)를 호출하며, 이는 useTimetableInfoList를 통해 timetableQueries.lectureInfo 쿼리를 실행합니다. queryFn의 가드로 인해 실제 API 호출은 방지되지만, queryKey: [TIMETABLE_INFO_LIST, 0]으로 캐시 엔트리가 생성됩니다.

placeholder 렌더링 시에는 Timetable 대신 시간표 그리드 UI만 표시하는 별도의 presentational 컴포넌트를 사용하는 것이 더 깔끔할 수 있습니다. 다만 현재 구현도 기능적으로는 동작하므로, 향후 리팩토링 시 고려하시면 됩니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/IndexComponents/IndexTimetable/index.tsx` around lines 31 -
39, The placeholder mounts Timetable with timetableFrameId={0}, which triggers
useMyLectures → useTimetableInfoList and creates a cache key
[TIMETABLE_INFO_LIST, 0]; replace this by rendering a presentational-only
component (e.g., TimetableGridPlaceholder or TimetableSkeleton) that reproduces
the grid UI and accepts columnWidth/firstColumnWidth/rowHeight/totalHeight props
but does not call useMyLectures, or alternatively avoid passing timetableFrameId
(ensure Timetable checks for undefined) so that timetableQueries.lectureInfo is
not instantiated; update the placeholder to use the new presentational component
instead of Timetable.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/components/IndexComponents/IndexTimetable/index.tsx`:
- Around line 31-39: The placeholder mounts Timetable with timetableFrameId={0},
which triggers useMyLectures → useTimetableInfoList and creates a cache key
[TIMETABLE_INFO_LIST, 0]; replace this by rendering a presentational-only
component (e.g., TimetableGridPlaceholder or TimetableSkeleton) that reproduces
the grid UI and accepts columnWidth/firstColumnWidth/rowHeight/totalHeight props
but does not call useMyLectures, or alternatively avoid passing timetableFrameId
(ensure Timetable checks for undefined) so that timetableQueries.lectureInfo is
not instantiated; update the placeholder to use the new presentational component
instead of Timetable.

In `@src/components/TimetablePage/components/MainTimetable/index.tsx`:
- Around line 24-31: MainTimetable receives timetableFrameId and passes it
directly to useMyLectures, so add defensive validation for timetableFrameId
(e.g., ensure it's a positive non-zero id) at the top of MainTimetable before
calling hooks; if the id is invalid (0, null, undefined), return an early
fallback (null, loading skeleton, or error state) or bypass hooks that require a
valid frame id (useMyLectures, useTotalGrades), and ensure any downstream use of
timetableFrameId is guarded to avoid passing an invalid id into
useMyLectures/useTotalGrades.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 5b94d81d-46e8-44b6-925f-30584d235bc0

📥 Commits

Reviewing files that changed from the base of the PR and between 44eeab9 and 87cf578.

📒 Files selected for processing (4)
  • src/api/timetable/queries.ts
  • src/components/IndexComponents/IndexTimetable/index.tsx
  • src/components/TimetablePage/components/MainTimetable/index.tsx
  • src/pages/timetable/index.tsx

Comment thread src/pages/timetable/index.tsx
@ff1451 ff1451 changed the title fix: guard empty timetable frame handling on PC timetable flows [유저] 유효하지 않은 시간표 frame id로 인한 버그 수정 Apr 1, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
src/components/TimetablePage/components/TimetableGridPlaceholder/index.tsx (1)

5-8: 시간 슬롯 정의를 한 곳으로 모아 주세요.

DEFAULT_TIME_STRING가 여기와 src/utils/zustand/myLectures.ts에 중복돼 있고, timetable__content 높이도 슬롯 개수를 다시 20으로 하드코딩하고 있습니다. 기본 시간대가 바뀌면 placeholder 높이와 실제 시간표 높이가 서로 어긋날 가능성이 큽니다. 공통 상수로 분리하고 timetable__content 높이도 columnHeight를 재사용하는 편이 안전합니다.

As per coding guidelines, src/**: 1. Next.js, React, TypeScript 팀 코드 컨벤션 및 공식 스타일 가이드(React/TS best practices)를 우선적으로 반영하여, 가독성·안정성(Null/에러 처리)·테스트/유지보수 용이성·브라우저/접근성 이슈 등을 검토해주세요.

Also applies to: 23-24, 48-48

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/TimetablePage/components/TimetableGridPlaceholder/index.tsx`
around lines 5 - 8, DEFAULT_TIME_STRING is duplicated between
TimetableGridPlaceholder (DEFAULT_TIME_STRING) and
src/utils/zustand/myLectures.ts and timetable__content height is hardcoded to
20; extract the shared time-slot array into a common constant module (e.g.,
export DEFAULT_TIME_SLOTS) and import it in both TimetableGridPlaceholder and
myLectures, then replace hardcoded "20"/manual slot-based heights by reusing the
shared columnHeight or computing height from DEFAULT_TIME_SLOTS.length (use the
existing columnHeight symbol if present) so placeholder and real timetable stay
in sync; update references to DEFAULT_TIME_STRING to the new exported name and
ensure imports are added where needed.
src/components/TimetablePage/components/MainTimetable/index.tsx (1)

105-120: 빈 시간표 상태를 텍스트로도 전달해 주세요.

여기서는 TimetableGridPlaceholder만 렌더링하는데, 그 컴포넌트 루트는 aria-hidden입니다 (src/components/TimetablePage/components/TimetableGridPlaceholder/index.tsx). 그래서 스크린 리더에서는 이 영역이 왜 비어 있는지 알 수 없습니다. 등록된 시간표가 없습니다 같은 안내 문구를 시각적으로 노출하거나 최소한 sr-only로 함께 렌더링하는 편이 좋겠습니다.

As per coding guidelines, src/components/**: 4. 접근성(a11y) 관련 속성이 적절히 사용되고 있는지 확인해주세요.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/TimetablePage/components/MainTimetable/index.tsx` around lines
105 - 120, The Timetable currently renders only TimetableGridPlaceholder (which
has aria-hidden) so screen readers get no indication of an empty timetable;
update the JSX where MainTimetableLayout is used (the timetableContent prop) to
render an explicit empty-state message alongside the placeholder—e.g., a
visually visible or screen-reader-only element with text like "등록된 시간표가 없습니다"—so
that either a visible hint or an sr-only <div> (or a11y utility component) is
included when myLectures is empty; modify the code around MainTimetableLayout /
TimetableGridPlaceholder usage to conditionally include that message.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/components/TimetablePage/components/MainTimetable/index.tsx`:
- Around line 193-198: The current MainTimetable branch only uses
isValidTimetableFrameId and can still render non-existent frames; update
MainTimetable to also verify the given timetableFrameId exists in the current
timeTableFrameList (or via the same selector/hook that provides frames), and if
it does not, replace it with the first valid frame id from timeTableFrameList;
only when no valid frames exist fall back to rendering InvalidMainTimetable;
ensure you reference and use the same source of truth used by
initialFrameId/resolvedCurrentFrameIndex so edit/download/modal flows and
useMyLectures receive a corrected id (adjust MainTimetable to validate
membership against timeTableFrameList, choose first valid id if missing, then
pass that id to ValidMainTimetable or render InvalidMainTimetable).

---

Nitpick comments:
In `@src/components/TimetablePage/components/MainTimetable/index.tsx`:
- Around line 105-120: The Timetable currently renders only
TimetableGridPlaceholder (which has aria-hidden) so screen readers get no
indication of an empty timetable; update the JSX where MainTimetableLayout is
used (the timetableContent prop) to render an explicit empty-state message
alongside the placeholder—e.g., a visually visible or screen-reader-only element
with text like "등록된 시간표가 없습니다"—so that either a visible hint or an sr-only <div>
(or a11y utility component) is included when myLectures is empty; modify the
code around MainTimetableLayout / TimetableGridPlaceholder usage to
conditionally include that message.

In `@src/components/TimetablePage/components/TimetableGridPlaceholder/index.tsx`:
- Around line 5-8: DEFAULT_TIME_STRING is duplicated between
TimetableGridPlaceholder (DEFAULT_TIME_STRING) and
src/utils/zustand/myLectures.ts and timetable__content height is hardcoded to
20; extract the shared time-slot array into a common constant module (e.g.,
export DEFAULT_TIME_SLOTS) and import it in both TimetableGridPlaceholder and
myLectures, then replace hardcoded "20"/manual slot-based heights by reusing the
shared columnHeight or computing height from DEFAULT_TIME_SLOTS.length (use the
existing columnHeight symbol if present) so placeholder and real timetable stay
in sync; update references to DEFAULT_TIME_STRING to the new exported name and
ensure imports are added where needed.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 42d5edd4-73cc-46b4-9602-6a71c27b7d8d

📥 Commits

Reviewing files that changed from the base of the PR and between 87cf578 and b3f98fb.

📒 Files selected for processing (3)
  • src/components/IndexComponents/IndexTimetable/index.tsx
  • src/components/TimetablePage/components/MainTimetable/index.tsx
  • src/components/TimetablePage/components/TimetableGridPlaceholder/index.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/components/IndexComponents/IndexTimetable/index.tsx

Comment thread src/components/TimetablePage/components/MainTimetable/index.tsx
@ff1451 ff1451 force-pushed the codex/fix-pc-timetable-empty-frame branch from b3f98fb to d7bf3a7 Compare April 1, 2026 02:37
Copy link
Copy Markdown

@dh2906 dh2906 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

냠냠

@ff1451 ff1451 merged commit 194d0c2 into develop Apr 1, 2026
3 checks passed
@github-actions github-actions Bot deleted the codex/fix-pc-timetable-empty-frame branch April 1, 2026 03:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[유저] PC 시간표 위젯 및 시간표 페이지에서 유효하지 않은 frame id 조회 발생

2 participants