[FEAT] 오늘 탭 API 연동 - #185
Conversation
- home 도메인에 있던 todoSchema, dayOfWeekSchema 등 응답 파싱 스키마를 api/todo/todo-schema.ts로 이동했습니다 - home/_types/todo-type.ts를 re-export 배럴로 변환해 기존 home 내부 import를 유지했습니다 - home/_types/home-view-type.ts의 apiDayOfWeekSchema를 공유 위치에서 import하도록 수정했습니다 - 도메인 간 직접 import 금지 규칙(structure.md)을 준수했습니다
- /api/v1/home/today 엔드포인트를 useSuspenseQuery로 연동했습니다 - today/_types/today-type.ts에 todayTodoSchema, todayDataSchema를 작성해 응답을 zod로 검증했습니다 - today/_queries/use-today.ts를 작성해 API 데이터를 가져오고 BaseResponse를 언랩했습니다 - useTodayTodoList 초기값을 mock에서 API 데이터로 교체했습니다 - completedCount/totalCount를 로컬 todos state 기반으로 계산해 체크 즉시 반영되도록 수정했습니다 - TodoMock 의존성을 제거하고 TodayTodo 타입으로 통일했습니다 - TodayTodoListContainer를 AsyncBoundary로 감쌌습니다
- 탭 이동 시 항상 최신 데이터를 가져오도록 staleTime을 0으로 설정했습니다
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Walkthrough공통 Todo Zod 스키마와 타입을 중앙화하고, Today 페이지가 오늘 데이터를 API에서 조회·검증해 목록에 전달하도록 변경했습니다. 목 데이터 기반 상태와 생성 타입을 실제 Today 응답 타입으로 전환하고 비동기 렌더링 경계를 추가했습니다. ChangesToday TODO API 연동
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant TodayPage
participant AsyncBoundary
participant TodayTodoListContainer
participant useToday
participant TodayAPI
TodayPage->>AsyncBoundary: 목록 컨테이너 렌더링
AsyncBoundary->>TodayTodoListContainer: 비동기 콘텐츠 렌더링
TodayTodoListContainer->>useToday: 오늘 데이터 조회
useToday->>TodayAPI: getToday 요청
TodayAPI-->>useToday: Today 응답
useToday->>useToday: todayDataSchema.parse
useToday-->>TodayTodoListContainer: 검증된 TodayData
TodayTodoListContainer->>TodayTodoListContainer: data.todos로 목록 초기화
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 |
There was a problem hiding this comment.
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/api/todo/todo-schema.ts`:
- Around line 121-136: Apply the type-naming convention at
apps/timo-web/api/todo/todo-schema.ts:121-136 by renaming the literal/union
alias TodoIcon to TodoIconTypes and declaring object-shaped inferred types such
as Todo and TodoTag as interfaces extending their corresponding Zod-inferred
schemas; preserve existing exports and usages. At
apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_types/today-type.ts:17-18,
convert the object types TodayTodo and TodayData from type aliases to interfaces
without changing their shapes.
- Around line 105-111: Update the durationSeconds and sortOrder fields in the
todo schema to handle null values before applying their zero fallback, matching
the nullish transform pattern used by priority and tag. Preserve the existing
numeric validation and ensure both undefined and null inputs resolve to 0
instead of causing a ZodError.
In
`@apps/timo-web/app/`[locale]/(main)/(with-time-sidebar)/today/_containers/TodayTodoListContainer.tsx:
- Around line 11-19: Synchronize the local todos state in useTodayTodoList with
refreshed server data instead of using data.todos only as the initial state. Add
an effect keyed by the incoming todos argument to update the hook’s todos state
whenever useToday provides new data, while preserving the existing local todo
interactions.
In
`@apps/timo-web/app/`[locale]/(main)/(with-time-sidebar)/today/_hooks/useTodayTodoList.ts:
- Around line 7-8: When the backend mutation APIs are integrated, remove the
local todos state and useState-based updates from useTodayTodoList, making the
React Query cache the single source of truth. Implement check, replay, and
delete optimistic updates by updating the relevant query cache directly while
preserving immediate UI feedback.
🪄 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: 076b8a84-8729-44ce-a004-2fffdaeaf229
📒 Files selected for processing (11)
apps/timo-web/api/todo/todo-schema.tsapps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_types/home-view-type.tsapps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_types/todo-type.tsapps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_containers/TodayDateHeaderContainer.tsxapps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_containers/TodayTodoListContainer.tsxapps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_containers/todo-modal/CreateTodoModalContainer.tsxapps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_hooks/todo-modal/use-create-todo-submit.tsapps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_hooks/useTodayTodoList.tsapps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_queries/use-today.tsapps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_types/today-type.tsapps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/page.tsx
| durationSeconds: z.number().default(0), | ||
| priority: todoPrioritySchema.nullish().transform((v) => v ?? "MEDIUM"), | ||
| tag: todoTagSchema.nullish().transform((v) => v ?? undefined), | ||
| hasMemo: z.boolean(), | ||
| isRepeated: z.boolean(), | ||
| timerStatus: todoTimerStatusSchema, | ||
| sortOrder: z.number().default(0), |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Zod .default()의 null 처리 주의
Zod 4에서 .default()는 필드 값이 undefined일 때만 동작하며, null에는 적용되지 않습니다. 서버 API 응답에서 durationSeconds나 sortOrder가 빈 값으로 null을 반환할 경우 파싱 에러(ZodError)가 발생하게 됩니다.
안전한 폴백 처리를 위해 다른 필드(priority, tag)처럼 .nullish().transform()을 사용하거나 .catch()를 활용하는 것을 권장합니다! 😉
🛠️ 제안하는 수정안
- durationSeconds: z.number().default(0),
+ durationSeconds: z.number().nullish().transform((v) => v ?? 0),
priority: todoPrioritySchema.nullish().transform((v) => v ?? "MEDIUM"),
tag: todoTagSchema.nullish().transform((v) => v ?? undefined),
hasMemo: z.boolean(),
isRepeated: z.boolean(),
timerStatus: todoTimerStatusSchema,
- sortOrder: z.number().default(0),
+ sortOrder: z.number().nullish().transform((v) => v ?? 0),📝 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.
| durationSeconds: z.number().default(0), | |
| priority: todoPrioritySchema.nullish().transform((v) => v ?? "MEDIUM"), | |
| tag: todoTagSchema.nullish().transform((v) => v ?? undefined), | |
| hasMemo: z.boolean(), | |
| isRepeated: z.boolean(), | |
| timerStatus: todoTimerStatusSchema, | |
| sortOrder: z.number().default(0), | |
| durationSeconds: z.number().nullish().transform((v) => v ?? 0), | |
| priority: todoPrioritySchema.nullish().transform((v) => v ?? "MEDIUM"), | |
| tag: todoTagSchema.nullish().transform((v) => v ?? undefined), | |
| hasMemo: z.boolean(), | |
| isRepeated: z.boolean(), | |
| timerStatus: todoTimerStatusSchema, | |
| sortOrder: z.number().nullish().transform((v) => v ?? 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 `@apps/timo-web/api/todo/todo-schema.ts` around lines 105 - 111, Update the
durationSeconds and sortOrder fields in the todo schema to handle null values
before applying their zero fallback, matching the nullish transform pattern used
by priority and tag. Preserve the existing numeric validation and ensure both
undefined and null inputs resolve to 0 instead of causing a ZodError.
| export type CreateTodoRequest = z.infer<typeof createTodoRequestSchema>; | ||
| export type RecommendDurationResponseData = z.infer< | ||
| typeof recommendDurationResponseSchema | ||
| >; | ||
|
|
||
| export type TodoIcon = z.infer<typeof todoIconSchema>; | ||
| export type TodoPriority = z.infer<typeof todoPrioritySchema>; | ||
| export type TodoPriorityTypes = TodoPriority; | ||
| export type TodoRepeatType = z.infer<typeof todoRepeatTypeSchema>; | ||
| export type TodoRepeatWeekday = z.infer<typeof todoRepeatWeekdaySchema>; | ||
| export type DayOfWeek = z.infer<typeof dayOfWeekSchema>; | ||
| export type TodoTimerStatusTypes = z.infer<typeof todoTimerStatusSchema>; | ||
| export type TodoTag = z.infer<typeof todoTagSchema>; | ||
| export type TodoSubtask = z.infer<typeof todoSubtaskSchema>; | ||
| export type Todo = z.infer<typeof todoSchema>; | ||
| export type TodoCreateResponseData = z.infer<typeof todoCreateResponseSchema>; |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value
타입 선언 및 명명 규칙 컨벤션 확인
가이드라인(경로 지침)에 따르면 '객체(Props 등) 타입은 interface로 선언', **'type alias는 유니언·튜플·리터럴에만 사용하며 접미사 Types 추가'**라는 명명 규칙이 명시되어 있습니다. Zod를 통해 추론(z.infer)된 타입들에도 해당 팀 컨벤션을 엄격하게 적용할지 가볍게 검토해 보시면 좋을 것 같습니다! 😊
apps/timo-web/api/todo/todo-schema.ts#L121-L136:TodoIcon을TodoIconTypes로 변경하거나,Todo나TodoTag같은 객체 타입은export interface Todo extends z.infer<typeof todoSchema> {}형태로 확장을 고려해 볼 수 있습니다.apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_types/today-type.ts#L17-L18: 동일하게TodayTodo,TodayData객체 타입도interface로 선언을 고려해 볼 수 있습니다.
📍 Affects 2 files
apps/timo-web/api/todo/todo-schema.ts#L121-L136(this comment)apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_types/today-type.ts#L17-L18
🤖 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/api/todo/todo-schema.ts` around lines 121 - 136, Apply the
type-naming convention at apps/timo-web/api/todo/todo-schema.ts:121-136 by
renaming the literal/union alias TodoIcon to TodoIconTypes and declaring
object-shaped inferred types such as Todo and TodoTag as interfaces extending
their corresponding Zod-inferred schemas; preserve existing exports and usages.
At
apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_types/today-type.ts:17-18,
convert the object types TodayTodo and TodayData from type aliases to interfaces
without changing their shapes.
Source: Path instructions
| export const useTodayTodoList = (initialTodos: TodayTodo[]) => { | ||
| const [todos, setTodos] = useState<TodayTodo[]>(initialTodos); |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value
서버 상태와 로컬 상태 동기화 관련 안내 (단일 출처 원칙)
가이드라인(경로 지침)에 따르면 서버 상태(API 데이터)는 React Query가 단일 출처(Single Source of Truth)가 되어야 합니다. 현재 PR 설명에 언급된 대로 체크/재생/삭제 기능의 빠른 시각적 피드백을 위해 임시로 useState를 활용하신 것으로 파악됩니다.
추후 백엔드 Mutation API 연동이 완료되면, 해당 useState를 걷어내고 React Query의 캐시 데이터를 직접 조작(Optimistic Update)하여 단일 출처 원칙을 복구하시기를 추천합니다! 👍
🤖 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)/(with-time-sidebar)/today/_hooks/useTodayTodoList.ts
around lines 7 - 8, When the backend mutation APIs are integrated, remove the
local todos state and useState-based updates from useTodayTodoList, making the
React Query cache the single source of truth. Implement check, replay, and
delete optimistic updates by updating the relevant query cache directly while
preserving immediate UI feedback.
Source: Path instructions
- formatDate, convertDurationToTimeText import 경로를 재구성된 utils 폴더 구조에 맞게 수정했습니다
Timo Performance ReportBundle Size — timo-web
Lighthouse — timo-web
Image Optimization — timo-web
측정 커밋: |
- api/todo/todo-schema를 api/common/todo-schema로 이동한 develop 변경에 맞게 import 경로를 수정했습니다 - today 도메인 CreateTodoModalContainer를 공유 위치로 이동한 develop 변경을 수락했습니다
- 로컬 state 방식 대신 useCreateTodo 뮤테이션으로 실제 API를 호출했습니다 - 생성 성공 시 today 쿼리를 invalidate해 서버 데이터를 반영했습니다 - 서버 refetch 후 로컬 state 동기화를 위해 useEffect를 추가했습니다 - handleAddTodo 제거 및 onCreateTodo prop 의존성을 제거했습니다 - todo.icon 값에 따라 IconGraphic을 렌더링했습니다
- sub-todo 리스트의 pl-8을 제거해 상위 투두와 체크박스 시작 위치를 맞췄습니다
- 투두 완료 체크 시 completed 항목이 목록 하단으로 정렬되도록 수정했습니다
- 투두 카드를 DetailTodoModalContainer로 감싸 클릭 시 상세 모달이 열리도록 연결했습니다
- handleCheck에서 updated 변수를 분리하면서 timerStatus 리터럴이 string으로 추론되던 문제를 as const 및 명시적 타입 주석으로 해결했습니다
ISSUE 🔗
close #180
What is this PR? 🔍
`/api/v1/home/today` 엔드포인트를 today 탭에 연동하고, 투두 생성 API를 연결하며 아이콘 표시와 하위 투두 체크박스 정렬을 구현했습니다. 추가로 완료 투두 하단 정렬과 카드 클릭 시 상세 모달 진입을 구현했습니다.
배경
공유 스키마 이동 (`api/common/todo-schema.ts`)
today 탭 조회 API 연동
투두 생성 API 연동
today 투두 아이콘 표시
하위 투두 체크박스 정렬 수정
`에서 `pl-8` 제거.
완료 투두 하단 정렬
today 투두 카드 클릭 시 상세 모달 연결
To Reviewers
`todoSchema`를 `api/common/todo-schema.ts`로 이동하면서 기존 `home/_types/todo-type.ts`를 re-export 배럴로만 남겼는데, 추후 home 내부 파일들도 직접 `api/common/todo-schema.ts`를 바라보도록 정리가 필요합니다.
today 탭의 체크·재생·삭제 뮤테이션은 현재 로컬 state만 변경하고 있어 새로고침 시 원상복구됩니다. 뮤테이션 API 연동 PR에서 `// TODO: API` 주석 위치에 붙이면 됩니다.
`useTodayTodoList`의 `useEffect` sync는 서버 refetch 후 로컬 check/play 상태를 초기화합니다. 현재는 check·play가 로컬 only여서 영향이 없지만, 뮤테이션 API 연동 시점에 optimistic update 전략과 함께 재검토가 필요합니다.
Screenshot 📷
Test Checklist ✔