Skip to content

[FIX] 상세 TODO 조회/수정/삭제 API 연결 개선#210

Merged
kimminna merged 15 commits into
developfrom
fix/web/202-todo-detail-api
Jul 15, 2026
Merged

[FIX] 상세 TODO 조회/수정/삭제 API 연결 개선#210
kimminna merged 15 commits into
developfrom
fix/web/202-todo-detail-api

Conversation

@yumin-kim2

Copy link
Copy Markdown
Contributor

ISSUE 🔗

close #202


What is this PR? 🔍

상세 TODO 모달의 조회/수정/삭제 API 연결 구조를 정리하고, generated API 훅을 기준으로 요청이 나가도록 수정했습니다.

기존에는 상세 모달 내부에서 API 요청 흐름과 UI 상태 관리가 섞여 있어 어떤 동작에서 어떤 요청이 나가는지 추적하기 어려웠습니다. 이번 작업에서는 상세 모달을 overlay 기반 컨테이너 구조로 분리하고, 상세 조회/수정/삭제 요청을 각각 담당 훅을 통해 처리하도록 정리했습니다.

해결한 문제

  • 상세 TODO 모달을 overlay 기반으로 열도록 변경했습니다.
  • generated의 useGetTodoDetail, useUpdateTodo, useDeleteTodo 흐름을 사용하도록 정리했습니다.
  • 상세 TODO 수정 시 전체 데이터를 다시 보내지 않고, 변경된 필드만 PATCH 요청으로 전달하도록 수정했습니다.
  • 제목/메모 수정 후 모달을 닫을 때 debounce 대기 중인 변경사항이 누락되지 않도록 닫기 전에 수정 요청을 한 번 더 처리했습니다.
  • 아이콘 선택 시 즉시 PATCH가 나가던 문제를 수정했습니다.
    • 아이콘을 클릭하는 순간에는 선택 상태만 변경합니다.
    • “아이콘 추가하기” 버튼을 다시 눌러 확정할 때만 PATCH 요청을 보냅니다.
  • 상세 모달의 타이머 상태가 active timer 상태를 반영하도록 수정했습니다.
  • 홈 TODO 카드와 상세 모달의 플레이 버튼 클릭 이벤트가 카드 클릭/드래그 이벤트에 막히지 않도록 이벤트 전파를 차단했습니다.

왜 이 방법을 사용했는지

상세 TODO 모달은 조회, 수정, 삭제가 모두 연결되는 화면이라 UI 컴포넌트 안에 API 로직이 직접 섞이면 변경 추적이 어려워집니다.

그래서 다음 기준으로 구조를 정리했습니다.

  • 모달을 여는 책임은 DetailTodoModalContainer에서 처리
  • 상세 조회는 generated useGetTodoDetail 사용
  • 수정 요청은 generated useUpdateTodo를 감싼 useUpdateTodoSubmit에서 처리
  • 삭제 요청은 generated useDeleteTodo를 감싼 useDeleteTodoSubmit에서 처리
  • 실제 모달 UI는 DetailTodoModalContent에서 렌더링과 입력 상태 관리에 집중

이렇게 나누면 어떤 UI 동작이 어떤 PATCH body를 만드는지 확인하기 쉬워지고, 이후 에러 처리나 토스트 처리를 추가할 때도 한 곳에서 관리하기 쉬워집니다.

어떻게 해결했는지

상세 조회

DetailTodoModalContainer에서 todoIddate를 기준으로 상세 TODO를 조회합니다.

useGetTodoDetail(todoId, { date })

조회된 데이터는 DetailTodoModalContent로 내려서 UI에 표시합니다.

상세 수정

수정은 generated useUpdateTodo를 사용하는 useUpdateTodoSubmit을 통해 처리합니다.
각 입력 변경 시 전체 TODO 데이터를 보내지 않고 변경된 필드만 PATCH body로 전달합니다.
예시:

updateTodo({ title })
updateTodo({ memo })
updateTodo({ icon })
updateTodo({ date: formatDateKey(nextDate) })
updateTodo({ durationSeconds })
updateTodo({ priority })
updateTodo({ tagId })

수정 성공 후에는 홈 목록과 상세 조회 쿼리를 invalidate하여 최신 데이터를 다시 가져오도록 했습니다.

