Skip to content

[FEAT] 홈 화면 TODO 조회·생성·완료·순서 변경 API 및 AI 소요 시간 추천 연동 - #160

Merged
kimminna merged 13 commits into
developfrom
feat/web/159-integrate-home-view-api
Jul 13, 2026
Merged

[FEAT] 홈 화면 TODO 조회·생성·완료·순서 변경 API 및 AI 소요 시간 추천 연동#160
kimminna merged 13 commits into
developfrom
feat/web/159-integrate-home-view-api

Conversation

@kimminna

@kimminna kimminna commented Jul 12, 2026

Copy link
Copy Markdown
Member

ISSUE 🔗

close #159



What is this PR? 🔍

홈 화면의 일별 TODO 목록을 mock 데이터 대신 실제 백엔드 API로 조회/생성/완료 처리하도록 연동하고, 그 과정에서 발견한 날짜 범위·스크롤 버그, null 파싱 오류, 드롭다운/모달 레이스 컨디션을 함께 수정했습니다. 추가로 TODO 생성 시 AI 예상 소요 시간 추천 기능을 연동했습니다.

배경

  • 기존 구조: HomeTodoContainergetHomeViewMock으로 로컬에서 생성한 가짜 날짜/TODO 데이터를 렌더링하고, TODO 생성·완료·순서 변경도 로컬 상태에만 반영하고 있었습니다.
  • 발생 문제: 실제 백엔드 응답 스펙(home.zod.ts)을 확인해보니 icon, durationSeconds, priority, tag, sortOrder가 모두 optional이고 timerStatusPAUSED가 추가돼 있어, 기존 로컬 zod 스키마(모두 required, PAUSED 없음)로는 실제 응답이 들어오는 즉시 .parse()가 깨질 상황이었습니다. 실제로 연동 후 icon/priority/tag 값이 없는 TODO에서 백엔드가 필드 자체를 생략하는 대신 null을 명시적으로 내려주고 있어, .optional()만으로는 막지 못하는 zod 파싱 에러가 실제로 발생했습니다. 또한 API 연동 후 기본 뷰에서 오늘 기준 이전 7일이 보이지 않고 오늘부터 미래만 계속 이어지는 것처럼 보이는 버그가 있었습니다.
  • 해결 방향: useSuspenseQuery 기반의 useHomeView 훅과 useCreateTodo/useChangeTodoStatus/useChangeSubtaskStatus/useRecommendDuration 뮤테이션으로 실제 API를 연동하고, 로컬 zod 스키마를 백엔드가 실제로 내려주는 값(명시적 null 포함)에 맞게 갱신했습니다. 날짜 정렬 로직도 원인을 찾아 제거했습니다.

홈 화면 데이터 연동

  • 변경 요약: _queries/use-home-view.tsuseHomeView 훅을 추가하고, HomeTodoContainer에서 getHomeViewMock 호출을 이 훅으로 교체했습니다.
  • 이유: 컨테이너가 page.tsx에서 AsyncBoundary(Suspense)로 감싸져 있어 useSuspenseQuery를 써야 했습니다.
  • 구현 방식: orval이 생성하는 useGetHome은 내부적으로 일반 useQuery용 타입(UseQueryOptions)으로 캐스팅되어 있어 skipToken을 허용하는데, 이 옵션 객체를 useSuspenseQuery에 그대로 스프레드하면 UseSuspenseQueryOptions와 타입이 충돌합니다. 그래서 생성된 getGetHomeQueryKey + getHome fetcher만 가져와 queryKey/queryFn을 직접 조립하고, select에서 BaseResponse.data를 언랩한 뒤 로컬 zod 스키마(homeViewDataSchema)로 .parse()해 응답을 검증합니다. 전역 queryClient(api/client/query-client.ts)에 staleTime: 5분이 기본값으로 설정돼 있어, 기본/주간 뷰 토글로 이전에 방문한 filter+date 조합에 5분 내 다시 진입하면 캐시만 쓰고 재요청을 안 하는 문제가 있어 이 쿼리에는 staleTime: 0을 별도로 지정했습니다.
  • 경계 · 제약: api/generated/ 내부 파일은 수정하지 않았습니다.

날짜 범위·오늘 스크롤 버그 수정

  • 변경 요약: 기본 뷰(가로 스크롤)에서 오늘을 배열 맨 앞으로 재정렬하던 reorderDaysTodayFirst(_utils/home-view.ts)를 제거하고, "오늘로 스크롤"하는 로직을 브라우저 네이티브 scrollIntoView로 교체했습니다.
  • 이유: reorderDaysTodayFirst가 오늘 이전 7일을 배열 끝(미래 7일보다 뒤)으로 옮기고 있어서, 화면상으로는 오늘부터 미래만 계속 이어지는 것처럼 보이고 과거 7일은 스크롤을 끝까지 넘겨야만 나타났습니다. 이후 "오늘" 버튼을 눌렀을 때도 하루 밀려서 스크롤되는 문제가 있었는데, 원인은 스크롤 위치 계산에 쓰던 element.offsetLeft가 스크롤 컨테이너가 아니라 가장 가까운 positioned 조상(사실상 <body>) 기준으로 계산되고 있어서였습니다. 사이드바 열림/닫힘에 따라 <main>margin-left(ml-55ml-5)가 바뀌는데 이 차이만큼 값이 오염되어 스크롤 목표 지점이 어긋났습니다.
  • 구현 방식: days 배열은 API가 내려주는 과거→오늘→미래 순서를 그대로 사용합니다. 각 날짜 카드에 data-today 속성을 추가하고, scrollContainerToToday에서 container.querySelector('[data-today="true"]')로 오늘 카드를 찾아 todayElement.scrollIntoView({ inline: "start" })로 정렬합니다. scrollIntoView는 조상 요소의 margin/position과 무관하게 브라우저가 직접 정렬 위치를 계산하므로, 이전에 시도했던 offsetLeftgetBoundingClientRect 기반 수동 계산보다 안전합니다.
  • 경계 · 제약: 사이드바 레이아웃(MainShellContainer.tsx)의 margin 구조 자체는 변경하지 않았습니다.

