Skip to content

♻️ [Refactor] 캘린더 화면 구조 및 반응형 UI 개선 (#97)#98

Merged
hyeeon merged 5 commits into
developfrom
fix/responsive-ui
Jul 26, 2026
Merged

♻️ [Refactor] 캘린더 화면 구조 및 반응형 UI 개선 (#97)#98
hyeeon merged 5 commits into
developfrom
fix/responsive-ui

Conversation

@hyeeon

@hyeeon hyeeon commented Jul 25, 2026

Copy link
Copy Markdown
Collaborator

📌 작업 내용

  • 시스템 글자 및 디스플레이 크기에 대응하도록 화면 레이아웃 개선
  • 캘린더와 일정 목록이 함께 스크롤되도록 구조 변경
  • 일정 이상 스크롤 시 최상단 이동 버튼 추가
  • document.tsx 자녀 필터를 공용 ChildFilterBar 컴포넌트로 통합

📷 스크린 샷

⚠️ 참고 사항

🔗 관련 이슈

Closes #97

Summary by CodeRabbit

  • 새로운 기능

    • 캘린더에서 긴 일정 목록을 확인한 뒤 상단으로 바로 이동할 수 있습니다.
    • 자녀 선택 필터를 캘린더와 문서 화면에서 일관된 형태로 사용할 수 있습니다.
    • 일정 로딩 및 빈 상태가 화면 흐름에 맞게 표시됩니다.
  • 개선 사항

    • 주간·월간 캘린더의 자동 스크롤과 오늘 날짜 표시가 개선되었습니다.
    • 요일 및 날짜 표시가 더 간결하고 안정적으로 정렬됩니다.
    • 프로필, 알림, 등록 화면의 긴 텍스트와 버튼 레이아웃이 다양한 화면 크기에 맞게 조정되었습니다.

@hyeeon
hyeeon requested a review from chae1125 July 25, 2026 17:20
@hyeeon hyeeon self-assigned this Jul 25, 2026
@hyeeon hyeeon added the refactor Code refactoring label Jul 25, 2026
@coderabbitai

coderabbitai Bot commented Jul 25, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@hyeeon, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 45 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 78e8add2-f978-479c-a689-75adce7c7f42

📥 Commits

Reviewing files that changed from the base of the PR and between 34719fa and c6046f3.

📒 Files selected for processing (1)
  • src/components/calendar/MonthCalendar.tsx
📝 Walkthrough

Walkthrough

캘린더의 자녀 데이터 로딩과 주간·월간 스크롤 구조를 재구성하고 상단 이동 버튼을 추가했습니다. 자녀 필터를 공통 컴포넌트로 통합했으며, 캘린더·문서·프로필·알림 화면의 텍스트 및 반응형 레이아웃을 조정했습니다.

Changes

캘린더 스크롤 및 렌더링

Layer / File(s) Summary
캘린더 상태 및 스크롤 흐름
app/(tabs)/calendar.tsx
자녀 API 결과를 스토어에 저장하고, 선택 자녀 변경·콘텐츠 크기 변경·스크롤 위치에 따라 캘린더 스크롤 상태를 갱신합니다.
주간·월간 캘린더 렌더링
app/(tabs)/calendar.tsx, src/styles/calendar/calendar.ts
주간 그룹 위치 기반 자동 스크롤, 인라인 로딩·빈 상태, 일간 일정 목록과 플로팅 상단 이동 버튼을 추가했습니다.
캘린더 표시 및 자녀 필터
src/components/calendar/*, src/components/common/ChildFilterBar.tsx
요일을 narrow 형식으로 표시하고 텍스트 크기·줄 수를 제한했으며, 필터 버튼을 최소 높이와 둥근 모양 중심으로 조정했습니다.

문서 필터 통합

Layer / File(s) Summary
문서 화면 필터 통합
app/(tabs)/document.tsx
기존 직접 렌더링하던 자녀 필터를 ChildFilterBar로 교체하고 사용하지 않는 필터 스타일을 제거했습니다.

반응형 화면 스타일

Layer / File(s) Summary
화면별 반응형 스타일
src/components/common/SelectionCard.tsx, src/styles/{notifications,profile,register}/*
고정 높이를 최소 높이와 세로 패딩으로 변경하고, 텍스트 줄바꿈·최소 너비·요소 축소 제약을 조정했습니다.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CalendarScreen
  participant fetchChildrenApi
  participant useChildrenStore
  participant CalendarScrollView
  CalendarScreen->>fetchChildrenApi: 자녀 데이터 요청
  fetchChildrenApi-->>CalendarScreen: 자녀 데이터 반환
  CalendarScreen->>useChildrenStore: setChildren 호출
  CalendarScreen->>CalendarScrollView: 선택 변경 시 scrollTo
  CalendarScrollView-->>CalendarScreen: 스크롤 위치 전달
  CalendarScreen->>CalendarScrollView: 임계값 초과 시 상단 버튼 표시
Loading

Possibly related PRs

Suggested reviewers: chae1125

Poem

당근처럼 둥근 버튼,
달력은 위로 훌쩍,
아이를 고르면 착착,
글자는 한 줄로 사뿐.
토끼도 일정을 찾아
깡충, 오늘로 돌아가네!

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목이 캘린더 화면 구조 개선과 반응형 UI, 스크롤 구조 변경이라는 핵심 변경을 잘 요약합니다.
Linked Issues check ✅ Passed 반응형 레이아웃 개선, 캘린더·일정 목록 동시 스크롤, 상단 이동 버튼 추가 요구를 모두 반영했습니다.
Out of Scope Changes check ✅ Passed 캘린더·문서·공용 필터와 전반적 레이아웃 조정이 모두 스크린 크기 대응과 스크롤 개선 목적에 부합합니다.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/responsive-ui

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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)
app/(tabs)/calendar.tsx (2)

116-121: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

렌더링 중 ref 값 변경(no-ref-current-in-render).

weekOffsetRef.current = weekOffset;가 컴포넌트 렌더 본문에서 조건 없이 직접 실행됩니다. React Doctor가 지적한 대로 렌더는 순수해야 하며, React가 커밋 없이 렌더를 재실행/폐기하는 경로(Concurrent 기능, React 19.2의 <Activity> 등)에서 예기치 않은 부작용의 원인이 될 수 있습니다.

이 "최신 값을 ref로 유지" 패턴은 React 19.2에 새로 추가된 useEffectEvent()로 대체하는 것이 권장됩니다. useEffect로 옮기면 한 렌더 지연이 생기지만 useEffectEvent는 그 지연 없이 항상 최신 값을 안전하게 참조할 수 있습니다.

♻️ 제안: useEffectEvent로 대체
-  const weekOffsetRef = useRef(weekOffset);
-  const shouldAutoScrollRef = useRef(weekOffset === 0);
-  weekOffsetRef.current = weekOffset;
+  const shouldAutoScrollRef = useRef(weekOffset === 0);
+  const getWeekOffset = useEffectEvent(() => weekOffset);

그리고 onContentSizeChange 콜백 내부의 weekOffsetRef.currentgetWeekOffset() 호출로 교체합니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/`(tabs)/calendar.tsx around lines 116 - 121, Replace the render-time
assignment to weekOffsetRef.current in the calendar component with React 19.2’s
useEffectEvent-based latest-value accessor. Define the accessor near
weekOffsetRef and update the onContentSizeChange callback to call
getWeekOffset() instead of reading weekOffsetRef.current, while preserving the
existing auto-scroll behavior.

Source: Linters/SAST tools


95-109: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

자녀 목록 fetch에 다른 호출과 동일한 race-guard 패턴이 빠져 있습니다.

주간/월간/일간/학사일정 fetch는 모두 reqId ref로 오래된 응답이 최신 상태를 덮어쓰지 않도록 보호하지만, fetchChildrenApi().then(setStoreChildren)는 이 보호가 없습니다. 포커스 이벤트가 짧은 간격으로 연속 발생하면 응답 순서가 뒤바뀌어 오래된 자녀 목록이 최신 목록을 덮어쓸 수 있습니다.

🛡️ 제안: 다른 fetch와 동일한 reqId 가드 적용
+  const childrenReqIdRef = useRef(0);
+
   useFocusEffect(
     useCallback(() => {
       if (!hasFocusedOnceRef.current) {
         hasFocusedOnceRef.current = true;
-        fetchChildrenApi()
-          .then(setStoreChildren)
-          .catch(() => {});
+        childrenReqIdRef.current += 1;
+        const reqId = childrenReqIdRef.current;
+        fetchChildrenApi()
+          .then((data) => { if (reqId === childrenReqIdRef.current) setStoreChildren(data); })
+          .catch(() => {});
         return;
       }
-      fetchChildrenApi()
-        .then(setStoreChildren)
-        .catch(() => {});
+      childrenReqIdRef.current += 1;
+      const reqId = childrenReqIdRef.current;
+      fetchChildrenApi()
+        .then((data) => { if (reqId === childrenReqIdRef.current) setStoreChildren(data); })
+        .catch(() => {});
       setFocusKey((k) => k + 1);
     }, [setStoreChildren])
   );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/`(tabs)/calendar.tsx around lines 95 - 109, Apply the existing reqId
race-guard pattern to the child-list fetch flow in useFocusEffect: track each
fetchChildrenApi request with a request-id ref and call setStoreChildren only
when its response matches the latest request. Preserve the current focus
handling and dependency structure while preventing stale responses from updating
the store.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/calendar/MonthCalendar.tsx`:
- Around line 35-40: Update the dayNames construction so each weekday header
uses a stable, domain-unique key derived from its fixed date or weekday identity
rather than the array index i. Adjust the corresponding day.key usage in the
header rendering while preserving the existing labels and ordering.

---

Nitpick comments:
In `@app/`(tabs)/calendar.tsx:
- Around line 116-121: Replace the render-time assignment to
weekOffsetRef.current in the calendar component with React 19.2’s
useEffectEvent-based latest-value accessor. Define the accessor near
weekOffsetRef and update the onContentSizeChange callback to call
getWeekOffset() instead of reading weekOffsetRef.current, while preserving the
existing auto-scroll behavior.
- Around line 95-109: Apply the existing reqId race-guard pattern to the
child-list fetch flow in useFocusEffect: track each fetchChildrenApi request
with a request-id ref and call setStoreChildren only when its response matches
the latest request. Preserve the current focus handling and dependency structure
while preventing stale responses from updating the store.
🪄 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 Plus

Run ID: 1a5250d1-dce0-47ac-bafb-baa6e239fb1f

📥 Commits

Reviewing files that changed from the base of the PR and between d8fc954 and 34719fa.

📒 Files selected for processing (13)
  • app/(tabs)/calendar.tsx
  • app/(tabs)/document.tsx
  • src/components/calendar/MonthCalendar.tsx
  • src/components/calendar/WeekCalendar.tsx
  • src/components/common/ChildFilterBar.tsx
  • src/components/common/SelectionCard.tsx
  • src/styles/calendar/calendar.ts
  • src/styles/notifications/notifications.ts
  • src/styles/profile/profile.ts
  • src/styles/profile/profileEdit.ts
  • src/styles/profile/profileNotification.ts
  • src/styles/register/language.ts
  • src/styles/register/notification.ts
📜 Review details
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2026-06-08T16:56:51.296Z
Learnt from: chae1125
Repo: GACHI-Project/GACHI-FE PR: 83
File: src/components/home/MealTimetableWidget.tsx:108-109
Timestamp: 2026-06-08T16:56:51.296Z
Learning: In the GACHI-FE React Native/Expo codebase, the `react/no-array-index-key` ESLint rule is enforced—do not use an array index (e.g., `key={i}`) as a React `key`. Use a stable, domain-unique identifier instead (e.g., an entity id, or a field guaranteed unique by the domain). If uniqueness depends on additional context, compose a key from multiple stable attributes (e.g., `${date}-${name}` for items repeated across multiple days).

Applied to files:

  • src/components/common/ChildFilterBar.tsx
  • src/components/common/SelectionCard.tsx
  • src/components/calendar/MonthCalendar.tsx
  • app/(tabs)/document.tsx
  • app/(tabs)/calendar.tsx
  • src/components/calendar/WeekCalendar.tsx
🪛 React Doctor (0.7.6)
app/(tabs)/calendar.tsx

[error] 119-119: This ref is mutated during render. React can replay or discard render work, so the mutation can leak from UI that never commits.

Move ref writes into an event handler or effect. Render must stay pure because React can replay or discard it. The predictable null-guarded lazy initialization pattern remains supported.

(no-ref-current-in-render)

🔇 Additional comments (15)
src/components/calendar/MonthCalendar.tsx (1)

104-105: LGTM!

Also applies to: 138-139, 175-176

src/components/calendar/WeekCalendar.tsx (1)

21-21: LGTM!

Also applies to: 65-75, 107-108

src/components/common/ChildFilterBar.tsx (1)

39-39: LGTM!

Also applies to: 54-57, 72-73, 85-87

app/(tabs)/document.tsx (1)

2-2: LGTM!

Also applies to: 14-14, 148-152, 212-214

src/components/common/SelectionCard.tsx (1)

38-38: LGTM!

Also applies to: 70-70, 95-95, 110-110, 124-124

src/styles/notifications/notifications.ts (1)

15-16: LGTM!

Also applies to: 57-65, 74-74, 91-94

src/styles/profile/profile.ts (1)

35-35: LGTM!

Also applies to: 53-54, 73-73, 84-84, 102-113, 130-142

src/styles/profile/profileEdit.ts (1)

51-53: LGTM!

src/styles/profile/profileNotification.ts (2)

36-37: LGTM!

Also applies to: 154-160


129-140: 🎯 Functional Correctness

알림 화면 제목과 뱃지를 함께 흐르는 flex 제어는 실제 렌더가 확인되지 않았습니다.

flexWrap만 추가해 제목 텍스트가 줄바꿈되더라도, 제목을 감싸는 row가 실제 flex item으로 사용되지 않으면 이 스타일이 적용되지 않습니다. 렌더 JSX에서 itemTitleRow / cardTitleRow가 뱃지와 같은 flex row 아이템인지 먼저 확인한 뒤, 필요하면 itemTitle/cardTitle/row에 flex: 1 minWidth: 0badge flexShrink: 0을 적용하세요.

src/styles/register/language.ts (1)

47-47: LGTM!

src/styles/register/notification.ts (1)

51-53: LGTM!

Also applies to: 90-94, 107-112, 129-136

app/(tabs)/calendar.tsx (2)

452-467: 🎯 Functional Correctness

todayGroupY가 갱신되지 않을 수 있는 경로 확인 필요.

todayGroupY.current는 오늘 날짜 그룹이 렌더링될 때만 onLayout으로 갱신됩니다(라인 490-494). 만약 특정 시점에 오늘 일정이 없어 weekDisplayGroups에 "오늘" 그룹이 아예 포함되지 않으면, 이전에 설정된 todayGroupY 값이 그대로 남아있게 됩니다. 이후 주(0주차)로 복귀했을 때 shouldAutoScrollRef가 다시 true가 되면서, 이 오래된 오프셋으로 스크롤을 시도할 가능성이 있는지 확인이 필요합니다.

발생 빈도는 낮아 보이지만(동일 세션 내에서 오늘 일정 유무가 바뀌는 경우), 방어적으로 weekDisplayGroups가 바뀔 때 오늘 그룹이 없으면 todayGroupY.current를 초기화하는 편이 안전합니다.


479-482: 🎯 Functional Correctness

문제 없음.

styles.loadingContainer 참조가 남아 있지 않습니다.

src/styles/calendar/calendar.ts (1)

30-51: LGTM!

Also applies to: 101-122, 182-182

Comment thread src/components/calendar/MonthCalendar.tsx Outdated
@hyeeon
hyeeon merged commit 2f5f8dd into develop Jul 26, 2026
1 check passed
@hyeeon
hyeeon deleted the fix/responsive-ui branch July 26, 2026 17:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

refactor Code refactoring

Projects

None yet

Development

Successfully merging this pull request may close these issues.

♻️ [Refactor] 캘린더 화면 구조 및 반응형 UI 개선

2 participants