상세 삭제

삭제는 generated useDeleteTodo를 사용하는 useDeleteTodoSubmit을 통해 처리합니다.
삭제 성공 시 홈 목록 쿼리를 invalidate하고, 모달을 닫도록 처리했습니다.

아이콘 수정

기존에는 아이콘을 선택하는 순간 PATCH 요청이 나갔습니다.
이번 수정에서는 pendingIcon 상태를 따로 두어 아이콘 선택과 저장을 분리했습니다.

  • 아이콘 클릭: pendingIcon만 변경
  • “아이콘 추가하기” 버튼 클릭: 현재 아이콘과 다를 때만 PATCH 요청
    이를 통해 사용자가 아이콘을 둘러보는 과정에서 불필요한 API 요청이 나가지 않도록 했습니다.

플레이 버튼 이벤트

홈 카드와 상세 모달 내부 플레이 버튼에서 stopPropagation을 처리했습니다.
카드 자체의 클릭/드래그 이벤트와 플레이 버튼 클릭 이벤트가 섞이지 않도록 분리하기 위해서입니다.



To Reviewers

현재 해결하지 못한 부분
날짜, AI 추천 시간, 드롭다운 수정 시 409 응답
현재 프론트에서는 generated API 기준으로 PATCH 요청을 정상적으로 보내고 있습니다.
예시:

{
  "date": "2026-07-16"
}
{
  "durationSeconds": 5400
}
{
  "priority": "HIGH"
}

하지만 일부 필드 수정 시 서버에서 409 응답이 반환됩니다.
아이콘 변경은 동일한 PATCH API에서 정상 처리되는 것으로 보아, 프론트에서 요청 자체가 누락되는 문제라기보다는 서버의 비즈니스 검증에서 거절되는 케이스로 보입니다.
특히 날짜 변경의 경우 상세 조회는 todoId + date 기준으로 이루어지지만, 수정 API는 PATCH /api/v1/todos/{todoId} 형태라 서버가 “어느 날짜 인스턴스를 수정하는지” 판단하기 어려울 수 있습니다.



Screenshot 📷



Test Checklist ✔

  • TODO
  • TODO
  • TODO

@vercel

vercel Bot commented Jul 15, 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 15, 2026 6:50am

Request Review

@github-actions github-actions Bot requested review from jjangminii and kimminna July 15, 2026 01:21
@github-actions github-actions Bot added the ⏰ Timo-web Timo 웹 서비스 label Jul 15, 2026
@github-actions github-actions Bot added the 🐛 Bug 기능이 정상적으로 작동하지 않는 문제 수정 label Jul 15, 2026
@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@yumin-kim2, 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: bd419fd0-e8c2-4d57-9703-77447ddceffd

📥 Commits

Reviewing files that changed from the base of the PR and between 541d6b9 and 144d973.

📒 Files selected for processing (12)
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_components/todo-card/HomeTodoCard.tsx
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_components/TodayTodoCard.tsx
  • apps/timo-web/app/[locale]/(main)/settings/_containers/account/SettingsLogoutModalContainer.tsx
  • apps/timo-web/components/todo-modal/detail/DetailTodoModalContent.tsx
  • apps/timo-web/components/todo-modal/detail/DetailTodoTaskFields.tsx
  • apps/timo-web/containers/todo-modal/detail/DetailTodoModalContainer.tsx
  • apps/timo-web/hooks/todo-modal/common/use-tag-field.tsx
  • apps/timo-web/hooks/todo-modal/detail/use-delete-todo-submit.ts
  • apps/timo-web/hooks/todo-modal/detail/use-detail-todo-form.ts
  • apps/timo-web/hooks/todo-modal/detail/use-detail-todo-icon-submit.ts
  • apps/timo-web/hooks/todo-modal/detail/use-detail-todo-patch-handlers.ts
  • apps/timo-web/hooks/todo-modal/detail/use-detail-todo-text-auto-save.ts

Walkthrough

상세 투두 모달의 업데이트 요청을 텍스트·서브태스크 중심의 디바운스 방식으로 분리하고, 필드별 패치 핸들러와 오버레이 기반 모달 수명 주기를 적용했습니다. 홈·오늘 카드 및 모달의 플레이 버튼은 클릭과 포인터 이벤트 전파를 차단합니다.