Todo 로컬 타입 · null 파싱 오류 수정

  • 변경 요약: _types/todo-type.tstodoSchema를 실제 백엔드 응답 스펙에 맞게 갱신했습니다 — icon/priority/tag는 백엔드가 값이 없을 때 필드를 생략하는 대신 명시적으로 null을 내려주는 것을 확인해 .nullish().transform(...)으로 null도 허용하도록 바꿨고(priority"MEDIUM", icon/tagundefined로 정규화), durationSeconds/sortOrder는 optional + 기본값, timerStatus enum에 PAUSED를 추가했습니다. TODO 생성 응답 검증용 todoCreateResponseSchema도 추가했습니다.
  • 이유: .optional()만으로는 undefined만 허용하고 null은 걸러내지 못해, 실제로 icon/priority/tag가 없는 TODO를 조회할 때마다 useHomeViewhomeViewDataSchema.parse()ZodError를 던지며 화면이 통째로 에러 바운더리로 빠지는 문제가 실제로 재현됐습니다.
  • 구현 방식: durationSeconds, sortOrder.default(0)을 적용해 값이 없어도 Todo 타입 자체는 이전처럼 non-optional하게 유지했습니다. priority/icon/tagnull/undefined를 모두 허용(.nullish())한 뒤 .transform()으로 UI가 기대하는 형태(priority는 기본값 "MEDIUM", icon/tagundefined)로 정규화해, Todo 타입의 출력 형태는 기존과 동일하게 유지했습니다. todoCreateResponseSchema{ todoId: number }만 검증하는 최소 스키마입니다.
  • 경계 · 제약: iconundefined가 되면서 폼의 null 값을 undefined로 변환하는 코드가 use-create-todo-submit.ts에 함께 필요해 같이 수정했습니다(icon: data.icon ?? undefined).

TODO 생성 API 연동

  • 변경 요약: use-create-todo-submit.ts에서 mock으로 로컬에 즉시 반영하던 TODO 생성 로직을 실제 useCreateTodo 뮤테이션(POST /api/v1/todos) 호출로 교체했습니다.
  • 이유: 기존에는 Date.now()를 todoId로 쓰는 가짜 객체를 만들어 화면에만 반영하고 서버에는 아무 것도 저장하지 않았습니다.
  • 구현 방식: 로컬 CreateTodoRequest(nullable 필드)를 generated TodoCreateRequest(optional 필드)로 변환하는 매퍼를 추가하고(icon: data.icon ?? undefined 등), 뮤테이션 성공 시 응답을 todoCreateResponseSchema.parse()한 뒤 홈 뷰 쿼리(getGetHomeQueryKey())를 invalidateQueries로 무효화해 서버 최신 데이터를 다시 조회하도록 했습니다. 이전에는 로컬에서 Todo 객체를 수동으로 조립해 todosByDate에 낙관적으로만 반영했는데, React Query가 서버 상태의 단일 출처가 되도록 이 방식으로 교체하면서 더 이상 필요 없어진 onCreate/CreateTodoTag 전달 체인(CreateTodoModalContainerHomeDayHeaderContainerHomeTodoContainer)을 함께 정리했습니다. 실패 시(네트워크 에러 또는 응답 형태 불일치) CreateTodoModalContainer에 추가한 AnimatedToast로 에러를 알립니다. 메시지 키 Toast.todoCreateFailed를 ko/en 메시지 파일에 추가했으며, 온보딩 실패 토스트(onboardingSubmitFailed)와 동일한 패턴을 따랐습니다.
  • 경계 · 제약: 타이머 토글은 이번 PR 범위 밖이며 use-home-todos-by-date.ts// TODO: API로 남아 있습니다.

투두 및 하위 투두 완료 상태 변경 API 연동

  • 변경 요약: use-home-todos-by-date.tshandleToggleCompleted/handleToggleSubtaskCompleted에서 로컬 상태만 바꾸던 로직을 실제 useChangeTodoStatus(PATCH /api/v1/todos/{todoId}/status)/useChangeSubtaskStatus(PATCH /api/v1/todos/{todoId}/subtasks/{subtaskId}/status) 뮤테이션 호출로 교체했습니다.
  • 이유: 체크박스를 눌러도 서버에는 아무 것도 저장되지 않고 새로고침하면 원래 상태로 돌아가는 상태였습니다.
  • 구현 방식: 체크박스를 누르면 즉시 로컬 상태를 낙관적으로 반영해 UI가 바로 반응하도록 하고, 뮤테이션 성공 시에는 응답 필드(completed/sortOrder)를 직접 반영하는 대신 홈 뷰 쿼리를 invalidateQueries로 무효화해 서버가 재계산한 완료 그룹 정렬 순서까지 정확히 반영되도록 했습니다. 실패 시에는 토글 직전 상태로 롤백합니다.
  • 경계 · 제약: 타이머가 실행/일시정지 중인 TODO는 완료 상태를 변경할 수 없다는 백엔드 제약이 있는데, 이 케이스에 대한 별도 에러 토스트 등 UX 처리는 이번 PR에 포함하지 않았습니다(뮤테이션 실패 시 낙관적 업데이트만 롤백됩니다).

