Skip to content

[FEAT] 통계 페이지 캘린더 구현#133

Open
yumin-kim2 wants to merge 22 commits into
developfrom
feat/web/129-statistics-calendar
Open

[FEAT] 통계 페이지 캘린더 구현#133
yumin-kim2 wants to merge 22 commits into
developfrom
feat/web/129-statistics-calendar

Conversation

@yumin-kim2

@yumin-kim2 yumin-kim2 commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

ISSUE 🔗

close #129



What is this PR? 🔍

통계 페이지의 월별 캘린더 UI를 구현했습니다.

월 상태를 어디에서 관리할지

처음에는 캘린더 내부에서 currentMonth를 관리할 수도 있다고 봤습니다.

하지만 월 이동 버튼은 헤더에 있고, 캘린더는 그 월을 기준으로 날짜를 렌더링해야 합니다. 즉 헤더와 캘린더가 같은 월 상태를 공유해야 했습니다.

그래서 currentMonth는 페이지 레벨에서 관리하고, 헤더와 캘린더에 각각 props로 내려주는 구조를 선택했습니다.

<StatisticsHeaderContainer
  currentMonth={currentMonth}
  onChangeMonth={setCurrentMonth}
/>

<StatisticsCalendar currentMonth={currentMonth} />

이 구조는 이후 API 연결 시에도 currentMonth를 기준으로 yearMonth 파라미터를 만들 수 있어 확장하기 쉽다고 판단했습니다.

page.tsx를 Server Component로 유지

초기에는 page.tsx에서 useState로 currentMonth를 관리했지만, 이 경우 page.tsx 전체가 Client Component가 됩니다.
App Router의 page.tsx는 라우트 진입점 역할에 집중하는 것이 좋다고 판단해, 상태 관리와 데이터 주입은 별도 컨테이너로 이동했습니다.

page.tsx
→ StatisticsPageContainer 렌더링

StatisticsPageContainer.tsx
→ currentMonth 상태 관리
→ mock calendarData 주입
→ Header와 Calendar 조립

이를 통해 page.tsx는 Server Component로 유지하고, 클라이언트 상태가 필요한 부분만 컨테이너에서 관리하도록 분리했습니다.

캘린더는 현재 월 날짜만 렌더링

해당 월의 1일이 시작되는 요일에 맞춰 앞쪽에 빈 칸을 추가했습니다.

getCalendarDates(currentMonth)
getFirstDayOffset(currentMonth)

완료율 기반 아이콘 매핑

API 명세의 completionRate 값을 기준으로 캘린더 아이콘 상태를 결정하도록 했습니다.

0 또는 null -> empty
1~49      -> outline
50~99     -> light
100       -> filled

아이콘 상태와 실제 SVG 컴포넌트는 매핑 객체로 관리했습니다.

const STATUS_ICON = {
  empty: StatisticsClockEmptyIcon,
  outline: StatisticsClockOutlineIcon,
  light: StatisticsClockLightIcon,
  filled: StatisticsClockFilledIcon,
  disabled: StatisticsClockDisabledIcon,
};

날짜 계산 유틸 분리

_utils/statisticsCalendar.ts에 캘린더 계산 로직을 분리했습니다.

  • getCalendarDates: 현재 월의 날짜 배열 생성
  • getFirstDayOffset: 1일 앞 빈 칸 개수 계산
  • formatDateKey: API의 yyyy-MM-dd 문자열과 매칭하기 위한 key 생성



To Reviewers

StatisticsCalendar가 mock 데이터를 직접 import하지 않도록 수정하고, calendarData prop으로 주입받도록 변경했습니다.

이번 PR에서 하지 않은 것

이번 PR은 월별 캘린더 UI 구현에 집중했습니다.
아래 작업은 다음 단계에서 처리할 예정입니다.

  • 날짜 클릭 시 selected 아이콘으로 변경
  • 선택 날짜에 따른 오른쪽 패널 전환
  • 월별 통계 패널과 일별 통계 패널 연결

날짜 클릭 상태는 오른쪽 패널 상태와 함께 움직여야 하므로, 캘린더 단독으로 처리하지 않고 이후 뷰 전환 작업에서 selectedDate 상태를 기준으로 함께 구현하는 것이 적절하다고 판단했습니다.

캘린더가 화면에 꽉 차 보이지 않는 문제