Changes

투두 상세 모달 및 카드 상호작용

Layer / File(s) Summary
부분 업데이트 요청 계약
apps/timo-web/utils/todo/detail-todo-update-request.ts, apps/timo-web/hooks/todo-modal/detail/use-detail-todo-form.ts, apps/timo-web/utils/todo/todo-time.ts
전체 업데이트 요청 빌더를 텍스트·서브태스크 전용 빌더로 교체하고, 폼 훅의 buildUpdateRequest 반환을 제거했습니다. API duration과 HH:MM 간 변환 유틸도 추가했습니다.
상세 모달 업데이트 제출
apps/timo-web/hooks/todo-modal/detail/use-detail-todo-icon-submit.ts, apps/timo-web/hooks/todo-modal/detail/use-detail-todo-patch-handlers.ts, apps/timo-web/hooks/todo-modal/detail/use-detail-todo-text-auto-save.ts, apps/timo-web/components/todo-modal/detail/DetailTodoModalContent.tsx
텍스트 변경을 2000ms 디바운스로 제출하고 닫기 전에 즉시 저장하며, 아이콘·시간·날짜·우선순위·태그·반복·서브태스크 변경을 개별 패치 요청으로 연결했습니다.
오버레이 모달 연결
apps/timo-web/containers/todo-modal/detail/DetailTodoModalContainer.tsx, apps/timo-web/hooks/todo-modal/detail/use-delete-todo-submit.ts
조회·삭제·수정·타이머 상태 처리를 내부 쿼리 컴포넌트로 이동하고, overlay.openonExited 기반 마운트 해제를 적용했습니다. 오류 콜백은 구체적인 오류 인자를 전달하도록 변경했습니다.
플레이 버튼 이벤트 전파 차단
apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_components/todo-card/HomeTodoCard.tsx, apps/timo-web/app/[locale]/(main)/today/_components/TodayTodoCard.tsx, apps/timo-web/components/todo-modal/detail/DetailTodoTaskFields.tsx
플레이 버튼과 래퍼에서 클릭·포인터 이벤트 전파를 중단한 뒤 플레이 동작을 실행하도록 변경했습니다.

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

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant DetailTodoModalContent
  participant UpdateHandlers
  participant UpdateRequestBuilder
  participant UpdateAPI
  User->>DetailTodoModalContent: 필드 변경
  DetailTodoModalContent->>UpdateHandlers: 변경 이벤트 전달
  UpdateHandlers->>UpdateRequestBuilder: 변경 데이터 정규화
  UpdateHandlers->>UpdateAPI: 디바운스 또는 즉시 PATCH 요청
  UpdateAPI-->>DetailTodoModalContent: 업데이트 결과
Loading

Possibly related PRs

Suggested reviewers: jjangminii, kimminna

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning 아이콘·날짜·서브태스크 패치 개선은 보이지만, 하위 태스크 글자 수 제한과 날짜 변경 후 데이터 유지가 충분히 확인되지 않습니다. 하위 태스크 입력 제한(KR 20/EN 30)과 날짜 변경 후 모달 상태 유지, 성공·실패 토스트 및 409 케이스 처리를 보강해 주세요.
Out of Scope Changes check ⚠️ Warning 홈/오늘 카드의 플레이 버튼 이벤트 차단은 이슈 #202의 상세 TODO 모달 수정 범위를 벗어나는 별도 변경입니다. 이슈 범위와 무관한 카드 플레이 이벤트 수정은 별도 PR로 분리하고, 상세 TODO 모달 관련 변경만 유지해 주세요.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed 제목이 상세 TODO 모달의 조회·수정·삭제 API 연결 개선을 정확히 요약합니다.
Description check ✅ Passed 설명도 상세 TODO 모달의 API 정리와 generated 훅 적용 내용을 직접 다루고 있습니다.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/web/202-todo-detail-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 added the ♣️ 유민 유민양 label Jul 15, 2026
@yumin-kim2 yumin-kim2 changed the title [FIX] API [FIX] 상세 TODO 조회/수정/삭제 API 연결 개선 Jul 15, 2026
@github-actions

github-actions Bot commented Jul 15, 2026

Copy link
Copy Markdown

Timo Performance Report