AI 소요 시간 추천 API 연동

  • 변경 요약: TODO 생성 모달의 시간 선택 드롭다운(TimeSelector)을 열면 현재 입력된 제목/태그를 기준으로 useRecommendDuration(POST /api/v1/ai/duration) API를 호출해 추천 소요 시간을 duration 입력값에 자동 반영하도록 연동했습니다.
  • 이유: 사용자가 직접 시간을 어림잡아 입력하는 대신, 과거 타이머 기록 기반 AI 추천을 받아 빠르게 채울 수 있도록 하기 위함입니다.
  • 구현 방식: Dropdown(디자인 시스템 공통 컴포넌트)에 onOpenChange 콜백을 추가하고, TimeSelectorTodoToolbarCreateTodoModalContent까지 onOpen/onTimeOpen prop으로 연결해 드롭다운이 열리는 시점을 훅으로 노출했습니다. use-time-field.tshandleTimeSelectorOpen에서 제목이 비어있지 않고 이미 요청 중이 아닐 때만 추천 API를 호출하고, 응답의 recommendedMinutes(분)를 HH:MM 문자열로 변환해 duration 필드에 반영하며 AI 아이콘을 선택 상태로 표시합니다. 실패 시 Toast.aiDurationRecommendFailed 토스트로 안내합니다. 또한 duration 수동 입력 마스크(formatDurationInput)를 숫자와 콜론만 허용(시 자릿수 제한 없음, 분은 2자리)하도록 다듬고, 검증 정규식(todo-schema.tsDURATION_PATTERN)도 동일한 형태(\d{1,3}:[0-5]\d)로 맞췄습니다.
  • 경계 · 제약: AI 추천 API는 RPM/RPD/TPM 레이트 리밋을 초과하면 429를 반환할 수 있는데(백엔드 문서 기준), 이 케이스도 공통 에러 토스트로만 처리되고 재시도 대기시간 안내 등의 세분화된 UX는 없습니다.

투두 순서 변경 API 연동

  • 변경 요약: use-home-todos-by-date.tshandleReorderTodo에서 mock(patchTodoOrderMock)으로만 반영하던 드래그 앤 드롭 순서 변경을 실제 useReorderTodo(PATCH /api/v1/todos/{todoId}/order) 뮤테이션 호출로 교체했습니다.
  • 이유: 드래그로 순서를 바꿔도 서버에는 저장되지 않아 새로고침하면 원래 순서로 돌아가는 상태였습니다.
  • 구현 방식: 기존과 동일하게 드래그가 끝나면 즉시 로컬 배열을 낙관적으로 재정렬해 반응성을 유지하고, todoId/newIndex/date(대상 날짜)를 실어 뮤테이션을 호출합니다. 성공 시에는 다른 완료 상태 변경 mutation들과 같은 패턴으로 홈 뷰 쿼리를 invalidateQueries로 무효화해 서버가 재계산한 정렬 순서를 다시 조회하고, 실패 시 드래그 이전 순서로 롤백합니다. 더 이상 쓰이지 않는 _mocks/todo-order-mock.ts는 삭제했습니다.
  • 경계 · 제약: 완료된 TODO는 순서를 변경할 수 없다는 기존 프런트 제약(reorderTodos 유틸)은 그대로 유지했고, 완료/미완료 그룹 간 이동은 여전히 완료 상태 변경 API가 담당합니다.

드롭다운 바깥 클릭 시 모달 닫힘 레이스 컨디션 수정

  • 변경 요약: OverlayModal에서 드롭다운(우선순위/태그/시간/반복 선택 등)이 열려 있는 상태에서 모달 배경을 클릭해 드롭다운을 닫으려 하면, 모달까지 함께 닫혀버리던 버그를 수정했습니다.
  • 이유: 드롭다운의 바깥 클릭 감지는 mousedown 시점에 자기 자신을 닫고 공용 플로팅 레이어 카운트(hasOpenFloatingLayer)를 즉시 감소시키는데, OverlayModal의 배경 닫기 판단은 그보다 늦게 발생하는 click 시점에 이 카운트를 확인하고 있었습니다. 같은 한 번의 클릭 안에서 mousedownclick보다 항상 먼저 발생하기 때문에, 모달이 카운트를 확인하는 시점에는 이미 드롭다운이 스스로 카운트를 감소시킨 뒤라 "열린 플로팅 레이어 없음"으로 잘못 판단해 모달까지 닫아버렸습니다.
  • 구현 방식: OverlayModal의 배경 요소에 onMouseDownCapture를 추가해, 캡처 단계(버블 단계보다 항상 먼저 실행되는 것이 DOM 스펙상 보장됨)에서 "이 클릭이 시작되는 시점에 플로팅 레이어가 열려 있었는지"를 ref에 스냅샷해두고, 이후 onClick에서는 그 스냅샷 값을 사용해 닫을지 판단하도록 했습니다.
  • 경계 · 제약: Escape 키로 닫는 로직은 단일 keydown 이벤트만 관여해 동일한 레이스 컨디션이 없어 변경하지 않았습니다.



To Reviewers

priority 기본값을 "MEDIUM"으로 정한 건 기존 use-create-todo-submit.ts에 이미 있던 관례를 따른 것인데, 실제로 백엔드가 우선순위 없는 TODO를 의도적으로 반환하는 케이스가 있다면 이 기본값이 맞는지 확인 부탁드립니다.

icon/priority/tag의 null 파싱 오류와 드롭다운/모달 레이스 컨디션은 실제 개발 중 브라우저에서 재현된 에러 로그(ZodError, 모달이 같이 닫히는 현상)를 보고 원인을 찾아 수정한 것이라 실제 케이스 기반이지만, 완료 상태 변경 API·순서 변경 API·AI 추천 API는 백엔드 CORS 설정이 아직 이 엔드포인트들(PATCH .../status, PATCH .../order, POST /api/v1/ai/duration)에 열려 있지 않아 로컬에서 실제 응답까지 끝까지 확인하지는 못했습니다. 백엔드 CORS 설정이 열리는 대로 실제 완료 처리/순서 변경/AI 추천 흐름을 한 번 더 확인해주시면 좋겠습니다.