Figma의 gap, padding, grid width 값을 맞췄지만 브라우저에서는 캘린더가 덜 차 보이는 문제가 있었습니다.
이는 캘린더 내부 spacing 문제라기보다, 현재 PR에서 오른쪽 통계 패널과 전체 layout 전환이 아직 포함되지 않았기 때문이라고 판단했습니다.
캘린더 grid를 임의로 넓히면 Figma 기준의 아이콘 간격과 grid 비율이 깨질 수 있어, 이번 PR에서는 캘린더 자체 수치를 유지했습니다.
이후 오른쪽 패널과 selectedDate 기반 화면 전환이 붙는 시점에 전체 layout 기준으로 다시 확인할 예정입니다!


Screenshot 📷

image



Test Checklist ✔

  • 상단 헤더의 월 이동 상태를 캘린더와 연결
  • 현재 월 기준으로 날짜 배열을 생성해 캘린더 grid를 렌더링
  • 날짜별 completionRate 값에 따라 통계 아이콘 상태를 다르게 표시
  • 캘린더 상단에 월 제목과 서버 기준 오늘 날짜를 표시
  • 날짜 계산 로직과 mock 데이터를 별도 파일로 분리

@yumin-kim2 yumin-kim2 linked an issue Jul 10, 2026 that may be closed by this pull request
@vercel

vercel Bot commented Jul 10, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
timo Ready Ready Preview, Comment Jul 10, 2026 7:26pm

@github-actions github-actions Bot added ⏰ Timo-web Timo 웹 서비스 ⌚ Timo-Design-system Timo 디자인 시스템 ✨ Feature 새로운 기능(기능성) 구현 ♣️ 유민 유민양 labels Jul 10, 2026
@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

통계 페이지에 현재 월을 공유하는 클라이언트 컨테이너와 이전·다음 월 이동 기능을 추가했습니다. 날짜별 완료율 응답 계약, 날짜 계산 유틸리티, 완료율 상태 아이콘이 포함된 월간 캘린더 UI를 구현하고 모의 데이터를 연결했습니다.

Changes

통계 캘린더 기능