Bundle Size — timo-web
라우트 크기 First Load JS
/[locale]/home 210.11 kB 🔴 415.97 kB
/[locale]/today 194.23 kB 🔴 400.09 kB
/[locale]/focus 160.03 kB 🔴 365.90 kB
/[locale]/settings 166.58 kB 🔴 372.44 kB
/[locale]/statistics 232.90 kB 🔴 438.76 kB
/[locale]/[...rest] 0 B 🟡 205.86 kB
/[locale]/login 212.93 kB 🔴 418.79 kB
/[locale]/oauth/callback 119.64 kB 🟡 325.50 kB
/[locale]/onboarding 232.88 kB 🔴 438.74 kB
/[locale] 118.95 kB 🟡 324.81 kB
/[locale]/policy 125.08 kB 🟡 330.94 kB

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

Lighthouse — timo-web
URL Perf A11y LCP CLS TBT
/en/home 🔴 62 🟢 95 🔴 15.6s 🟢 0.000 🟡 497ms
/en/today 🔴 66 🟢 95 🔴 15.5s 🟢 0.000 🟡 357ms
/en/focus 🔴 59 🟢 95 🔴 15.3s 🟢 0.000 🟡 588ms
/en/statistics 🔴 62 🟢 95 🔴 15.2s 🟢 0.000 🟡 485ms

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

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

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

측정 커밋: 05e23f4

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

🤖 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/_components/todo-card/HomeTodoCard.tsx:
- Around line 130-135: Remove the redundant onPointerDownCapture handler from
the interactive wrapper using stopInteractiveEvent, keeping the onPointerDown
handler and existing onClick behavior unchanged.

In `@apps/timo-web/app/`[locale]/(main)/today/_components/TodayTodoCard.tsx:
- Around line 66-71: Remove the duplicate stopPlayEvent handler and reuse the
existing stopPropagation utility defined immediately above it for the relevant
event handlers in TodayTodoCard.
- Line 16: Remove the unused MouseEvent and PointerEvent type imports from
TodayTodoCard.tsx after removing stopPlayEvent, while retaining KeyboardEvent
and ReactNode if they are still referenced.
- Around line 132-136: Replace the stopPlayEvent usage in the onClick and
onPointerDown handlers of TodayTodoCard with the existing stopPropagation
function, preserving the current event-stopping behavior while keeping onPlay
execution unchanged.

In `@apps/timo-web/components/todo-modal/detail/DetailTodoModalContent.tsx`:
- Around line 189-209: handleDateChange와 handleTimeChange에서 로컬 폼 상태를 변경한 뒤
updateTodo가 실패하면 이전 날짜·시간 값으로 롤백하거나 재조회하도록 useUpdateTodoSubmit의 onError를 연결하세요.
실패 시 사용자에게 토스트를 표시하고, 성공 시에는 기존 업데이트 동작을 유지하세요.

In `@apps/timo-web/containers/todo-modal/detail/DetailTodoModalContainer.tsx`:
- Line 49: Update the error path in DetailTodoModalContainer so a failed or
missing todo does not return null before OverlayModal cleanup runs. Render the
error modal or explicitly invoke the existing close and unmount handling,
ensuring overlay.open is cleared and the user can close or retry.
- Around line 54-69: The todo update submission currently does not propagate
mutation results to the content component, so failed updates are treated as
completed. In DetailTodoModalContainer.tsx, update updateTodo to pass onSuccess
and onError callbacks through useUpdateTodoSubmit: update
lastSubmittedTextUpdateSignatureRef only on success and show the existing error
toast on failure so retry remains possible. Apply the corresponding callback
handling in DetailTodoModalContent.tsx at the affected submission site; the
container and content must preserve success-only submission confirmation and
failure retry behavior.

In `@apps/timo-web/utils/todo/detail-todo-update-request.ts`:
- Around line 23-32: Update buildDetailTodoSubtasksUpdateRequest to apply
truncateToWeightedLength to each trimmed subtask value before filtering and
constructing the PATCH payload, using the same weighted-length limit and
behavior already applied to todo titles; ensure over-limit subtasks are
truncated rather than sent unchanged.
🪄 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: 00bb9372-1b37-494b-9388-d5c3bd52a74c

📥 Commits

Reviewing files that changed from the base of the PR and between e46922b and 0cb5616.

