[FEAT] 공통 캘린더 컴포넌트 구현#119
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning Review limit reached
Next review available in: 30 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the 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 configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (5)
Walkthrough디자인 시스템에 Calendar 컴포넌트와 Storybook 스토리가 추가되었습니다. 날짜 셀 생성, 선택 상태 처리, 월/연 헤더 렌더링, ChangesCalendar 컴포넌트 구현
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant User
participant CalendarDemo
participant Calendar
participant WeeklyButton
User->>WeeklyButton: 이전/다음 월 클릭
WeeklyButton->>Calendar: onClick 실행
Calendar->>Calendar: visibleMonth 갱신 및 날짜 재계산
User->>Calendar: 날짜 셀 클릭
Calendar->>CalendarDemo: onChange(date) 호출
CalendarDemo->>Calendar: value 갱신
Possibly related PRs
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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 |
Timo Performance ReportBundle Size — timo-web
Lighthouse — timo-web
Image Optimization — timo-web
측정 커밋: |
There was a problem hiding this comment.
Actionable comments posted: 6
🤖 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 `@packages/timo-design-system/src/components/calendar/Calendar.stories.tsx`:
- Around line 19-32: In CalendarDemo, the initial selectedDate and defaultMonth
point to different months, so the selected day is not visible when the story
opens. Update the story so Calendar’s value and defaultMonth are in the same
month, using the existing CalendarDemo, selectedDate, and CalendarProps setup so
the selection styling can be seen immediately in the default story.
In `@packages/timo-design-system/src/components/calendar/Calendar.tsx`:
- Around line 8-21: The MONTH_LABELS constant in Calendar is hardcoded to
English month names, so update the month label rendering to be locale-aware
using Intl.DateTimeFormat (or confirm English is intentionally required by the
design). Adjust the Calendar component to derive month names from the active
locale instead of relying on the static MONTH_LABELS array.
- Around line 3-4: Replace the relative imports in Calendar with absolute
imports by updating the `cn` and `WeeklyButton` imports to use the project’s
absolute path alias instead of `../../lib` and
`../button/weekly-button/WeeklyButton`. Keep the `Calendar` component unchanged
otherwise and ensure the new import paths resolve to the same modules via the
existing absolute path convention.
- Around line 119-142: `Calendar`의 날짜 버튼 클릭 처리에서 다른
달(`calendarDate.isCurrentMonth === false`) 날짜를 눌렀을 때도 현재 보이는 달이 바뀌도록 수정하세요.
`calendarDates.map` 안의 `button` `onClick`에서 `onChange` 호출 후, 선택한
`calendarDate.date`의 월을 기준으로 `visibleMonth`를 갱신하거나 부모로 월 변경 이벤트를 함께 전달하는 방식으로
연결하면 됩니다. `isCurrentMonth`, `onChange`, `visibleMonth`가 이 동작을 제어하는 핵심이니, 흐리게 표시된
날짜 클릭 시 해당 월로 자동 이동하도록 처리하세요.
- Around line 64-66: The visibleMonth state in Calendar is initialized only once
with useState(defaultMonth ?? value ?? new Date()), so it does not follow later
controlled value updates. Update Calendar so visibleMonth stays in sync when the
value prop changes, for example by adding a useEffect in Calendar.tsx that
watches value and updates setVisibleMonth accordingly while preserving
defaultMonth behavior. Use the Calendar and visibleMonth state in
packages/timo-design-system/src/components/calendar/Calendar.tsx to locate the
fix.
- Around line 29-49: The fixed 35-item range in getCalendarDates is truncating
months that require 6 weeks. Update Calendar.tsx so getCalendarDates generates
42 dates instead of 35, and make sure the calendar grid logic in Calendar and
WEEKDAYS stays separated so the header row does not reduce the visible date
slots. Keep the existing date calculation and isCurrentMonth behavior, just
extend the range to cover the full month display.
🪄 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: 3c4ab145-16b2-4b5f-8105-74310a0f569a
📒 Files selected for processing (3)
packages/timo-design-system/src/components/calendar/Calendar.stories.tsxpackages/timo-design-system/src/components/calendar/Calendar.tsxpackages/timo-design-system/src/components/index.ts
| import { cn } from "../../lib"; | ||
| import { WeeklyButton } from "../button/weekly-button/WeeklyButton"; |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
상대 경로 import는 절대 경로로 바꿔주세요.
"../../lib", "../button/weekly-button/WeeklyButton" 모두 상대 경로 import입니다.
As per path instructions, **/*.{ts,tsx}: "절대 경로 import 사용 (상대 경로 ../../ 지양)".
📦 절대 경로 import 예시
-import { cn } from "../../lib";
-import { WeeklyButton } from "../button/weekly-button/WeeklyButton";
+import { cn } from "`@/lib`";
+import { WeeklyButton } from "`@/components/button/weekly-button/WeeklyButton`";📝 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.
| import { cn } from "../../lib"; | |
| import { WeeklyButton } from "../button/weekly-button/WeeklyButton"; | |
| import { cn } from "`@/lib`"; | |
| import { WeeklyButton } from "`@/components/button/weekly-button/WeeklyButton`"; |
🤖 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 `@packages/timo-design-system/src/components/calendar/Calendar.tsx` around
lines 3 - 4, Replace the relative imports in Calendar with absolute imports by
updating the `cn` and `WeeklyButton` imports to use the project’s absolute path
alias instead of `../../lib` and `../button/weekly-button/WeeklyButton`. Keep
the `Calendar` component unchanged otherwise and ensure the new import paths
resolve to the same modules via the existing absolute path convention.
Source: Path instructions
| const [visibleMonth, setVisibleMonth] = useState( | ||
| defaultMonth ?? value ?? new Date(), | ||
| ); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
visibleMonth가 controlled value 변경을 따라가지 않아요.
useState(defaultMonth ?? value ?? new Date())는 마운트 시 한 번만 평가됩니다. 이후 부모가 value를 다른 달의 날짜로 바꿔도 visibleMonth는 그대로 남아 표시되는 달이 갱신되지 않습니다. Storybook 데모(Calendar.stories.tsx)처럼 value를 controlled로 관리하는 소비 패턴을 고려하면, 외부에서 값이 바뀌는 경우를 지원해야 할 가능성이 높습니다.
useEffect로 value 변경 시 visibleMonth를 동기화하거나, 최소한 이 제약을 CalendarProps 문서에 명시하는 것을 권장드려요.
🔄 value 변경 시 visibleMonth 동기화 제안
+import { useEffect, useState } from "react";
-import { useState } from "react";
export const Calendar = ({ value, defaultMonth, onChange, className }: CalendarProps) => {
const [visibleMonth, setVisibleMonth] = useState(
defaultMonth ?? value ?? new Date(),
);
+ useEffect(() => {
+ if (value) {
+ setVisibleMonth(value);
+ }
+ }, [value]);🤖 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 `@packages/timo-design-system/src/components/calendar/Calendar.tsx` around
lines 64 - 66, The visibleMonth state in Calendar is initialized only once with
useState(defaultMonth ?? value ?? new Date()), so it does not follow later
controlled value updates. Update Calendar so visibleMonth stays in sync when the
value prop changes, for example by adding a useEffect in Calendar.tsx that
watches value and updates setVisibleMonth accordingly while preserving
defaultMonth behavior. Use the Calendar and visibleMonth state in
packages/timo-design-system/src/components/calendar/Calendar.tsx to locate the
fix.
| <WeeklyButton | ||
| variant="left" | ||
| onClick={() => moveMonth(-1)} | ||
| className="absolute top-0 left-0" | ||
| /> |
There was a problem hiding this comment.
캘린더 버튼에서도 WeeklyButton이 쓰여서, 위클리 버튼 자체 컴포넌트명을 변경하는 게 어떨까 싶어요!
아이콘 기반이니까 ChevronButton 정도가 어떨까요?
Co-authored-by: 김민아 <kmina121777@naver.com>
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/timo-design-system/src/components/calendar/Calendar.tsx (1)
88-107: 🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win
moveMonth가 정의되지 않아 월 이동 버튼이 동작하지 않아요. 🫠함수명을
moveMonth에서handleMoveMonth로 리네임하셨지만, 두WeeklyButton의onClick호출 site는 여전히moveMonth를 참조하고 있습니다. 버튼 클릭 시ReferenceError: moveMonth is not defined가 발생합니다.정적 분석에서도
'handleMoveMonth' is assigned a value but never used경고가 나오고 있어요.🔧 호출 site 수정 제안
<WeeklyButton variant="left" - onClick={() => moveMonth(-1)} + onClick={() => handleMoveMonth(-1)} className="absolute top-0 left-0" /><WeeklyButton variant="right" - onClick={() => moveMonth(1)} + onClick={() => handleMoveMonth(1)} className="absolute top-0 right-0" />🤖 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 `@packages/timo-design-system/src/components/calendar/Calendar.tsx` around lines 88 - 107, The month navigation buttons in Calendar are still calling the old moveMonth handler, so the renamed handler is unused and clicks throw a ReferenceError. Update the onClick handlers on both WeeklyButton instances in Calendar.tsx to call handleMoveMonth with the correct direction values, and ensure the component consistently uses the renamed handler instead of moveMonth.Source: Linters/SAST tools
🤖 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.
Outside diff comments:
In `@packages/timo-design-system/src/components/calendar/Calendar.tsx`:
- Around line 88-107: The month navigation buttons in Calendar are still calling
the old moveMonth handler, so the renamed handler is unused and clicks throw a
ReferenceError. Update the onClick handlers on both WeeklyButton instances in
Calendar.tsx to call handleMoveMonth with the correct direction values, and
ensure the component consistently uses the renamed handler instead of moveMonth.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 0ba05bbb-fd6d-452e-a346-c611ee9a9518
📒 Files selected for processing (1)
packages/timo-design-system/src/components/calendar/Calendar.tsx
There was a problem hiding this comment.
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)
packages/timo-design-system/src/components/calendar/Calendar.tsx (2)
95-95: 🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win🚨
moveMonth가 정의되지 않았어요 — 런타임 ReferenceError 발생!Line 82에서
handleMoveMonth로 정의했지만, Line 95와 110에서는moveMonth를 호출하고 있습니다.moveMonth는 스코프에 존재하지 않아 클릭 시ReferenceError: moveMonth is not defined가 발생합니다. 이는 파이프라인 Type Check 실패의 원인이기도 합니다.🔧 제안: `handleMoveMonth`로 통일
<WeeklyButton variant="left" - onClick={() => moveMonth(-1)} + onClick={() => handleMoveMonth(-1)} className="absolute top-0 left-0" /><WeeklyButton variant="right" - onClick={() => moveMonth(1)} + onClick={() => handleMoveMonth(1)} className="absolute top-0 right-0" />Also applies to: 110-110
🤖 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 `@packages/timo-design-system/src/components/calendar/Calendar.tsx` at line 95, `Calendar` has a handler name mismatch: `handleMoveMonth` is defined, but the click handlers still call `moveMonth`, which causes a runtime ReferenceError. Update the `Calendar` component so the month navigation buttons consistently use `handleMoveMonth` everywhere it is referenced, including both previous/next controls, and keep the naming aligned with the existing function definition.Source: Pipeline failures
115-115: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
grid-rows-6는 6주 달(42칸)에서 레이아웃이 넘칩니다.요일 헤더 7칸(1행) + 날짜 셀이 같은 grid 안에 있습니다.
calendarCellCount가 42일 때 총 49개 아이템 = 7행이 필요하지만grid-rows-6로 고정되어 있어 7번째 행이 암시적 행으로 처리됩니다. 이로 인해 6주 달의 마지막 주가 의도한 높이로 렌더되지 않을 수 있습니다.요일 헤더를 별도 grid로 분리하거나,
grid-rows-7로 변경하는 것을 권장합니다.📐 제안: 요일 헤더 분리
- <div className={cn("grid grid-cols-7 grid-rows-6 gap-x-1 gap-y-1.5")}> - {WEEKDAYS.map((weekday, index) => ( - <div - key={`${weekday}-${index}`} - className="typo-headline-b-16 text-timo-gray-900 flex items-center justify-center" - > - {weekday} - </div> - ))} - - {calendarDates.map((calendarDate) => { + <div className="mb-1.5 grid grid-cols-7 gap-x-1"> + {WEEKDAYS.map((weekday, index) => ( + <div + key={`${weekday}-${index}`} + className="typo-headline-b-16 text-timo-gray-900 flex items-center justify-center" + > + {weekday} + </div> + ))} + </div> + + <div className={cn("grid grid-cols-7 grid-rows-6 gap-x-1 gap-y-1.5")}> + {calendarDates.map((calendarDate) => {🤖 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 `@packages/timo-design-system/src/components/calendar/Calendar.tsx` at line 115, The Calendar grid uses a fixed grid-rows-6 while the same grid contains both the 7-day header and all date cells, so 6-week months can overflow into an implicit 7th row. Update the layout in Calendar to either separate the weekday header from the date grid or change the container to use grid-rows-7, and keep the logic around calendarCellCount aligned with the rendered row count.
♻️ Duplicate comments (2)
packages/timo-design-system/src/components/index.ts (1)
35-35: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
WeeklyButtonexport명과 파일명ChevronButton이 불일치합니다.Calendar.tsx 리뷰에서 통합 제안드린 rename과 동일한 이슈입니다. export명도
ChevronButton으로 맞춰주세요.🔧 제안
-export { WeeklyButton } from "./button/chevron-button/ChevronButton"; +export { ChevronButton } from "./button/chevron-button/ChevronButton";🤖 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 `@packages/timo-design-system/src/components/index.ts` at line 35, The component export name in the index barrel is mismatched with the actual component/file name, so update the export in the components index to use ChevronButton instead of WeeklyButton. Keep the exported symbol aligned with the existing ChevronButton component from button/chevron-button/ChevronButton so imports and naming stay consistent across the design system.packages/timo-design-system/src/components/button/chevron-button/ChevronButton.stories.tsx (1)
1-6: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winStorybook title은
ChevronButton인데 import는WeeklyButton이에요.title과 컴포넌트명이 불일치하면 Storybook에서 혼란이 발생합니다. Calendar.tsx에서 제안드린 전체 rename을 적용하면 자연스럽게 해결됩니다.
🔧 제안
-import { WeeklyButton } from "./ChevronButton"; +import { ChevronButton } from "./ChevronButton";이하
WeeklyButton→ChevronButton치환🤖 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 `@packages/timo-design-system/src/components/button/chevron-button/ChevronButton.stories.tsx` around lines 1 - 6, The Storybook story is still importing and referencing WeeklyButton while the story title and component are ChevronButton, so update ChevronButton.stories.tsx to use ChevronButton consistently. Replace the WeeklyButton import and any related story metadata/args references with ChevronButton so the story name, imported symbol, and component identity all match.
🤖 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 `@packages/timo-design-system/src/components/calendar/Calendar.tsx`:
- Line 4: Rename the remaining WeeklyButton symbols to ChevronButton so the
component, exports, and types all match the new file/story naming. Update the
component definition in ChevronButton-related code, including WeeklyButtonProps
and WeeklyButtonVariantTypes to ChevronButtonProps and
ChevronButtonVariantTypes, and adjust any exports/imports such as the one used
in Calendar.tsx and the barrel index.ts so they reference ChevronButton
consistently.
---
Outside diff comments:
In `@packages/timo-design-system/src/components/calendar/Calendar.tsx`:
- Line 95: `Calendar` has a handler name mismatch: `handleMoveMonth` is defined,
but the click handlers still call `moveMonth`, which causes a runtime
ReferenceError. Update the `Calendar` component so the month navigation buttons
consistently use `handleMoveMonth` everywhere it is referenced, including both
previous/next controls, and keep the naming aligned with the existing function
definition.
- Line 115: The Calendar grid uses a fixed grid-rows-6 while the same grid
contains both the 7-day header and all date cells, so 6-week months can overflow
into an implicit 7th row. Update the layout in Calendar to either separate the
weekday header from the date grid or change the container to use grid-rows-7,
and keep the logic around calendarCellCount aligned with the rendered row count.
---
Duplicate comments:
In
`@packages/timo-design-system/src/components/button/chevron-button/ChevronButton.stories.tsx`:
- Around line 1-6: The Storybook story is still importing and referencing
WeeklyButton while the story title and component are ChevronButton, so update
ChevronButton.stories.tsx to use ChevronButton consistently. Replace the
WeeklyButton import and any related story metadata/args references with
ChevronButton so the story name, imported symbol, and component identity all
match.
In `@packages/timo-design-system/src/components/index.ts`:
- Line 35: The component export name in the index barrel is mismatched with the
actual component/file name, so update the export in the components index to use
ChevronButton instead of WeeklyButton. Keep the exported symbol aligned with the
existing ChevronButton component from button/chevron-button/ChevronButton so
imports and naming stay consistent across the design system.
🪄 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: ad192f05-55e5-4428-96f0-a72f2bb9b41d
📒 Files selected for processing (5)
packages/timo-design-system/src/components/button/chevron-button/ChevronButton.stories.tsxpackages/timo-design-system/src/components/button/chevron-button/ChevronButton.tsxpackages/timo-design-system/src/components/calendar/Calendar.stories.tsxpackages/timo-design-system/src/components/calendar/Calendar.tsxpackages/timo-design-system/src/components/index.ts
| import { useState } from "react"; | ||
|
|
||
| import { cn } from "../../lib"; | ||
| import { WeeklyButton } from "../button/chevron-button/ChevronButton"; |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy lift
WeeklyButton → ChevronButton 이름 변경이 완료되지 않았어요.
파일명은 ChevronButton으로 바뀌었고 Storybook title도 ChevronButton으로 갱신되었지만, 컴포넌트 자체는 여전히 WeeklyButton입니다. 파일명과 export명이 불일치하면 사용자가 헷갈립니다. kimminna님의 이전 리뷰에서도 ChevronButton으로 변경을 제안하셨던 부분입니다.
컴포넌트명, export명, 타입명(WeeklyButtonProps → ChevronButtonProps, WeeklyButtonVariantTypes → ChevronButtonVariantTypes)까지 모두 ChevronButton으로 통일하는 것을 권장합니다.
♻️ 전체 rename 제안
ChevronButton.tsx 내부:
-export type WeeklyButtonVariantTypes = "left" | "right";
+export type ChevronButtonVariantTypes = "left" | "right";
-const WEEKLY_BUTTON_ICON: Record<WeeklyButtonVariantTypes, ReactNode> = {
+const CHEVRON_BUTTON_ICON: Record<ChevronButtonVariantTypes, ReactNode> = {
-const WEEKLY_BUTTON_ARIA_LABEL: Record<WeeklyButtonVariantTypes, string> = {
+const CHEVRON_BUTTON_ARIA_LABEL: Record<ChevronButtonVariantTypes, string> = {
-export interface WeeklyButtonProps {
+export interface ChevronButtonProps {
-export const WeeklyButton = ({ ... }: WeeklyButtonProps) => {
+export const ChevronButton = ({ ... }: ChevronButtonProps) => {index.ts:
-export { WeeklyButton } from "./button/chevron-button/ChevronButton";
+export { ChevronButton } from "./button/chevron-button/ChevronButton";Calendar.tsx:
-import { WeeklyButton } from "../button/chevron-button/ChevronButton";
+import { ChevronButton } from "../button/chevron-button/ChevronButton";Also applies to: 125-160
🤖 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 `@packages/timo-design-system/src/components/calendar/Calendar.tsx` at line 4,
Rename the remaining WeeklyButton symbols to ChevronButton so the component,
exports, and types all match the new file/story naming. Update the component
definition in ChevronButton-related code, including WeeklyButtonProps and
WeeklyButtonVariantTypes to ChevronButtonProps and ChevronButtonVariantTypes,
and adjust any exports/imports such as the one used in Calendar.tsx and the
barrel index.ts so they reference ChevronButton consistently.
ISSUE 🔗
close #107
What is this PR? 🔍
디자인 시스템에 날짜 선택이 가능한
Calendar공통 컴포넌트를 추가했습니다.이번 작업은 단순히 캘린더 UI를 하나 그리는 것보다, “이걸 라이브러리로 가져갈지, 직접 구현할지”를 고민하면서 진행했습니다.
왜 직접 구현했는지
처음에는
react-day-picker같은 캘린더 라이브러리를 도입하는 것도 고려했습니다.다만 지금 필요한 기능은 날짜 선택, 월 이동, 오늘 날짜 표시, 이전/다음 달 날짜 흐리게 표시 정도였습니다.
반대로 디자인 요구사항은 꽤 구체적이었습니다.
WeeklyButton과 동일한 월 이동 버튼이 정도 요구사항이면 라이브러리의 DOM 구조와 스타일을 덮어쓰는 비용이 더 커질 수 있다고 판단했습니다.
그래서 이번에는 외부 라이브러리 없이, 필요한 범위만 직접 구현하는 방향으로 결정했습니다.
날짜 그리드 계산
캘린더에서 제일 먼저 정리한 부분은 “이번 달 날짜만 1일부터 31일까지 보여주면 되는가?”였습니다.
실제로는 캘린더 첫 칸이 항상 1일이 아닙니다.
예를 들어 2026년 6월은 1일이 월요일이라, 첫 칸에는 5월 31일이 들어가야 합니다.
그래서 현재 월의 1일 요일을 기준으로 캘린더 시작 날짜를 계산하고, 그 날짜부터 순서대로 날짜 데이터를 만들었습니다.
이렇게 하면 1일이 월요일인 달은 하루 전인 일요일부터, 1일이 수요일인 달은 사흘 전인 일요일부터 캘린더를 시작할 수 있습니다.
Figma 간격을 맞추면서 겪은 점
레이아웃을 맞추는 과정에서 가장 헷갈렸던 부분은 gap 기준이었습니다.
Figma에서 글자 자체 사이의 간격과, 글자를 감싸는 셀 기준의 간격이 다르게 보였습니다.
처음에는 날짜 텍스트 각각에 고정 크기를 주려고 했지만, 그러면 텍스트 기준과 셀 기준이 섞여서 오히려 맞추기 어려웠습니다.
그래서 최종적으로는 캘린더 전체 너비를 고정하고, 내부 padding과 grid gap을 기준으로 배치했습니다.
날짜 텍스트는 셀 안에서 중앙 정렬되도록 두고, 개별 텍스트마다 억지로 너비를 맞추지는 않았습니다.
flowchart LR A["Figma 간격 확인"] --> B{"기준 선택"} B --> C["글자 기준"] C --> C1["텍스트마다 크기 고정"] C1 --> C2["셀 기준과 섞임"] B --> D["셀 기준"] D --> D1["전체 width + padding"] D1 --> D2["grid gap"] D2 --> E["채택"]또 하나의 트러블은 h-full이었습니다.
grid에 h-full을 주면 부모 높이를 전부 채우려 하면서 하단 날짜가 컨테이너 밖으로 밀리는 문제가 있었습니다.
그래서 grid는 필요한 만큼의 높이만 가지도록 두고, 캘린더 컨테이너의 고정 크기와 padding 안에서 정렬되도록 조정했습니다.
기존 버튼 재사용
월 이동 버튼은 새로 만들지 않고 기존 WeeklyButton을 재사용했습니다.
선택 날짜와 오늘 날짜
선택 날짜와 오늘 날짜는 비슷해 보이지만 다른 상태라서 분리해서 계산했습니다.
처음에는 “선택된 날짜인지 확인하는 함수도 prop으로 받아야 하나?”를 고민했지만, 현재 구조에서는 value가 선택 날짜의 기준이기 때문에 Calendar 내부에서 isSelected를 계산하는 게 더 단순했습니다.
사용처는 value와 onChange만 관리하고, 어떤 날짜가 선택되었는지는 Calendar가 판단합니다.
날짜 칸 수를 동적으로 계산한 이유
캘린더를 만들면서 35칸으로 고정할지, 42칸으로 고정할지 고민했습니다.
처음에는 Figma 기준이 5주 형태에 가까워 보여서 35칸으로 시작했습니다.
하지만 2026년 8월처럼 1일이 토요일이고 31일까지 있는 달은 5주 안에 모든 날짜가 들어가지 않습니다.
이런 달은 실제로 6주가 필요하기 때문에, 모든 달을 35칸으로 고정하면 마지막 주 날짜가 잘릴 수 있습니다.
그래서 현재 월의 시작 요일과 마지막 날짜를 기준으로 필요한 칸 수를 계산하도록 변경했습니다.
firstDay + lastDay는 “이전 달 날짜로 채워야 하는 칸 수 + 이번 달 날짜 수”를 의미합니다.
이 값이 35를 넘으면 5주 안에 표시할 수 없기 때문에 42칸을 사용하고, 그렇지 않으면 35칸을 사용합니다.
이렇게 하면 5주로 충분한 달은 기존처럼 5주로 표시하고, 6주가 필요한 달만 한 줄 더 렌더링할 수 있습니다.
To Reviewers
Calendar를 외부 라이브러리로 만들지 않고 직접 구현한 이유는 현재 요구사항이 날짜 선택과 월 이동 중심으로 작고, Figma 디자인과 디자인 시스템 토큰을 맞추는 비중이 더 컸기 때문입니다.
특히 아래 지점을 중점적으로 봐주시면 좋겠습니다.
Screenshot 📷
2026-07-08.6.47.27.mov
Test Checklist ✔