날짜 범위/스크롤 버그는 로컬 Playwright 브라우저 확장 연결 문제로 실제 화면에서 직접 재현·확인하지 못하고 코드 레벨 분석(offsetLeft가 positioned 조상 기준이라는 점)만으로 수정했습니다. 사이드바를 열고 닫으며 오늘 카드 정렬이 정상인지, "오늘" 버튼을 눌렀을 때 정확히 오늘로 이동하는지 리뷰어분이나 QA 단계에서 한 번 더 확인해주시면 좋겠습니다.



Screenshot 📷

Animation



Test Checklist ✔

  • pnpm tsc --noEmit 통과
  • pnpm eslint 통과 (변경 파일 기준)
  • 브라우저에서 /home 페이지 실동작 확인 — 미실행: 로컬 Playwright 브라우저 연결 문제로 미완료
  • 사이드바 열림/닫힘 상태에서 오늘 카드 정렬 확인 — 미실행: 위와 동일한 사유
  • TODO 생성 실제 화면 동작 확인 — 미실행: 위와 동일한 사유
  • TODO 완료/하위 투두 완료 상태 변경 실제 화면 동작 확인 — 미실행: 백엔드 CORS 미설정으로 확인 불가
  • AI 소요 시간 추천 실제 화면 동작 확인 — 미실행: 백엔드 CORS 미설정으로 확인 불가
  • 드롭다운 연 상태에서 모달 배경 클릭 시 드롭다운만 닫히는지 확인 — 미실행: 브라우저 연결 문제로 미완료
  • 드래그 앤 드롭으로 순서 변경 후 새로고침해도 순서 유지되는지 확인 — 미실행: 백엔드 CORS 미설정으로 확인 불가
  • 타이머 토글 mutation 연동 — 후속 작업

- useSuspenseQuery 기반 useHomeView 훅을 추가하고 mock 대신 실제 API 응답을 사용하도록 홈 화면 컨테이너를 교체했습니다
- Todo 로컬 zod 스키마를 실제 백엔드 응답 스펙에 맞춰 optional 필드를 반영하고 timerStatus에 PAUSED를 추가했습니다
- optional해진 tag 필드에 맞춰 컨테이너 렌더링과 TODO 생성 시 icon null 처리를 수정했습니다
@vercel

vercel Bot commented Jul 12, 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 13, 2026 10:59am

Request Review

@github-actions
github-actions Bot requested a review from ehye1 July 12, 2026 17:55
@github-actions github-actions Bot added the ⏰ Timo-web Timo 웹 서비스 label Jul 12, 2026
@github-actions
github-actions Bot requested a review from jjangminii July 12, 2026 17:55
@github-actions github-actions Bot added ✨ Feature 새로운 기능(기능성) 구현 ♦️ 민아 민아상 labels Jul 12, 2026
@coderabbitai

coderabbitai Bot commented Jul 12, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

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

Next review available in: 44 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: ASSERTIVE

Plan: Pro Plus

Run ID: f9c6328c-815e-4916-b278-3abbb0b89344

📥 Commits

Reviewing files that changed from the base of the PR and between 001b574 and de08d88.

📒 Files selected for processing (3)
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_hooks/use-home-todos-by-date.ts
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_mocks/todo-order-mock.ts
  • apps/timo-web/components/modal/OverlayModal.tsx

Walkthrough

홈 화면의 Todo 데이터를 실제 API 조회 결과로 전환하고, Todo 생성·완료·시간 추천을 API 기반 흐름으로 변경했습니다. 오늘 카드 스크롤, 선택 필드 정규화, 실패 토스트 및 오버레이 닫힘 제어도 갱신했습니다.

Changes

홈 화면 및 Todo 흐름