📒 Files selected for processing (7)
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_components/todo-card/HomeTodoCard.tsx
  • apps/timo-web/app/[locale]/(main)/today/_components/TodayTodoCard.tsx
  • apps/timo-web/components/todo-modal/detail/DetailTodoModalContent.tsx
  • apps/timo-web/components/todo-modal/detail/DetailTodoTaskFields.tsx
  • apps/timo-web/containers/todo-modal/detail/DetailTodoModalContainer.tsx
  • apps/timo-web/hooks/todo-modal/detail/use-detail-todo-form.ts
  • apps/timo-web/utils/todo/detail-todo-update-request.ts

Comment thread apps/timo-web/app/[locale]/(main)/today/_components/TodayTodoCard.tsx Outdated
Comment thread apps/timo-web/app/[locale]/(main)/today/_components/TodayTodoCard.tsx Outdated
Comment thread apps/timo-web/app/[locale]/(main)/today/_components/TodayTodoCard.tsx Outdated
Comment thread apps/timo-web/components/todo-modal/detail/DetailTodoModalContent.tsx Outdated
const closeDetailTodoModal = () => {
setIsOpen(false);
};
if (isError || !todo) return null;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

조회 실패 시 overlay를 반드시 종료하세요.

Line 49에서 바로 null을 반환하면 OverlayModal이 렌더링되지 않아 onExited도 호출되지 않습니다. 따라서 overlay.open 항목이 보이지 않는 상태로 남고 사용자는 닫거나 재시도할 수 없습니다. 오류 모달을 렌더링하거나 오류 시 closeunmount를 명시적으로 처리해 주세요.

🤖 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/containers/todo-modal/detail/DetailTodoModalContainer.tsx` at
line 49, Update the error path in DetailTodoModalContainer so a failed or
missing todo does not return null before OverlayModal cleanup runs. Render the
error modal or explicitly invoke the existing close and unmount handling,
ensuring overlay.open is cleared and the user can close or retry.

Comment thread apps/timo-web/utils/todo/detail-todo-update-request.ts

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

♻️ Duplicate comments (2)
apps/timo-web/containers/todo-modal/detail/DetailTodoModalContainer.tsx (2)

76-87: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

updateTodoonSuccess가 없어서 실패한 수정도 "성공"처럼 처리됩니다.

handleUpdateonError만 넘기고 onSuccess는 넘기지 않아서, 하위 훅들(use-detail-todo-patch-handlers.ts의 날짜/시간 롤백, use-detail-todo-text-auto-save.tslastSubmittedTextUpdateSignatureRef)이 성공 여부를 알 방법이 없습니다. 바로 위 deleteTodo(66-74행)는 onSuccess/onError를 둘 다 활용하는 대조적인 패턴이라, updateTodo도 같은 방식으로 맞춰주면 좋겠습니다. 자세한 내용은 아래 통합 코멘트에 정리했어요.

🤖 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/containers/todo-modal/detail/DetailTodoModalContainer.tsx`
around lines 76 - 87, Update updateTodo to pass an onSuccess callback to
handleUpdate, matching the existing deleteTodo pattern and preserving the
onError callback. Ensure the success callback is propagated so the detail todo
patch handlers and text auto-save logic can distinguish successful updates from
failures.

55-61: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

조회 실패 시 여전히 overlay가 정리되지 않습니다.

onActionError 훅으로 에러 토스트는 뜨지만, 61행에서 바로 null을 반환하는 구조는 그대로라 OverlayModal이 한 번도 렌더링되지 않고 onExited(=unmount)도 호출되지 않습니다. 토스트로 실패는 알 수 있어도, overlay 스택에는 사용자가 닫을 수 없는 "빈 항목"이 남게 돼요. 이전 리뷰에서 지적된 사항이 이번 변경에도 그대로 남아있습니다.

에러 상태를 위한 최소 UI를 렌더링하거나, close/unmount를 명시적으로 호출해 정리해 주세요.