Layer / File(s) Summary
캘린더 계약과 날짜 계산
apps/timo-web/app/[locale]/(main)/statistics/_types/statistics.ts, apps/timo-web/app/[locale]/(main)/statistics/_mocks/*, apps/timo-web/app/[locale]/(main)/statistics/_utils/*
Zod 기반 캘린더 응답 타입, 모의 데이터, 월별 날짜 목록·요일 오프셋·날짜 키 계산 및 로케일별 날짜 포맷터를 추가했습니다.
월 상태 공유와 페이지 연결
apps/timo-web/app/[locale]/(main)/statistics/page.tsx, apps/timo-web/app/[locale]/(main)/statistics/_containers/*
현재 월 상태를 StatisticsContainer가 관리하고, 헤더의 월 변경 콜백과 캘린더 입력을 연결하도록 컨테이너 구조를 변경했습니다.
완료율 기반 캘린더 렌더링
apps/timo-web/app/[locale]/(main)/statistics/_components/StatisticsCalendar.tsx
요일 그리드와 월 날짜를 렌더링하고, completionRatenull 값에 따라 날짜 아이콘 상태를 매핑합니다.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant StatisticsPage
  participant StatisticsContainer
  participant StatisticsHeaderContainer
  participant StatisticsCalendar
  StatisticsPage->>StatisticsContainer: 통계 화면 렌더링
  StatisticsContainer->>StatisticsHeaderContainer: 현재 월과 변경 콜백 전달
  StatisticsHeaderContainer->>StatisticsContainer: 이전 또는 다음 월 변경
  StatisticsContainer->>StatisticsCalendar: 변경된 월과 캘린더 데이터 전달
  StatisticsCalendar->>StatisticsCalendar: 날짜 그리드와 완료율 아이콘 렌더링
Loading

Possibly related PRs

Suggested labels: ♦️ 민아

Suggested reviewers: jjangminii, ehye1

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning 월간 캘린더, 요일 헤더, 월 이동, 상태 아이콘은 맞지만 오늘/선택 날짜 셀 강조는 보이지 않습니다. 캘린더 셀에 today/selected 상태를 판별해 배경·테두리 스타일을 추가하고, 월 변경 시 그 상태도 함께 갱신하세요.
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 (3 passed)
Check name Status Explanation
Title check ✅ Passed 제목이 통계 페이지 월간 캘린더 구현이라는 핵심 변경을 정확히 요약합니다.
Description check ✅ Passed 설명이 캘린더 UI, 상태 구조, 유틸 분리 등 실제 변경과 잘 맞습니다.
Out of Scope Changes check ✅ Passed 컨테이너, mock, 유틸 추가는 캘린더 구현 범위 안에 있어 별도 불필요한 변경은 보이지 않습니다.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/web/129-statistics-calendar
⚔️ Resolve merge conflicts
  • Resolve merge conflict in branch feat/web/129-statistics-calendar

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.

@github-actions

github-actions Bot commented Jul 10, 2026

Copy link
Copy Markdown

Storybook Preview

항목 링크
Storybook 열기
Chromatic 빌드 확인

마지막 업데이트: 2026-07-10 18:22 UTC

@github-actions

github-actions Bot commented Jul 10, 2026

Copy link
Copy Markdown

Timo Performance Report

Bundle Size — timo-web
라우트 크기 First Load JS
/[locale]/home 72.02 kB 🟡 277.84 kB
/[locale]/today 55.42 kB 🟡 261.24 kB
/[locale]/focus 52.27 kB 🟡 258.09 kB
/[locale]/settings/account 0 B 🟡 205.82 kB
/[locale]/settings 0 B 🟡 205.82 kB
/[locale]/settings/policy 0 B 🟡 205.82 kB
/[locale]/statistics 51.02 kB 🟡 256.84 kB
/[locale]/[...rest] 0 B 🟡 205.82 kB
/[locale]/onboarding 107.39 kB 🟡 313.21 kB
/[locale] 0 B 🟡 205.82 kB

공유 번들: 205.82 kB
🟢 < 200kB  |  🟡 < 350kB  |  🔴 ≥ 350kB (First Load JS · gzip)

Lighthouse — timo-web
URL Perf A11y LCP CLS TBT
/en/home 🔴 65 🟢 96 🔴 14.0s 🟢 0.000 🟡 397ms
/en/today 🟡 72 🟡 93 🔴 13.7s 🟢 0.000 🟢 196ms
/en/focus 🟡 72 🟡 91 🔴 13.6s 🟢 0.000 🟡 202ms
/en/statistics 🟡 72 🟢 96 🔴 13.7s 🟢 0.000 🟢 184ms

Perf ≥ 70 / A11y ≥ 85 목표
LCP 🟢 < 2.5s 🟡 < 4s 🔴 ≥ 4s  |  CLS 🟢 < 0.1 🟡 < 0.25 🔴 ≥ 0.25  |  TBT 🟢 < 200ms 🟡 < 600ms 🔴 ≥ 600ms

Image Optimization — timo-web

public/ 디렉토리에 이미지가 없습니다.

측정 커밋: 79d6d6e

@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: 4

🤖 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
`@apps/timo-web/app/`[locale]/(main)/statistics/_components/StatisticsCalendar.tsx:
- Around line 1-19: StatisticsCalendar의 MOCK_STATISTICS_CALENDAR 직접 의존성을 제거하세요.
MOCK_STATISTICS_CALENDAR import는 상위 컨테이너 또는 페이지로 이동하고, StatisticsCalendar이 필요한
캘린더 데이터를 props로 받도록 타입과 호출부를 수정해 순수 UI 컴포넌트로 유지하세요.
- Line 52: Replace the direct new Date(MOCK_STATISTICS_CALENDAR.today) call in
StatisticsCalendar with a local-date parser that splits the YYYY-MM-DD key into
numeric year, month, and day and constructs new Date(year, month - 1, day); add
and reuse a parseDateKey helper in _utils/statisticsCalendar.ts alongside
formatDateKey.
- Around line 31-41: Remove the unused isCurrentMonth parameter and disabled
branch from getIconStatus, then update all call sites to pass only
completionRate. Also remove the unused StatisticsClockDisabledIcon import and
STATUS_ICON.disabled mapping, along with any related dead types or logic.

In `@apps/timo-web/app/`[locale]/(main)/statistics/page.tsx:
- Around line 1-20: Keep StatisticsPage as a Server Component by removing the
"use client" directive and useState from page.tsx, and have it render only a new
StatisticsPageContainer. Move the currentMonth state and existing
StatisticsHeaderContainer/StatisticsCalendar composition into a "use client"
component named StatisticsPageContainer under _containers.
🪄 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: ASSERTIVE

Plan: Pro Plus

Run ID: 8407106d-f434-4f4e-964f-2c3c03884f9d

📥 Commits

Reviewing files that changed from the base of the PR and between 3bb25bc and 28e192b.

⛔ Files ignored due to path filters (9)
  • packages/timo-design-system/src/icons/source/statistics-clock-disabled.svg is excluded by !**/*.svg
  • packages/timo-design-system/src/icons/source/statistics-clock-empty-selected.svg is excluded by !**/*.svg
  • packages/timo-design-system/src/icons/source/statistics-clock-empty.svg is excluded by !**/*.svg
  • packages/timo-design-system/src/icons/source/statistics-clock-filled-selected.svg is excluded by !**/*.svg
  • packages/timo-design-system/src/icons/source/statistics-clock-filled.svg is excluded by !**/*.svg
  • packages/timo-design-system/src/icons/source/statistics-clock-light-selected.svg is excluded by !**/*.svg
  • packages/timo-design-system/src/icons/source/statistics-clock-light.svg is excluded by !**/*.svg
  • packages/timo-design-system/src/icons/source/statistics-clock-outline-selected.svg is excluded by !**/*.svg
  • packages/timo-design-system/src/icons/source/statistics-clock-outline.svg is excluded by !**/*.svg