Layer / File(s) Summary
Todo 스키마와 홈 조회 계약
apps/timo-web/.../home/_types/todo-type.ts, apps/timo-web/.../home/_queries/use-home-view.ts, apps/timo-web/api/todo/todo-schema.ts
Todo 선택 필드와 PAUSED 상태를 반영하고, 검증된 홈 조회 및 시간 추천 응답 스키마를 추가합니다.
홈 화면 데이터와 오늘 위치 렌더링
apps/timo-web/.../home/_containers/HomeTodoContainer.tsx, apps/timo-web/.../home/_hooks/use-home-today-scroll.ts, apps/timo-web/.../home/_containers/todo-card/*
mock days 대신 API days를 사용하고 오늘 표시 속성, 오늘 카드 기준 스크롤, tag 없는 Todo 처리 및 생성 콜백 제거를 적용합니다.
Todo 생성 요청과 실패 토스트
apps/timo-web/.../home/_hooks/todo-modal/use-create-todo-submit.ts, apps/timo-web/.../home/_containers/todo-modal/*, apps/timo-web/messages/*.json
생성 요청을 정규화·검증하고 홈 쿼리를 갱신하며, 생성 및 AI 추천 실패를 다국어 토스트로 표시합니다.
Todo 상태 동기화
apps/timo-web/.../home/_hooks/use-home-todos-by-date.ts
Todo와 서브태스크 완료 상태를 낙관적으로 갱신하고 API 실패 시 이전 상태로 복구합니다.
AI 시간 추천 연결
apps/timo-web/.../home/_hooks/todo-modal/use-time-field.ts, packages/timo-design-system/src/components/{layout/dropdown,time/time-selector,todo/todo-toolbar}/*
시간 선택기 오픈 이벤트를 연결하고 AI 추천 시간을 요청·포맷·적용합니다.

오버레이 상호작용

Layer / File(s) Summary
오버레이 닫힘 상태 보존
apps/timo-web/components/modal/OverlayModal.tsx
마우스 다운 시 floating layer 상태를 저장해 클릭 시 오버레이 닫힘 여부를 결정합니다.

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

Sequence Diagram(s)

sequenceDiagram
  participant CreateTodoModalContent
  participant TimeSelector
  participant useTimeField
  participant RecommendDurationAPI
  CreateTodoModalContent->>TimeSelector: 시간 선택기 오픈 콜백 연결
  TimeSelector->>useTimeField: 오픈 이벤트 전달
  useTimeField->>RecommendDurationAPI: 제목·태그 기반 추천 시간 요청
  RecommendDurationAPI-->>useTimeField: 추천 분 반환
  useTimeField-->>CreateTodoModalContent: HH:MM 시간 또는 오류 토스트
Loading

Possibly related PRs

Suggested labels: 🐛 Bug

Suggested reviewers: yumin-kim2, jjangminii, ehye1

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning 이슈 #159 범위를 넘어 TODO 생성·완료·AI 추천·모달 레이스 수정 등 별도 기능이 함께 포함되었습니다. 조회 API 연동만 남기고 생성/완료/AI/레이스 수정은 별도 PR로 분리하거나 이슈 범위를 갱신하세요.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed 직접 이슈 #159의 핵심 요구사항인 useHomeView 추가, HomeTodoContainer 교체, 스키마 반영, 태그 처리, 검증 확인을 충족합니다.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Title check ✅ Passed 홈 화면 TODO API 연동과 AI 소요 시간 추천이라는 핵심 변경을 잘 요약한 제목입니다.
Description check ✅ Passed 설명이 변경 내용과 일치하며, API 연동·버그 수정·AI 추천까지 관련성이 높습니다.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/web/159-integrate-home-view-api

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 12, 2026

Copy link
Copy Markdown

Timo Performance Report

Bundle Size — timo-web
라우트 크기 First Load JS
/[locale]/home 197.64 kB 🔴 403.47 kB
/[locale]/today 153.80 kB 🔴 359.64 kB
/[locale]/focus 150.86 kB 🔴 356.69 kB
/[locale]/settings/account 0 B 🟡 205.84 kB
/[locale]/settings 160.87 kB 🔴 366.71 kB
/[locale]/statistics 149.33 kB 🔴 355.16 kB
/[locale]/[...rest] 0 B 🟡 205.84 kB
/[locale]/login 116.90 kB 🟡 322.74 kB
/[locale]/oauth/callback 116.18 kB 🟡 322.01 kB
/[locale]/onboarding 228.22 kB 🔴 434.06 kB
/[locale] 0 B 🟡 205.84 kB

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

Lighthouse — timo-web
URL Perf A11y LCP CLS TBT
/en/home 🔴 68 🟢 95 🔴 15.5s 🟢 0.000 🟡 301ms
/en/today 🔴 60 🟢 95 🔴 15.2s 🟢 0.000 🟡 599ms
/en/focus 🟡 70 🟢 95 🔴 14.9s 🟢 0.000 🟡 229ms
/en/statistics 🔴 62 🟢 95 🔴 15.1s 🟢 0.000 🟡 513ms

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

Image Optimization — timo-web
파일 크기 포맷 상태
images/google-calendar.png 36.20 kB PNG ⚠️ 🟢
images/google-logo.png 26.79 kB PNG ⚠️ 🟢

총 2개 · 63.00 kB  |  🟢 < 200KB  |  🟡 < 500KB  |  🔴 ≥ 500KB
⚠️ 2개 파일 WebP/AVIF 변환 권장

측정 커밋: 472f3b6

- 오늘을 배열 맨 앞으로 재정렬하던 reorderDaysTodayFirst를 제거해 과거 7일~미래 7일이 정상 순서로 보이도록 했습니다
- 오늘 카드로 스크롤하는 로직을 scrollIntoView(inline: "start") 기반으로 바꿔 오늘이 정확히 좌측 기준으로 오도록 했습니다
- mock으로 로컬에서 TODO를 생성하던 로직을 실제 POST /api/v1/todos 뮤테이션 호출로 교체했습니다
- 응답을 로컬 zod 스키마로 검증해 실제 서버 todoId를 사용하도록 했습니다
- 생성 실패 시 에러 토스트를 노출하도록 했습니다
@kimminna kimminna changed the title [FEAT] 홈 화면 조회 API 연동 [FEAT] 홈 화면 조회/생성 API 연동 Jul 12, 2026

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

🤖 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)/(with-time-sidebar)/home/_containers/HomeTodoContainer.tsx:
- Around line 53-57: Update the useEffect in HomeTodoContainer that calls
scrollContainerToToday so its dependency array includes baseDate, ensuring the
scroll position is recalculated when the displayed date range changes; preserve
the existing isWeekView and scrollRef dependencies.

In
`@apps/timo-web/app/`[locale]/(main)/(with-time-sidebar)/home/_hooks/todo-modal/use-create-todo-submit.ts:
- Around line 60-88: Update handleSubmit’s onError callback and the unsuccessful
safeParse branch in useCreateTodoSubmit to log the API error or
schema-validation details before opening the error toast. Preserve the existing
setIsErrorToastOpen behavior and successful onCreate flow.
🪄 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: 646ceb8f-e722-44bd-b694-3e22f2085987

📥 Commits

Reviewing files that changed from the base of the PR and between e98c0da and f2a6109.

📒 Files selected for processing (8)
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_containers/HomeTodoContainer.tsx
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_containers/todo-modal/CreateTodoModalContainer.tsx
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_hooks/todo-modal/use-create-todo-submit.ts
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_hooks/use-home-today-scroll.ts
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_types/todo-type.ts
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_utils/home-view.ts
  • apps/timo-web/messages/en.json
  • apps/timo-web/messages/ko.json
💤 Files with no reviewable changes (1)
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_utils/home-view.ts

kimminna added 7 commits July 13, 2026 16:09
- 백엔드가 priority/tag를 값이 없을 때 필드 생략이 아닌 명시적 null로 내려줘서 zod parse가 깨지던 문제를 수정했습니다
- .optional()을 .nullish().transform(...)으로 바꿔 null도 허용하고 각각 "MEDIUM"/undefined로 정규화했습니다
- 백엔드가 icon 값이 없을 때 필드 생략이 아닌 명시적 null로 내려줘서 zod parse가 깨지던 문제를 수정했습니다
- icon 필드를 .optional()에서 .nullish().transform(...)으로 바꿔 null도 허용하고 undefined로 정규화했습니다
- 투두/하위 투두 체크박스 토글 시 실제 완료 상태 변경 API를 호출하도록 연동했습니다
- 낙관적 업데이트로 즉시 UI에 반영하고, 실패 시 이전 상태로 롤백하도록 했습니다
- 성공 시에는 응답 필드를 직접 반영하는 대신 홈 뷰 쿼리를 무효화해 서버 최신 데이터로 다시 조회하도록 했습니다
- 로컬 상태에 Todo 객체를 수동으로 조립해 넣던 방식을 홈 뷰 쿼리 invalidate로 교체해 서버 상태 중복 관리를 없앴습니다
- 더 이상 쓰이지 않는 onCreate/CreateTodoTag 전달 체인을 컨테이너에서 정리했습니다
- 시간 선택 드롭다운을 열면 현재 제목/태그 기준으로 AI 예상 소요 시간 추천 API를 호출하도록 연동했습니다
- 추천 결과를 duration 입력값에 자동 반영하고, 실패 시 토스트로 안내합니다
- duration 수동 입력은 숫자와 콜론만 허용하도록 마스킹을 추가했습니다 (시 자릿수는 제한 없음, 분은 2자리)
- Dropdown 컴포넌트에 열림 이벤트(onOpenChange)를 추가해 TimeSelector/TodoToolbar를 통해 상위로 전달했습니다
- 드롭다운의 바깥 클릭 감지(mousedown)가 모달의 배경 클릭 닫기 판단(click)보다 먼저 실행되면서, 플로팅 레이어가 이미 닫힌 상태로 판단돼 모달까지 함께 닫히던 문제를 수정했습니다
- 배경 클릭 시작 시점(mousedown 캡처 단계)에 플로팅 레이어 열림 여부를 미리 스냅샷해두고, 이후 click 시점에는 그 값을 사용하도록 했습니다
- 전역 staleTime(5분) 설정 때문에 기본/주간 뷰를 토글해 이전에 방문했던 조합으로 돌아가면 캐시만 쓰고 재요청을 안 하던 문제를 수정했습니다
- 홈 뷰 쿼리에 staleTime: 0을 지정해 토글 시 항상 최신 데이터를 다시 요청하도록 했습니다
@kimminna
kimminna requested a review from yumin-kim2 as a code owner July 13, 2026 08:57
@github-actions github-actions Bot added the ⌚ Timo-Design-system Timo 디자인 시스템 label Jul 13, 2026
@github-actions

github-actions Bot commented Jul 13, 2026

Copy link
Copy Markdown

Storybook Preview

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

마지막 업데이트: 2026-07-13 10:58 UTC

@kimminna kimminna changed the title [FEAT] 홈 화면 조회/생성 API 연동 [FEAT] 홈 화면 TODO 조회·생성·완료 API 및 AI 소요 시간 추천 연동 Jul 13, 2026

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

🤖 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 68-70: Update recommendDurationResponseSchema so
recommendedMinutes uses Zod’s integer validation rather than accepting arbitrary
numbers; retain the existing numeric schema behavior while rejecting non-integer
values before formatMinutesToDuration consumes them.

In
`@apps/timo-web/app/`[locale]/(main)/(with-time-sidebar)/home/_hooks/todo-modal/use-time-field.ts:
- Around line 64-97: Update applyRecommendedDuration so AI recommendations are
only stored in recommendedDuration and reflected in the selector display,
without calling field.onChange during handleTimeSelectorOpen. Keep the existing
handleSelectTime "ai" branch responsible for applying the stored recommendation
to the form only after the user explicitly selects the AI option.

In
`@apps/timo-web/app/`[locale]/(main)/(with-time-sidebar)/home/_hooks/use-home-todos-by-date.ts:
- Around line 44-64: Update handleToggleCompleted in
apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_hooks/use-home-todos-by-date.ts#L44-L64
to snapshot only the targeted todo identified by todoId and restore that item
via updateTodo in onError, preserving concurrent changes to other todos. Apply
the same item-level snapshot and rollback in handleToggleSubtaskCompleted at
apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_hooks/use-home-todos-by-date.ts#L89-L105
for the targeted subtask.

In `@apps/timo-web/components/modal/OverlayModal.tsx`:
- Line 32: Update the OverlayModal interaction flow to capture pointer input via
onPointerDownCapture instead of onMouseDownCapture, so mouse, touch, and pen
inputs share the snapshot path. In the onClick handler, read
wasFloatingLayerOpenRef.current for the current interaction, then immediately
reset it to false before continuing.

In `@packages/timo-design-system/src/components/layout/dropdown/Dropdown.tsx`:
- Around line 81-85: Update the Dropdown component’s isOpen synchronization so
onOpenChange is invoked from a useEffect whenever isOpen changes, covering
toggle(), outside-click, Escape-key, and close() paths. Remove the direct
callback from toggle() to avoid duplicate notifications while preserving the
existing state transitions.
🪄 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: 6ecef49a-13d0-4cd1-ba93-38980cd117ce

📥 Commits

Reviewing files that changed from the base of the PR and between f2a6109 and 001b574.

📒 Files selected for processing (16)
  • apps/timo-web/api/todo/todo-schema.ts
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_containers/HomeTodoContainer.tsx
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_containers/todo-card/HomeDayHeaderContainer.tsx
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_containers/todo-modal/CreateTodoModalContainer.tsx
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_containers/todo-modal/CreateTodoModalContent.tsx
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_hooks/todo-modal/use-create-todo-submit.ts
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_hooks/todo-modal/use-time-field.ts
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_hooks/use-home-todos-by-date.ts
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_queries/use-home-view.ts
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_types/todo-type.ts
  • apps/timo-web/components/modal/OverlayModal.tsx
  • apps/timo-web/messages/en.json
  • apps/timo-web/messages/ko.json
  • packages/timo-design-system/src/components/layout/dropdown/Dropdown.tsx
  • packages/timo-design-system/src/components/time/time-selector/TimeSelector.tsx
  • packages/timo-design-system/src/components/todo/todo-toolbar/TodoToolbar.tsx
💤 Files with no reviewable changes (1)
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_containers/HomeTodoContainer.tsx

Comment thread apps/timo-web/api/todo/todo-schema.ts
const [shouldRender, setShouldRender] = useState(isOpen);
const [isVisible, setIsVisible] = useState(false);
const dialogRef = useRef<HTMLDivElement>(null);
const wasFloatingLayerOpenRef = useRef(false);

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Map the file first, then inspect the relevant slice.
ast-grep outline apps/timo-web/components/modal/OverlayModal.tsx --view expanded

echo
echo '--- file with line numbers ---'
cat -n apps/timo-web/components/modal/OverlayModal.tsx | sed -n '1,220p'

echo
echo '--- search for related overlay/modal patterns ---'
rg -n "onMouseDownCapture|onPointerDownCapture|wasFloatingLayerOpenRef|hasOpenFloatingLayer|floating layer|OverlayModal" apps/timo-web -S

Repository: Team-Timo/Timo-client

Length of output: 6154


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- pointer/mouse event usage in app ---'
rg -n "onPointerDownCapture|onPointerDown|onMouseDownCapture|onMouseDown" apps/timo-web -S

echo
echo '--- focus trap / overlay click-related patterns ---'
rg -n "allowOutsideClick|clickOutsideDeactivates|escapeDeactivates|hasOpenFloatingLayer" apps/timo-web -S

Repository: Team-Timo/Timo-client

Length of output: 943


🌐 Web query:

React onMouseDown touch pointer events click synthesis MDN pointer events

💡 Result:

In web development, pointer events, touch events, and mouse events represent different ways to handle user interaction. The Pointer Events API is the modern, hardware-agnostic standard designed to unify these inputs into a single model [1][2][3]. Key Concepts: Pointer Events vs. Mouse/Touch Events: - Mouse Events (e.g., mousedown, mouseup, click): Specifically designed for mouse input. They are legacy events that have been extended to support touch and other devices to maintain backward compatibility [4][5][6]. - Touch Events (e.g., touchstart, touchend): Low-level events specific to touch interfaces. They support multi-touch but are not standard on all devices and can be difficult to manage alongside mouse inputs [5][7]. - Pointer Events (e.g., pointerdown, pointerup, pointermove): A unified API that handles mouse, touch, and pen/stylus input within a single set of event types [1][2][3]. Using pointer events is the recommended approach for modern, cross-device applications [5][8]. Click Synthesis: Browsers perform "click synthesis" to ensure compatibility. When a user performs a touch gesture or a primary button mouse press and release, the browser generates a click event [6]. This synthesis allows elements designed for mouse input to function on touch devices automatically [3][6]. However, reliance on click synthesis can introduce delays (historically, some mobile browsers delayed clicks to wait for potential double-tap gestures), which is why pointer events are often preferred for responsive, performance-critical applications [9][5]. React's Event System: React provides a cross-browser wrapper called the SyntheticEvent system [10][11]. Instead of attaching event listeners to every individual DOM element, React uses event delegation, attaching a single listener to the root (or document) node to intercept native events [12]. When a native event occurs, React captures it, creates a cross-browser-compatible SyntheticEvent object, and dispatches it through its own component tree [10][12]. This system provides consistent behavior across browsers, regardless of whether the underlying input is a mouse, touch, or pointer event [9][11]. Best Practices: For modern applications, developers are encouraged to use pointer events (e.g., onPointerDown) to handle interactions. This provides a unified way to process inputs while maintaining access to the pointerType property to distinguish between devices if specific logic is needed (e.g., desktop vs. mobile interactions) [9][2]. For complex UI components like buttons, many developers use abstraction libraries (like React Aria) that handle these inconsistencies by implementing robust fallbacks and managing touch/mouse/keyboard interactions in a unified, accessible manner [9].

Citations:


포인터 입력까지 포함해 snapshot을 매 클릭마다 소비해 주세요. onMouseDownCapture만으로는 입력 경로가 제한되고, wasFloatingLayerOpenRefonClick 후 초기화하지 않으면 다음 상호작용에 이전 값이 남을 수 있습니다. onPointerDownCapture로 넓히고, onClick에서 값을 읽은 뒤 바로 false로 리셋해 주세요. React 이벤트 / MDN Pointer Events 문서도 같이 보면 좋습니다.

🤖 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/components/modal/OverlayModal.tsx` at line 32, Update the
OverlayModal interaction flow to capture pointer input via onPointerDownCapture
instead of onMouseDownCapture, so mouse, touch, and pen inputs share the
snapshot path. In the onClick handler, read wasFloatingLayerOpenRef.current for
the current interaction, then immediately reset it to false before continuing.

Comment on lines +81 to +85
const toggle = () => {
const next = !isOpen;
setIsOpen(next);
onOpenChange?.(next);
};

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 | 🟠 Major | ⚡ Quick win

onOpenChange가 모든 열림/닫힘 경로에서 호출되지 않습니다.

toggle()에서만 onOpenChange가 호출되고, 외부 클릭(Line 60), ESC 키(Line 66), close()(Line 86)에서는 호출되지 않습니다. 소비자의 상태가 드롭다운 실제 상태와 불일치하게 되어, AI 시간 추천 등 후속 로직이 누락될 수 있습니다.

useEffectisOpen 변경을 감지해 onOpenChange를 일관되게 호출하는 방식을 권장합니다. React 공식 문서의 Using an Effect to synchronize with an external system를 참고하세요.

🔧 제안 수정 — `useEffect`로 `onOpenChange` 호출 일원화
   }, [isOpen]);
 
+  const prevOpenRef = useRef(false);
+  useEffect(() => {
+    if (prevOpenRef.current !== isOpen) {
+      onOpenChange?.(isOpen);
+      prevOpenRef.current = isOpen;
+    }
+  }, [isOpen, onOpenChange]);
+
   const toggle = () => {
     const next = !isOpen;
     setIsOpen(next);
-    onOpenChange?.(next);
   };
   const close = () => setIsOpen(false);
📝 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 toggle = () => {
const next = !isOpen;
setIsOpen(next);
onOpenChange?.(next);
};
}, [isOpen]);
const prevOpenRef = useRef(false);
useEffect(() => {
if (prevOpenRef.current !== isOpen) {
onOpenChange?.(isOpen);
prevOpenRef.current = isOpen;
}
}, [isOpen, onOpenChange]);
const toggle = () => {
const next = !isOpen;
setIsOpen(next);
};
const close = () => setIsOpen(false);
🤖 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/layout/dropdown/Dropdown.tsx`
around lines 81 - 85, Update the Dropdown component’s isOpen synchronization so
onOpenChange is invoked from a useEffect whenever isOpen changes, covering
toggle(), outside-click, Escape-key, and close() paths. Remove the direct
callback from toggle() to avoid duplicate notifications while preserving the
existing state transitions.

- 드래그 앤 드롭으로 순서 변경 시 mock 대신 실제 reorderTodo 뮤테이션(PATCH /api/v1/todos/{todoId}/order)을 호출하도록 연동했습니다
- 성공 시 홈 뷰 쿼리를 무효화해 서버 최신 정렬 순서를 다시 조회하고, 실패 시 이전 순서로 롤백합니다
- 더 이상 쓰이지 않는 todo-order-mock.ts를 삭제했습니다
@kimminna kimminna changed the title [FEAT] 홈 화면 TODO 조회·생성·완료 API 및 AI 소요 시간 추천 연동 [FEAT] 홈 화면 TODO 조회·생성·완료·순서 변경 API 및 AI 소요 시간 추천 연동 Jul 13, 2026

@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 on lines +81 to +85
const toggle = () => {
const next = !isOpen;
setIsOpen(next);
onOpenChange?.(next);
};

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.

외부 클릭(close())이나 ESC 키로 닫힐 때는 onOpenChange가 호출되지 않고있어요. 지금은 TimeSelector가 열림 시점(onOpen)만 쓰고 있어 당장 문제는 아니지만, isOpen 상태 변화와 onOpenChange 알림이 항상 동기화되도록 useEffect 기반으로 일원화해두면 나중에 닫힘 시점 로직이 추가될 때 오류를 막을 수 있을 것 같다고 생각합니다-!

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

좋습니다!

Comment on lines +81 to +83
onMouseDownCapture={() => {
wasFloatingLayerOpenRef.current = hasOpenFloatingLayer();
}}

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.

저희 마우스/키보드 환경만 체크하고 있는데, 태블릿 반응형도 고려해서 만들고 있으니 터치/펜슬 입력도 함께 체크해봐야 할 것 같아요-!

queryKey: getGetHomeQueryKey({ filter, baseDate }),
queryFn: ({ signal }) => getHome({ filter, baseDate }, undefined, signal),
select: ({ data }) => homeViewDataSchema.parse(data),
staleTime: 0,

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.

의도적으로 항상 최신 데이터를 받으려는 결정으로 보이는데, invalidateQueries를 이미 여러 곳에서 호출하고 있어 staleTime: 0이 캐시 이점을 거의 없애고 있다고 생각해요. 화면 전환/포커스 복귀마다 refetch가 잦아질 수 있으니 의도한 트레이드오프가 맞는지 확인해주세요-!

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

staleTime: 0 은 뷰 토글 시 캐시만 쓰고 재요청 안 되는 버그를 고치기 위해 의도적으로 추가한 거여서 의도한 게 맞습니다!

- onMouseDownCapture를 onPointerDownCapture로 교체해 태블릿 터치/펜슬 입력에서도 플로팅 레이어 상태를 정확히 감지하도록 수정
@kimminna
kimminna merged commit 219c3c2 into develop Jul 13, 2026
12 of 13 checks passed
@kimminna
kimminna deleted the feat/web/159-integrate-home-view-api branch July 13, 2026 10:59
@kimminna kimminna mentioned this pull request Jul 14, 2026
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] 홈 화면 조회 API 연동

2 participants