🤖 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/containers/todo-modal/detail/DetailTodoModalContainer.tsx`
around lines 55 - 61, Update DetailTodoModalContainer so the isError or
missing-todo path does not return null while the overlay remains mounted. Render
the minimum error-state UI needed for OverlayModal to invoke its
onExited/unmount cleanup, or explicitly call the existing close/unmount action
before returning; preserve the current onActionError toast behavior.
🤖 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/hooks/todo-modal/detail/use-detail-todo-icon-submit.ts`:
- Around line 6-51: Connect handleRemoveIcon to the persistence flow instead of
only clearing pendingIcon: extend TodoUpdateRequest.icon with the PATCH deletion
representation and add the corresponding removeIcon submission callback or
payload handling. Update handleSubmitIcon and handleToggleIconPanel so a pending
null submits removal, while unchanged selections still close without updating
and non-null selections retain the existing selectIcon/onUpdate behavior.

In `@apps/timo-web/hooks/todo-modal/detail/use-detail-todo-patch-handlers.ts`:
- Around line 46-57: Update handleDateChange and handleTimeChange to preserve
the previous form values and roll them back when updateTodo fails. Extend the
surrounding updateTodo/onUpdate flow to expose mutation failure through the
existing success/error callback mechanism, then restore the prior date or time
only on failure while retaining the optimistic update on success.

In `@apps/timo-web/hooks/todo-modal/detail/use-detail-todo-text-auto-save.ts`:
- Around line 53-61: Update submitTextUpdate and its surrounding update flow so
lastSubmittedTextUpdateSignatureRef is recorded only after the PATCH succeeds,
rather than immediately after the void onUpdate call. Propagate or reuse the
container’s updateTodo success signal through the callback contract, while
preserving the existing duplicate-signature guard and retrying failed updates
when the text remains unchanged.

---

Duplicate comments:
In `@apps/timo-web/containers/todo-modal/detail/DetailTodoModalContainer.tsx`:
- Around line 76-87: Update updateTodo to pass an onSuccess callback to
handleUpdate, matching the existing deleteTodo pattern and preserving the
onError callback. Ensure the success callback is propagated so the detail todo
patch handlers and text auto-save logic can distinguish successful updates from
failures.
- Around line 55-61: Update DetailTodoModalContainer so the isError or
missing-todo path does not return null while the overlay remains mounted. Render
the minimum error-state UI needed for OverlayModal to invoke its
onExited/unmount cleanup, or explicitly call the existing close/unmount action
before returning; preserve the current onActionError toast behavior.
🪄 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: eb9a4df8-325b-457a-89b3-637a1a20e3d4

📥 Commits

Reviewing files that changed from the base of the PR and between 0cb5616 and 541d6b9.

📒 Files selected for processing (8)
  • apps/timo-web/components/todo-modal/detail/DetailTodoModalContent.tsx
  • apps/timo-web/containers/todo-modal/detail/DetailTodoModalContainer.tsx
  • apps/timo-web/hooks/todo-modal/detail/use-delete-todo-submit.ts
  • apps/timo-web/hooks/todo-modal/detail/use-detail-todo-form.ts
  • apps/timo-web/hooks/todo-modal/detail/use-detail-todo-icon-submit.ts
  • apps/timo-web/hooks/todo-modal/detail/use-detail-todo-patch-handlers.ts
  • apps/timo-web/hooks/todo-modal/detail/use-detail-todo-text-auto-save.ts
  • apps/timo-web/utils/todo/todo-time.ts

Comment on lines +6 to +51
export interface UseDetailTodoIconSubmitParams {
icon: TodoIconValue | null;
selectIcon: (icon: TodoIconValue) => void;
onUpdate: (data: TodoUpdateRequest) => void;
}