📒 Files selected for processing (8)
  • apps/timo-web/app/[locale]/(main)/statistics/_components/.gitkeep
  • apps/timo-web/app/[locale]/(main)/statistics/_components/StatisticsCalendar.tsx
  • apps/timo-web/app/[locale]/(main)/statistics/_containers/StatisticsHeaderContainer.tsx
  • apps/timo-web/app/[locale]/(main)/statistics/_mocks/statisticsCalendar.ts
  • apps/timo-web/app/[locale]/(main)/statistics/_types/statistics.ts
  • apps/timo-web/app/[locale]/(main)/statistics/_utils/formatStatisticsDate.ts
  • apps/timo-web/app/[locale]/(main)/statistics/_utils/statisticsCalendar.ts
  • apps/timo-web/app/[locale]/(main)/statistics/page.tsx

Comment thread apps/timo-web/app/[locale]/(main)/statistics/_components/StatisticsCalendar.tsx Outdated
Comment thread apps/timo-web/app/[locale]/(main)/statistics/_components/StatisticsCalendar.tsx Outdated
export const StatisticsCalendar = ({
currentMonth,
}: StatisticsCalendarProps) => {
const today = new Date(MOCK_STATISTICS_CALENDAR.today);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

new Date("2026-07-10")은 UTC로 파싱되어 timezone에 따라 날짜가 shift될 수 있습니다.

ECMAScript 스펙에 따라 date-only ISO 문자열(YYYY-MM-DD)은 UTC 자정으로 파싱됩니다. formatStatisticsCalendarDate는 local timezone으로 포맷하므로, UTC-5 등의 timezone에서는 "7월 9일"로 표시됩니다. 한국(UTC+9)에서는 문제가 없지만, 안전한 파싱 방식을 사용하는 것이 좋습니다.

🛡️ 제안하는 안전한 날짜 파싱
-const today = new Date(MOCK_STATISTICS_CALENDAR.today);
+const [year, month, day] = MOCK_STATISTICS_CALENDAR.today.split("-").map(Number);
+const today = new Date(year, month - 1, day);

또는 formatDateKey와 동일한 방식으로 local date를 생성하는 헬퍼를 _utils/statisticsCalendar.ts에 추가할 수 있습니다:

export const parseDateKey = (dateKey: string): Date => {
  const [year, month, day] = dateKey.split("-").map(Number);
  return new Date(year, month - 1, day);
};
📝 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.

Suggested change
const today = new Date(MOCK_STATISTICS_CALENDAR.today);
const [year, month, day] = MOCK_STATISTICS_CALENDAR.today.split("-").map(Number);
const today = new Date(year, month - 1, day);
🤖 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
`@apps/timo-web/app/`[locale]/(main)/statistics/_components/StatisticsCalendar.tsx
at line 52, Replace the direct new Date(MOCK_STATISTICS_CALENDAR.today) call in
StatisticsCalendar with a local-date parser that splits the YYYY-MM-DD key into
numeric year, month, and day and constructs new Date(year, month - 1, day); add
and reuse a parseDateKey helper in _utils/statisticsCalendar.ts alongside
formatDateKey.

Comment thread apps/timo-web/app/[locale]/(main)/statistics/page.tsx Outdated

@jjangminii jjangminii left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

고생하셨습니다~ 이제 패널하고 합치면 뷰 금방 만들 수 있을것 같아요
코멘트 확인해주세요-!

Comment thread packages/timo-design-system/src/icons/source/statistics-clock-disabled.svg Outdated
Comment thread packages/timo-design-system/src/icons/source/statistics-clock-empty-selected.svg Outdated
Comment thread apps/timo-web/app/[locale]/(main)/statistics/_components/StatisticsCalendar.tsx Outdated
Comment thread apps/timo-web/app/[locale]/(main)/statistics/_components/StatisticsCalendar.tsx Outdated
Comment thread apps/timo-web/app/[locale]/(main)/statistics/_utils/statisticsCalendar.ts Outdated
Comment thread apps/timo-web/app/[locale]/(main)/statistics/_components/StatisticsCalendar.tsx Outdated
Comment thread apps/timo-web/app/[locale]/(main)/statistics/page.tsx Outdated

@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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
apps/timo-web/app/[locale]/(main)/statistics/_components/StatisticsCalendar.tsx (2)

97-97: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

key prop에 toISOString() 대신 dateKey를 재사용하세요.

이전 리뷰에서 지적된 내용이 아직 반영되지 않았습니다. Line 90에서 이미 dateKey를 계산하고 있으므로, Line 97에서 toISOString()을 다시 호출하는 것은 중복 계산입니다. 더 중요한 것은 toISOString()이 UTC 기준으로 변환되므로, UTC+9 timezone에서 new Date(2026, 6, 1) (local July 1)의 toISOString()"2026-06-30T15:00:00.000Z"가 되어 의도치 않게 전날 키가 생성됩니다.

♻️ 제안하는 수정
- key={calendarDate.date.toISOString()}
+ key={dateKey}
🤖 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
`@apps/timo-web/app/`[locale]/(main)/statistics/_components/StatisticsCalendar.tsx
at line 97, StatisticsCalendar의 calendarDate 렌더링에서 key로 다시 toISOString()을 호출하지
말고, 이미 계산된 dateKey 변수를 재사용하세요. dateKey가 로컬 날짜를 기준으로 안정적인 키를 생성하도록 유지하고,
key={dateKey}로 변경해 UTC 변환으로 날짜가 하루 밀리는 문제와 중복 계산을 해결하세요.

73-82: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

요일 헤더에 aria-label을 추가해 접근성을 개선하세요.

이전 리뷰에서 지적된 내용이 아직 반영되지 않았습니다. WEEKDAYS가 단일 문자("M", "T", "W"...)로 렌더링되므로, 스크린리더 사용자는 두 개의 "T"와 두 개의 "S"를 구분할 수 없습니다. 경로 규칙에 따르면 시맨틱 태그 사용과 접근성이 권장됩니다.

참고: WAI-ARIA aria-label documentation

♿ 제안하는 접근성 개선
  {WEEKDAYS.map((weekday, index) => (
    <div
      key={`${weekday}-${index}`}
+     aria-label={["월", "화", "수", "목", "금", "토", "일"][index]}
      className={cn(
        "typo-headline-m-14 flex h-6 items-center justify-center",
        index >= 5 ? "text-timo-red" : "text-timo-gray-900",
      )}
    >
      {weekday}
    </div>
  ))}
🤖 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
`@apps/timo-web/app/`[locale]/(main)/statistics/_components/StatisticsCalendar.tsx
around lines 73 - 82, 요일 헤더의 단일 문자 표기를 스크린리더가 구분할 수 있도록 접근성을 보완하세요.
StatisticsCalendar의 WEEKDAYS.map 렌더링에서 각 헤더 div에 전체 요일명을 제공하는 aria-label을 추가하고,
기존 시각적 표기와 스타일은 유지하세요.

Source: Path instructions

🤖 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
`@apps/timo-web/app/`[locale]/(main)/statistics/_components/StatisticsCalendar.tsx:
- Line 51: StatisticsCalendar의 `calendarData.today`를 `new Date("YYYY-MM-DD")`로
직접 파싱하지 말고, 연·월·일을 분리해 local timezone 기준으로 Date를 생성하도록 수정하세요. 이후
`formatStatisticsCalendarDate`와 비교·포맷 로직이 동일한 local 날짜를 사용하도록 `today` 초기화 코드를
조정하세요.

---

Outside diff comments:
In
`@apps/timo-web/app/`[locale]/(main)/statistics/_components/StatisticsCalendar.tsx:
- Line 97: StatisticsCalendar의 calendarDate 렌더링에서 key로 다시 toISOString()을 호출하지
말고, 이미 계산된 dateKey 변수를 재사용하세요. dateKey가 로컬 날짜를 기준으로 안정적인 키를 생성하도록 유지하고,
key={dateKey}로 변경해 UTC 변환으로 날짜가 하루 밀리는 문제와 중복 계산을 해결하세요.
- Around line 73-82: 요일 헤더의 단일 문자 표기를 스크린리더가 구분할 수 있도록 접근성을 보완하세요.
StatisticsCalendar의 WEEKDAYS.map 렌더링에서 각 헤더 div에 전체 요일명을 제공하는 aria-label을 추가하고,
기존 시각적 표기와 스타일은 유지하세요.
🪄 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: ASSERTIVE

Plan: Pro Plus

Run ID: 0d9c1a1d-6658-40c3-818b-2673e7c258a9

📥 Commits

Reviewing files that changed from the base of the PR and between 345a47f and 1247588.

📒 Files selected for processing (5)
  • apps/timo-web/app/[locale]/(main)/statistics/_components/StatisticsCalendar.tsx
  • apps/timo-web/app/[locale]/(main)/statistics/_containers/StatisticsPageContainer.tsx
  • apps/timo-web/app/[locale]/(main)/statistics/_utils/formatStatisticsDate.ts
  • apps/timo-web/app/[locale]/(main)/statistics/_utils/statisticsCalendar.ts
  • apps/timo-web/app/[locale]/(main)/statistics/page.tsx

currentMonth,
calendarData,
}: StatisticsCalendarProps) => {
const today = new Date(calendarData.today);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

new Date(calendarData.today) UTC 파싱 이슈가 여전히 남아 있습니다.

이전 리뷰에서 지적된 내용이 아직 반영되지 않았습니다. calendarData.today"YYYY-MM-DD" 형식의 문자열이므로, ECMAScript 스펙상 UTC 자정으로 파싱됩니다. formatStatisticsCalendarDate가 local timezone 기준으로 포맷하므로, UTC-5 등의 timezone에서는 날짜가 하루 앞당겨져 표시됩니다.

참고: ECMAScript Date Time String Format specification

🛡️ 제안하는 안전한 날짜 파싱
- const today = new Date(calendarData.today);
+ const [year, month, day] = calendarData.today.split("-").map(Number);
+ const today = new Date(year, month - 1, day);
🤖 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
`@apps/timo-web/app/`[locale]/(main)/statistics/_components/StatisticsCalendar.tsx
at line 51, StatisticsCalendar의 `calendarData.today`를 `new Date("YYYY-MM-DD")`로
직접 파싱하지 말고, 연·월·일을 분리해 local timezone 기준으로 Date를 생성하도록 수정하세요. 이후
`formatStatisticsCalendarDate`와 비교·포맷 로직이 동일한 local 날짜를 사용하도록 `today` 초기화 코드를
조정하세요.

@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.

♻️ Duplicate comments (1)
apps/timo-web/app/[locale]/(main)/statistics/_components/StatisticsCalendar.tsx (1)

62-62: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

new Date(calendarData.today) UTC 파싱 이슈가 여전히 해결되지 않았습니다.

이전 리뷰에서 두 차례 지적된 내용이 아직 반영되지 않았습니다. calendarData.today"YYYY-MM-DD" 형식 문자열이며, ECMAScript 스펙상 date-only ISO 문자열은 UTC 자정으로 파싱됩니다. formatStatisticsCalendarDate가 local timezone 기준으로 포맷하므로 UTC-5 등의 timezone에서 날짜가 하루 앞당겨져 표시됩니다.

🛡️ 제안하는 안전한 날짜 파싱
- const today = new Date(calendarData.today);
+ const [year, month, day] = calendarData.today.split("-").map(Number);
+ const today = new Date(year, month - 1, day);
🤖 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
`@apps/timo-web/app/`[locale]/(main)/statistics/_components/StatisticsCalendar.tsx
at line 62, StatisticsCalendar의 today 생성에서 date-only 문자열을 new Date(string)으로
파싱하지 않도록 수정하세요. calendarData.today의 YYYY-MM-DD 값을 연·월·일로 분리해 로컬 시간 기준 Date를
생성하고, 이후 formatStatisticsCalendarDate 호출이 해당 로컬 날짜를 사용하도록 하세요.
🤖 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.

Duplicate comments:
In
`@apps/timo-web/app/`[locale]/(main)/statistics/_components/StatisticsCalendar.tsx:
- Line 62: StatisticsCalendar의 today 생성에서 date-only 문자열을 new Date(string)으로 파싱하지
않도록 수정하세요. calendarData.today의 YYYY-MM-DD 값을 연·월·일로 분리해 로컬 시간 기준 Date를 생성하고, 이후
formatStatisticsCalendarDate 호출이 해당 로컬 날짜를 사용하도록 하세요.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: ca19f367-93f9-4247-b39f-da3add383df7

📥 Commits

Reviewing files that changed from the base of the PR and between 1247588 and 1911f94.

📒 Files selected for processing (2)
  • apps/timo-web/app/[locale]/(main)/statistics/_components/StatisticsCalendar.tsx
  • apps/timo-web/app/[locale]/(main)/statistics/_utils/statisticsCalendar.ts

@kimminna kimminna left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

고생하셧습니다! 간단한 코멘트만 확인해 주세요!

Comment thread apps/timo-web/app/[locale]/(main)/statistics/_types/statistics.ts Outdated
Co-authored-by: 김민아 <kmina121777@naver.com>

@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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/timo-web/app/[locale]/(main)/statistics/_containers/StatisticsPageContainer.tsx (1)

7-24: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

StatisticsPageContainer.tsx는 정리 대상입니다. 같은 이름의 export가 두 파일에 있어도 모듈이 분리되어 있어서 충돌은 없습니다. 지금은 page.tsxStatisticsContainer.tsx만 사용하므로, StatisticsPageContainer.tsx는 삭제하거나 실제 사용처에 맞게 파일명/임포트를 하나로 맞춰 두는 편이 깔끔합니다.

🤖 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
`@apps/timo-web/app/`[locale]/(main)/statistics/_containers/StatisticsPageContainer.tsx
around lines 7 - 24, Remove the unused StatisticsPageContainer.tsx, or
consolidate it with StatisticsContainer.tsx by aligning the filename, export,
and imports so page.tsx references a single implementation; verify no remaining
imports depend on the redundant file.
♻️ Duplicate comments (1)
apps/timo-web/app/[locale]/(main)/statistics/_components/StatisticsCalendar.tsx (1)

62-62: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

new Date(calendarData.today) UTC 파싱 이슈가 여전히 해결되지 않았습니다.

이전 리뷰에서 두 차례 지적된 내용이 현재 코드에 반영되지 않았습니다. calendarData.today"2026-07-10" 형식의 date-only ISO 문자열이므로, ECMAScript 스펙상 UTC 자정으로 파싱됩니다. formatStatisticsCalendarDate는 local timezone 기준으로 포맷하므로, UTC-5 등의 timezone에서는 날짜가 하루 앞당겨져 표시됩니다.

참고: ECMAScript Date Time String Format

🛡️ 제안하는 안전한 날짜 파싱
- const today = new Date(calendarData.today);
+ const [year, month, day] = calendarData.today.split("-").map(Number);
+ const today = new Date(year, month - 1, day);
🤖 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
`@apps/timo-web/app/`[locale]/(main)/statistics/_components/StatisticsCalendar.tsx
at line 62, StatisticsCalendar의 `calendarData.today`를 `new Date()`로 직접 파싱하지 말고,
date-only 문자열을 로컬 날짜로 해석하도록 수정하세요. `today` 생성 로직과 `formatStatisticsCalendarDate`
사용 지점을 확인해 연·월·일을 직접 분리해 로컬 `Date`를 생성하거나 동일한 안전한 날짜 파서 함수를 사용하도록 변경하세요.
🤖 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 `@apps/timo-web/app/`[locale]/(main)/statistics/_types/statistics.ts:
- Around line 3-12: statisticsCalendarResponseSchema의 날짜 필드 검증을 강화하세요.
yearMonth와 today는 현재 형식에 맞는 ISO 날짜 스키마를 사용하고, days 내부 객체의 date 필드도 z.string() 대신
Zod 4의 z.iso.date()로 검증하도록 변경하세요.

---

Outside diff comments:
In
`@apps/timo-web/app/`[locale]/(main)/statistics/_containers/StatisticsPageContainer.tsx:
- Around line 7-24: Remove the unused StatisticsPageContainer.tsx, or
consolidate it with StatisticsContainer.tsx by aligning the filename, export,
and imports so page.tsx references a single implementation; verify no remaining
imports depend on the redundant file.

---

Duplicate comments:
In
`@apps/timo-web/app/`[locale]/(main)/statistics/_components/StatisticsCalendar.tsx:
- Line 62: StatisticsCalendar의 `calendarData.today`를 `new Date()`로 직접 파싱하지 말고,
date-only 문자열을 로컬 날짜로 해석하도록 수정하세요. `today` 생성 로직과 `formatStatisticsCalendarDate`
사용 지점을 확인해 연·월·일을 직접 분리해 로컬 `Date`를 생성하거나 동일한 안전한 날짜 파서 함수를 사용하도록 변경하세요.
🪄 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: ASSERTIVE

Plan: Pro Plus

Run ID: 3d4c3e75-a26f-4cf5-8846-21fcd0031483

📥 Commits

Reviewing files that changed from the base of the PR and between 1911f94 and 910cdd4.

📒 Files selected for processing (9)
  • apps/timo-web/app/[locale]/(main)/statistics/_components/StatisticsCalendar.tsx
  • apps/timo-web/app/[locale]/(main)/statistics/_containers/StatisticsContainer.tsx
  • apps/timo-web/app/[locale]/(main)/statistics/_containers/StatisticsHeaderContainer.tsx
  • apps/timo-web/app/[locale]/(main)/statistics/_containers/StatisticsPageContainer.tsx
  • apps/timo-web/app/[locale]/(main)/statistics/_mocks/statistics-calendar.ts
  • apps/timo-web/app/[locale]/(main)/statistics/_types/statistics.ts
  • apps/timo-web/app/[locale]/(main)/statistics/_utils/format-statistics-date.ts
  • apps/timo-web/app/[locale]/(main)/statistics/_utils/statistics-calendar.ts
  • apps/timo-web/app/[locale]/(main)/statistics/page.tsx
💤 Files with no reviewable changes (1)
  • apps/timo-web/app/[locale]/(main)/statistics/_containers/StatisticsHeaderContainer.tsx

Comment on lines +3 to +12
export const statisticsCalendarResponseSchema = z.object({
yearMonth: z.string(),
today: z.string(),
days: z.array(
z.object({
date: z.string(),
completionRate: z.number().nullable(),
}),
),
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Zod 4의 z.iso.date()로 날짜 문자열 검증을 강화하는 것을 제안합니다.

현재 z.string()은 모든 문자열을 허용하므로 날짜 형식 오류를 런타임에서 잡을 수 없습니다. Zod 4에서新增된 z.iso.date()를 사용하면 YYYY-MM-DD 형식 검증이 보장됩니다.

참고: Zod 4 String Enhancements

♻️ 제안하는 스키마 개선
 export const statisticsCalendarResponseSchema = z.object({
-  yearMonth: z.string(),
-  today: z.string(),
+  yearMonth: z.string().regex(/^\d{4}-\d{2}$/),
+  today: z.iso.date(),
   days: z.array(
     z.object({
-      date: z.string(),
+      date: z.iso.date(),
       completionRate: z.number().nullable(),
     }),
   ),
 });
📝 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.

Suggested change
export const statisticsCalendarResponseSchema = z.object({
yearMonth: z.string(),
today: z.string(),
days: z.array(
z.object({
date: z.string(),
completionRate: z.number().nullable(),
}),
),
});
export const statisticsCalendarResponseSchema = z.object({
yearMonth: z.string().regex(/^\d{4}-\d{2}$/),
today: z.iso.date(),
days: z.array(
z.object({
date: z.iso.date(),
completionRate: z.number().nullable(),
}),
),
});
🤖 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 `@apps/timo-web/app/`[locale]/(main)/statistics/_types/statistics.ts around
lines 3 - 12, statisticsCalendarResponseSchema의 날짜 필드 검증을 강화하세요. yearMonth와
today는 현재 형식에 맞는 ISO 날짜 스키마를 사용하고, days 내부 객체의 date 필드도 z.string() 대신 Zod 4의
z.iso.date()로 검증하도록 변경하세요.

@ehye1 ehye1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

고생햇셔요!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✨ Feature 새로운 기능(기능성) 구현 ⌚ Timo-Design-system Timo 디자인 시스템 ⏰ Timo-web Timo 웹 서비스 ♣️ 유민 유민양

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEAT] 통계 월간 캘린더 구현

4 participants