export const useDetailTodoIconSubmit = ({
icon,
selectIcon,
onUpdate,
}: UseDetailTodoIconSubmitParams) => {
const [isIconPanelOpen, setIsIconPanelOpen] = useState(false);
const [pendingIcon, setPendingIcon] = useState<TodoIconValue | null>(icon);

const handleSelectIcon = (nextIcon: TodoIconValue) => {
setPendingIcon(nextIcon);
};

const handleOpenIconPanel = () => {
setPendingIcon(icon);
setIsIconPanelOpen(true);
};

const handleSubmitIcon = () => {
if (!pendingIcon || pendingIcon === icon) {
setIsIconPanelOpen(false);
return;
}

selectIcon(pendingIcon);
onUpdate({ icon: pendingIcon });
setIsIconPanelOpen(false);
};

const handleToggleIconPanel = () => {
if (isIconPanelOpen) {
handleSubmitIcon();
return;
}

handleOpenIconPanel();
};

const handleRemoveIcon = () => {
setPendingIcon(null);
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail
fd 'todoUpdateRequestIcon' apps/timo-web/api/generated -a
rg -n "TodoUpdateRequestIcon" apps/timo-web/api/generated -g '*.ts' -A5

Repository: Team-Timo/Timo-client

Length of output: 2281


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "## skill"
cat -n .agents/skills/quality/timo-review/SKILL.md

echo "## hook file"
cat -n apps/timo-web/hooks/todo-modal/detail/use-detail-todo-icon-submit.ts

echo "## form file"
cat -n apps/timo-web/hooks/todo-modal/detail/use-detail-todo-form.ts | sed -n '1,220p'

echo "## generated update request"
cat -n apps/timo-web/api/generated/models/todoUpdateRequest.ts

echo "## icon submit usages"
rg -n "useDetailTodoIconSubmit|handleRemoveIcon|removeIcon|onRemoveIcon|selectIcon" apps/timo-web -g '*.ts' -g '*.tsx' -A3 -B3

Repository: Team-Timo/Timo-client

Length of output: 25410


🏁 Script executed:

#!/bin/bash
set -euo pipefail

cat -n .agents/skills/quality/timo-review/SKILL.md
printf '\n---\n'
cat -n apps/timo-web/hooks/todo-modal/detail/use-detail-todo-icon-submit.ts
printf '\n---\n'
cat -n apps/timo-web/hooks/todo-modal/detail/use-detail-todo-form.ts | sed -n '1,220p'
printf '\n---\n'
cat -n apps/timo-web/api/generated/models/todoUpdateRequest.ts
printf '\n---\n'
rg -n "useDetailTodoIconSubmit|handleRemoveIcon|removeIcon|onRemoveIcon|selectIcon" apps/timo-web -g '*.ts' -g '*.tsx' -A3 -B3

Repository: Team-Timo/Timo-client

Length of output: 25345


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "## patch handlers"
cat -n apps/timo-web/hooks/todo-modal/detail/use-detail-todo-patch-handlers.ts

echo "## detail modal"
cat -n apps/timo-web/components/todo-modal/detail/DetailTodoModalContent.tsx | sed -n '1,220p'

echo "## icon selector/remove flow"
cat -n apps/timo-web/components/todo-modal/common/TodoIconField.tsx | sed -n '1,220p'

echo "## update todo usages"
rg -n "updateTodo\(|patchHandlers\.updateTodo|onUpdate\(" apps/timo-web -g '*.ts' -g '*.tsx' -A3 -B3

Repository: Team-Timo/Timo-client

Length of output: 30776


아이콘 제거를 PATCH 경로에 연결해 주세요

handleRemoveIcon이 로컬 pendingIconnull로 바꾸고 끝나서, 제거가 저장되지 않습니다. TodoUpdateRequest.icon도 enum만 허용해 삭제 표현이 없으니, 현재 구조에선 패널을 닫을 때 원래 아이콘으로 되돌아갑니다. 제거를 지원하려면 removeIcon까지 제출 흐름에 연결하고, PATCH 스펙에 삭제 표현을 추가해야 합니다.

🤖 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/hooks/todo-modal/detail/use-detail-todo-icon-submit.ts` around
lines 6 - 51, Connect handleRemoveIcon to the persistence flow instead of only
clearing pendingIcon: extend TodoUpdateRequest.icon with the PATCH deletion
representation and add the corresponding removeIcon submission callback or
payload handling. Update handleSubmitIcon and handleToggleIconPanel so a pending
null submits removal, while unchanged selections still close without updating
and non-null selections retain the existing selectIcon/onUpdate behavior.

@kimminna kimminna merged commit 4a572f5 into develop Jul 15, 2026
11 checks passed
@kimminna kimminna deleted the fix/web/202-todo-detail-api branch July 15, 2026 06:51
@kimminna kimminna mentioned this pull request Jul 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

⏰ Timo-web Timo 웹 서비스 ♣️ 유민 유민양 🐛 Bug 기능이 정상적으로 작동하지 않는 문제 수정

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FIX] 투두 상세 모달 API 요청 오류 수정

2 participants