[FEAT] 태그 API 연동 및 설정 확인 모달·폴더 구조 개선 - #178
Conversation
- 태그 목록/생성 응답 zod 스키마를 api/tag/tag-schema.ts에 추가했습니다 - getTags/createTag/deleteTag를 감싸는 공유 React Query 훅을 queries/tag/에 추가했습니다 - 홈 도메인에만 있던 태그 생성 모달을 components/tag/로 이동해 설정 페이지와 공유하도록 했습니다
- 홈 모달의 태그 선택 필드에서 고정 프리셋 mock 대신 실제 태그 목록 조회 API를 사용하도록 했습니다 - 태그 생성 시 실제 생성 API를 호출하고 응답으로 받은 tagId를 바로 선택하도록 했습니다 - 태그 생성 실패 시 안내할 토스트 메시지를 ko/en에 추가했습니다
- 설정 페이지의 태그 관리 목록을 mock 배열 대신 실제 태그 목록 조회 API로 교체했습니다 - 태그 이름은 응답에 isDefault가 없어 기본 태그 5종 이름과 매칭해 삭제 가능 여부를 판단하도록 했습니다 - "태그 추가" 버튼을 임시 window.prompt에서 실제 생성 모달로 교체했습니다 - TagChip의 삭제 버튼에 실제 태그 삭제 API를 연동했습니다 - 태그 add/remove가 즉시 반영되도록 해 저장 버튼은 언어 변경에만 적용되도록 했습니다
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning Review limit reached
Next review available in: 32 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 (16)
Walkthrough태그 API 스키마와 React Query 훅을 추가하고 홈 모달과 설정 화면에서 태그 생성·삭제를 서버 API로 처리하도록 변경했다. 설정 화면에는 언어·태그·로그아웃 확인 모달, 약관 컨테이너, 세션 정리 및 관련 라우팅 구조가 추가됐다. Changes태그 API 및 홈 모달
설정 화면 연동
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant User
participant SettingsProfileContainer
participant useSettingsProfile
participant useCreateTag
participant useDeleteTag
participant TagAPI
User->>SettingsProfileContainer: 태그 추가 또는 삭제 선택
SettingsProfileContainer->>useSettingsProfile: 생성·삭제 액션 전달
useSettingsProfile->>useCreateTag: 태그 생성 요청
useSettingsProfile->>useDeleteTag: tagId 삭제 요청
useCreateTag->>TagAPI: 생성 API 호출
useDeleteTag->>TagAPI: 삭제 API 호출
TagAPI-->>useSettingsProfile: 작업 결과 반환
useSettingsProfile-->>SettingsProfileContainer: 성공 또는 오류 콜백
Possibly related PRs
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 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: 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/app/`[locale]/(main)/(with-time-sidebar)/home/_hooks/todo-modal/use-tag-field.tsx:
- Around line 58-76: Use the useCreateTag mutation’s isPending state in
CreateTagModalContainer and pass it to CreateButton so submission is disabled
while tag creation is in progress, preventing duplicate requests. Keep the
existing onSuccess and onError handling unchanged.
In `@apps/timo-web/app/`[locale]/(main)/settings/_hooks/useSettingsProfile.tsx:
- Around line 65-96: Update handleAddTag to guard before opening
CreateTagModalContainer: when tagItems.length is at least 8, show the existing
tagLimit toast/message and return immediately. Reuse the established translation
or toast mechanism from useTagField, while preserving the current modal and
creation flow below the guard for fewer than 8 tags.
- Around line 17-46: Update useSettingsProfile so default-tag detection compares
each tag’s stable name key (or the API’s stable isDefault field) instead of
translated values from tCommon. Reuse the same key-based criterion as
HomeTodoContainer, and keep translation limited to rendering labels so locale
changes never make default tags appear deletable.
In `@apps/timo-web/components/tag/CreateTagModalContainer.tsx`:
- Around line 38-43: Update handleCreate and the onCreate contract so creation
returns a Promise indicating success; await that result and call setName("")
only after a successful API response. Preserve the trimmed input when creation
fails, including the existing validation behavior.
- Around line 27-28: Update CreateTagModalContainer’s translation usage to
depend on a shared Tag namespace instead of Home, including changing the
relevant key references from Home.createTagModal.* to Tag.createTagModal.*. Move
the corresponding createTagModal entries from the Home namespace to the Tag
namespace in both en.json and ko.json, while preserving the existing Common
translations.
🪄 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: 5d75d67f-3c9a-4783-bcbe-96f8d0bb4182
📒 Files selected for processing (14)
apps/timo-web/api/tag/tag-schema.tsapps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_containers/todo-modal/CreateTodoModalContent.tsxapps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_hooks/todo-modal/use-tag-field.tsxapps/timo-web/app/[locale]/(main)/settings/_components/SettingsProfileForm.tsxapps/timo-web/app/[locale]/(main)/settings/_containers/SettingsProfileContainer.tsxapps/timo-web/app/[locale]/(main)/settings/_hooks/useSettingsProfile.tsxapps/timo-web/app/[locale]/(main)/settings/_mocks/profile-mock.tsapps/timo-web/app/[locale]/(main)/settings/_types/profile-type.tsapps/timo-web/components/tag/CreateTagModalContainer.tsxapps/timo-web/messages/en.jsonapps/timo-web/messages/ko.jsonapps/timo-web/queries/tag/use-create-tag.tsapps/timo-web/queries/tag/use-delete-tag.tsapps/timo-web/queries/tag/use-tags.ts
💤 Files with no reviewable changes (1)
- apps/timo-web/app/[locale]/(main)/settings/_mocks/profile-mock.ts
There was a problem hiding this comment.
Caution
Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.
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/app/`[locale]/(main)/(with-time-sidebar)/home/_hooks/todo-modal/use-tag-field.tsx:
- Around line 58-76: Use the useCreateTag mutation’s isPending state in
CreateTagModalContainer and pass it to CreateButton so submission is disabled
while tag creation is in progress, preventing duplicate requests. Keep the
existing onSuccess and onError handling unchanged.
In `@apps/timo-web/app/`[locale]/(main)/settings/_hooks/useSettingsProfile.tsx:
- Around line 65-96: Update handleAddTag to guard before opening
CreateTagModalContainer: when tagItems.length is at least 8, show the existing
tagLimit toast/message and return immediately. Reuse the established translation
or toast mechanism from useTagField, while preserving the current modal and
creation flow below the guard for fewer than 8 tags.
- Around line 17-46: Update useSettingsProfile so default-tag detection compares
each tag’s stable name key (or the API’s stable isDefault field) instead of
translated values from tCommon. Reuse the same key-based criterion as
HomeTodoContainer, and keep translation limited to rendering labels so locale
changes never make default tags appear deletable.
In `@apps/timo-web/components/tag/CreateTagModalContainer.tsx`:
- Around line 38-43: Update handleCreate and the onCreate contract so creation
returns a Promise indicating success; await that result and call setName("")
only after a successful API response. Preserve the trimmed input when creation
fails, including the existing validation behavior.
- Around line 27-28: Update CreateTagModalContainer’s translation usage to
depend on a shared Tag namespace instead of Home, including changing the
relevant key references from Home.createTagModal.* to Tag.createTagModal.*. Move
the corresponding createTagModal entries from the Home namespace to the Tag
namespace in both en.json and ko.json, while preserving the existing Common
translations.
🪄 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: 5d75d67f-3c9a-4783-bcbe-96f8d0bb4182
📒 Files selected for processing (14)
apps/timo-web/api/tag/tag-schema.tsapps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_containers/todo-modal/CreateTodoModalContent.tsxapps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_hooks/todo-modal/use-tag-field.tsxapps/timo-web/app/[locale]/(main)/settings/_components/SettingsProfileForm.tsxapps/timo-web/app/[locale]/(main)/settings/_containers/SettingsProfileContainer.tsxapps/timo-web/app/[locale]/(main)/settings/_hooks/useSettingsProfile.tsxapps/timo-web/app/[locale]/(main)/settings/_mocks/profile-mock.tsapps/timo-web/app/[locale]/(main)/settings/_types/profile-type.tsapps/timo-web/components/tag/CreateTagModalContainer.tsxapps/timo-web/messages/en.jsonapps/timo-web/messages/ko.jsonapps/timo-web/queries/tag/use-create-tag.tsapps/timo-web/queries/tag/use-delete-tag.tsapps/timo-web/queries/tag/use-tags.ts
💤 Files with no reviewable changes (1)
- apps/timo-web/app/[locale]/(main)/settings/_mocks/profile-mock.ts
🛑 Comments failed to post (5)
apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_hooks/todo-modal/use-tag-field.tsx (1)
58-76: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash # CreateTagModalContainer의 onCreate prop 타입과 로딩 상태 처리 확인 ast-grep outline apps/timo-web/components/tag/CreateTagModalContainer.tsx --items all --type function,interface --match 'onCreate|loading|disabled|isLoading' # onCreate prop 타입 정의 확인 rg -n -C3 'onCreate' apps/timo-web/components/tag/CreateTagModalContainer.tsxRepository: Team-Timo/Timo-client
Length of output: 737
🏁 Script executed:
#!/bin/bash set -euo pipefail printf '\n== CreateTagModalContainer outline ==\n' ast-grep outline apps/timo-web/components/tag/CreateTagModalContainer.tsx --view expanded printf '\n== CreateTagModalContainer relevant lines ==\n' cat -n apps/timo-web/components/tag/CreateTagModalContainer.tsx | sed -n '1,220p' printf '\n== use-tag-field wiring ==\n' cat -n apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_hooks/todo-modal/use-tag-field.tsx | sed -n '1,220p'Repository: Team-Timo/Timo-client
Length of output: 3765
🏁 Script executed:
#!/bin/bash set -euo pipefail printf '\n== use-tag-field.tsx ==\n' cat -n 'apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_hooks/todo-modal/use-tag-field.tsx' | sed -n '1,260p' printf '\n== createTag usages and definition ==\n' rg -n -C3 'createTag|isPending|isLoading|mutate\(' 'apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home' 'apps/timo-web/components/tag' 'apps/timo-web/app' | sed -n '1,260p'Repository: Team-Timo/Timo-client
Length of output: 18647
🏁 Script executed:
#!/bin/bash set -euo pipefail printf '\n== useCreateTag hook ==\n' cat -n 'apps/timo-web/queries/tag/use-create-tag.ts' | sed -n '1,220p' printf '\n== settings hook wiring ==\n' cat -n 'apps/timo-web/app/[locale]/(main)/settings/_hooks/useSettingsProfile.tsx' | sed -n '1,220p' printf '\n== CreateTagModalContainer usages ==\n' rg -n -C2 'CreateTagModalContainer' apps/timo-web | sed -n '1,240p'Repository: Team-Timo/Timo-client
Length of output: 10330
생성 중 제출 차단 추가
CreateTagModalContainer쪽에 생성 중 상태가 없어서onCreate가 실행된 뒤에도 버튼이 계속 눌릴 수 있습니다.useCreateTag의isPending을 내려CreateButton을 비활성화하거나, 필요하면mutateAsync로 바꿔 중복 태그 생성 요청을 막아주세요.
참고: https://tanstack.com/query/latest/docs/react/reference/useMutation🤖 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)/home/_hooks/todo-modal/use-tag-field.tsx around lines 58 - 76, Use the useCreateTag mutation’s isPending state in CreateTagModalContainer and pass it to CreateButton so submission is disabled while tag creation is in progress, preventing duplicate requests. Keep the existing onSuccess and onError handling unchanged.apps/timo-web/app/[locale]/(main)/settings/_hooks/useSettingsProfile.tsx (2)
17-46: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash set -euo pipefail git ls-files 'apps/timo-web/app/[locale]/(main)/settings/**' 'apps/timo-web/queries/tag/**' 'apps/timo-web/**/tag*' 'packages/**/tag*' | sed -n '1,200p' echo '--- useSettingsProfile ---' sed -n '1,240p' 'apps/timo-web/app/[locale]/(main)/settings/_hooks/useSettingsProfile.tsx' echo '--- tag-related files ---' rg -n "tagSchema|useTags|useCreateTag|useDeleteTag|tagId|isDefault|dailyLife|work|exercise|assignment|additional|tagLimit|Common" \ 'apps/timo-web' 'packages' \ -g '!**/node_modules/**' \ -g '!**/.next/**' \ -g '!**/dist/**' \ -g '!**/build/**'Repository: Team-Timo/Timo-client
Length of output: 19373
🏁 Script executed:
#!/bin/bash set -euo pipefail echo '--- api/tag/tag-schema.ts ---' cat -n 'apps/timo-web/api/tag/tag-schema.ts' echo '--- queries/tag/use-tags.ts ---' cat -n 'apps/timo-web/queries/tag/use-tags.ts' echo '--- queries/tag/use-create-tag.ts ---' cat -n 'apps/timo-web/queries/tag/use-create-tag.ts' echo '--- queries/tag/use-delete-tag.ts ---' cat -n 'apps/timo-web/queries/tag/use-delete-tag.ts' echo '--- generated tag response models ---' cat -n 'apps/timo-web/api/generated/models/tagResponse.ts' echo cat -n 'apps/timo-web/api/generated/models/tagListResponse.ts' echo cat -n 'apps/timo-web/api/generated/models/tagCreateResponse.ts' echo '--- use of isDefault elsewhere ---' rg -n "isDefault" 'apps/timo-web/app' 'apps/timo-web/components' 'apps/timo-web/queries' -g '!**/node_modules/**' -g '!**/.next/**'Repository: Team-Timo/Timo-client
Length of output: 3925
🏁 Script executed:
#!/bin/bash set -euo pipefail echo '--- home container tag usage ---' sed -n '90,130p' 'apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_containers/HomeTodoContainer.tsx' echo '--- tag field usage ---' sed -n '20,90p' 'apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_hooks/todo-modal/use-tag-field.tsx' echo '--- tag mocks and sample names ---' sed -n '1,120p' 'apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_mocks/todo-mock.ts' echo sed -n '1,120p' 'apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_mocks/today-todo-mock.ts' echo '--- tag display components in settings ---' sed -n '110,150p' 'apps/timo-web/app/[locale]/(main)/settings/_components/SettingsProfileForm.tsx'Repository: Team-Timo/Timo-client
Length of output: 9535
🏁 Script executed:
#!/bin/bash set -euo pipefail rg -n "function isTagLabelKey|const isTagLabelKey|export .*isTagLabelKey|isTagLabelKey\\(" 'apps/timo-web' -g '!**/node_modules/**' -g '!**/.next/**' echo '--- possible helper file(s) ---' fd -a 'tag' 'apps/timo-web' | sed -n '1,120p'Repository: Team-Timo/Timo-client
Length of output: 1854
🏁 Script executed:
#!/bin/bash set -euo pipefail echo '--- HomeTodoContainer top section ---' sed -n '1,50p' 'apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_containers/HomeTodoContainer.tsx' echo '--- settings profile type ---' cat -n 'apps/timo-web/app/[locale]/(main)/settings/_types/profile-type.ts'Repository: Team-Timo/Timo-client
Length of output: 2948
기본 태그 판별 기준을
tag.name번역값이 아니라 안정적인 키로 맞춰주세요.
useSettingsProfile에서는tCommon(\tag.${key}`)로 만든 번역 라벨과tag.name을 비교하고 있는데, 이 코드베이스의 다른 태그 표시 로직은tag.name을"work" | "dailyLife" ...같은 키로 보고 화면에서만 번역합니다. 지금 방식이면 로케일이 바뀌면 기본 태그가 커스텀 태그처럼 보여 삭제 가능 상태로 풀릴 수 있어요.HomeTodoContainer와 같은 기준으로 키 비교를 재사용하거나, API의isDefault` 같은 안정적인 필드를 쓰는 쪽이 맞습니다.
next-intl 문서와 태그 도메인 스키마를 같이 맞춰두면 덜 미끄러집니다.🤖 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)/settings/_hooks/useSettingsProfile.tsx around lines 17 - 46, Update useSettingsProfile so default-tag detection compares each tag’s stable name key (or the API’s stable isDefault field) instead of translated values from tCommon. Reuse the same key-based criterion as HomeTodoContainer, and keep translation limited to rendering labels so locale changes never make default tags appear deletable.
65-96: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash set -euo pipefail # 1) Inspect the target file and nearby context sed -n '1,220p' apps/timo-web/app/[locale]/(main)/settings/_hooks/useSettingsProfile.tsx # 2) Find tag limit / error message definitions and related usages rg -n "tagLimit|tagActionFailed|isTagLimitToastOpen|isTagErrorToastOpen|최대 8개|8개" apps/timo-web -S # 3) Inspect the tag creation flow and error handling around createTag rg -n "createTag\\(|onError: \\(\\) =>|onSuccess: \\(response\\)|CreateTagModalContainer|existingLabels" apps/timo-web/app apps/timo-web/queries -SRepository: Team-Timo/Timo-client
Length of output: 212
🏁 Script executed:
#!/bin/bash set -euo pipefail # Target file sed -n '1,220p' 'apps/timo-web/app/[locale]/(main)/settings/_hooks/useSettingsProfile.tsx' # Related message/error strings and toast states rg -n "tagLimit|tagActionFailed|isTagLimitToastOpen|isTagErrorToastOpen|최대 8개|8개" apps/timo-web -S # Tag creation flow usage rg -n "createTag\\(|CreateTagModalContainer|existingLabels|onError: \\(\\) =>|onSuccess: \\(response\\)" 'apps/timo-web/app' 'apps/timo-web/queries' -SRepository: Team-Timo/Timo-client
Length of output: 9328
🏁 Script executed:
#!/bin/bash set -euo pipefail # Inspect the tag-field flow that already uses a limit toast sed -n '1,220p' 'apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_hooks/todo-modal/use-tag-field.tsx' # Inspect the limit toast container sed -n '1,200p' 'apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_containers/toast/TagLimitToastContainer.tsx' # Inspect settings container toast wiring sed -n '1,180p' 'apps/timo-web/app/[locale]/(main)/settings/_containers/SettingsProfileContainer.tsx' # Inspect create-tag API/query and schema for error/response shape sed -n '1,220p' 'apps/timo-web/queries/tag/use-create-tag.ts' sed -n '1,220p' 'apps/timo-web/api/tag/tag-schema.ts'Repository: Team-Timo/Timo-client
Length of output: 6121
태그 한도 초과는 별도 안내로 분기해 주세요.
apps/timo-web/app/[locale]/(main)/settings/_hooks/useSettingsProfile.tsx에는 8개 제한 가드가 없어서, 이미 한도에 도달한 상태에서도 생성 모달이 열리고 실패 시tagActionFailed만 노출됩니다.home의useTagField처럼tagItems.length >= 8이면 먼저tagLimit토스트를 띄우거나, 서버 에러를 구분해 같은 문구를 재사용하면 사용자 안내가 더 정확해집니다.
참고: https://next-intl.dev/docs/usage/translations🤖 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)/settings/_hooks/useSettingsProfile.tsx around lines 65 - 96, Update handleAddTag to guard before opening CreateTagModalContainer: when tagItems.length is at least 8, show the existing tagLimit toast/message and return immediately. Reuse the established translation or toast mechanism from useTagField, while preserving the current modal and creation flow below the guard for fewer than 8 tags.apps/timo-web/components/tag/CreateTagModalContainer.tsx (2)
27-28: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
공유 컴포넌트에서
useTranslations("Home")사용은 도메인 결합을 유발합니다.
components/tag/는 앱 전역 공유 위치이지만,useTranslations("Home")을 사용해 Home 도메인의 번역 키(Home.createTagModal.*)에 의존하고 있습니다. 설정 페이지 등 다른 도메인에서 이 모달을 사용할 때도 Home 번역이 필요하므로 결합도가 높아집니다.
Tag또는Common등 공유 번역 네임스페이스로 분리하는 것을 권장합니다.♻️ 제안: 번역 네임스페이스 분리
- const t = useTranslations("Home"); + const t = useTranslations("Tag");그리고
messages/en.json과messages/ko.json에서createTagModal키를Home에서Tag네임스페이스로 이동합니다.📝 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.const t = useTranslations("Tag"); const tCommon = useTranslations("Common");🤖 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/tag/CreateTagModalContainer.tsx` around lines 27 - 28, Update CreateTagModalContainer’s translation usage to depend on a shared Tag namespace instead of Home, including changing the relevant key references from Home.createTagModal.* to Tag.createTagModal.*. Move the corresponding createTagModal entries from the Home namespace to the Tag namespace in both en.json and ko.json, while preserving the existing Common translations.
38-43: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value
API 실패 시 입력값이 미리 초기화되어 사용자가 다시 입력해야 합니다.
handleCreate에서onCreate(trimmedName)호출 직후setName("")을 실행하므로, API 호출이 실패해도 입력값이 이미 비워집니다. 에러 토스트는 표시되지만 사용자가 원래 입력값을 다시 타이핑해야 하는 UX 저하가 발생합니다.
onCreate가 Promise를 반환하도록 변경하여 성공 시에만 입력값을 초기화하는 방안을 고려해 보세요.♻️ 제안: onCreate 반환값 기반 초기화
- const handleCreate = () => { - if (!isValid) return; - - onCreate(trimmedName); - setName(""); - }; + const handleCreate = async () => { + if (!isValid) return; + + const result = await onCreate(trimmedName); + if (result) setName(""); + };이를 위해
onCreate시그니처를(label: string) => Promise<boolean>등으로 변경하고, 호출자에서 API 성공 여부를 반환하도록 합니다.🤖 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/tag/CreateTagModalContainer.tsx` around lines 38 - 43, Update handleCreate and the onCreate contract so creation returns a Promise indicating success; await that result and call setName("") only after a successful API response. Preserve the trimmed input when creation fails, including the existing validation behavior.
- 기본 태그 이름을 프론트에서 추정해 매칭하던 방식이 실제 백엔드 기본 태그 구성과 달라 삭제 버튼이 잘못 노출됐습니다 - GET /api/v1/tags 응답에 실제로 내려오는 isDefault 필드를 로컬 zod 스키마에 추가해 그대로 사용하도록 했습니다
…pi-integration # Conflicts: # apps/timo-web/messages/en.json # apps/timo-web/messages/ko.json
Timo Performance ReportBundle Size — timo-web
Lighthouse — timo-web
Image Optimization — timo-web
측정 커밋: |
- 빈 페이지만 있던 설정 계정 탭 라우트를 제거했습니다
- policy/, withdrawal/ 기능 도메인 폴더를 다른 설정 탭과 동일하게 settings/_components, _containers, _types 평탄한 구조로 통일했습니다 - 이동에 따라 SettingsTabsContainer의 import 경로를 갱신했습니다
…to feat/web/177-tag-api-integration # Conflicts: # apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_containers/tag-modal/CreateTagModalContainer.tsx # apps/timo-web/app/[locale]/(main)/settings/_containers/SettingsPolicyContainer.tsx # apps/timo-web/app/[locale]/(main)/settings/_containers/SettingsTabsContainer.tsx # apps/timo-web/app/[locale]/(main)/settings/_hooks/useSettingsProfile.tsx # apps/timo-web/app/[locale]/(main)/settings/_mocks/profile-mock.ts # apps/timo-web/app/[locale]/(main)/settings/_types/profile-type.ts # apps/timo-web/components/tag-modal/CreateTagModalContainer.tsx # apps/timo-web/components/tag/CreateTagModalContainer.tsx # apps/timo-web/hooks/todo-modal/use-tag-field.tsx
- 언어 변경 확인 모달에 다른 모달들과 동일하게 타이모 로고 아이콘을 추가했습니다 - 태그 삭제, 로그아웃 클릭 시에도 동일한 패턴의 확인 모달이 뜨도록 추가했습니다 - SettingsProfileForm을 SettingsProfileView로 리네임하고 언어/태그/로그아웃 섹션의 ref·상태·모달 로직을 각각의 컨테이너로 분리했습니다 - 설정 폴더를 account/terms/withdrawal 탭 단위 하위 폴더로 재구조화했습니다
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 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/tag/tag-schema.ts`:
- Around line 4-6: Strengthen the tagId validation in the tag schema by
requiring it to be an integer greater than zero, while leaving the existing name
and isDefault validations unchanged.
In
`@apps/timo-web/app/`[locale]/(main)/settings/_containers/account/SettingsLogoutModalContainer.tsx:
- Around line 29-34: Replace the hidden-trigger and imperative click flow in
SettingsLogoutModalContainer.tsx at lines 29-34 with a declarative Modal.Trigger
using asChild around the existing PillButton, if supported, and ensure the modal
closes after the action completes. In SettingsTagsSectionContainer.tsx at lines
65-70, use controlled isOpen/onClose state for dynamically selected chips and
explicitly close the modal after a successful delete action.
In
`@apps/timo-web/app/`[locale]/(main)/settings/_hooks/account/useSettingsProfile.tsx:
- Around line 126-138: In the successful language-update flow of the settings
profile hook, replace the direct cache mutation via queryClient.setQueryData
with queryClient.invalidateQueries for the getGetMyProfileQueryKey query. Keep
commitLanguage(next) and ensure the profile is refetched from the server so the
cached language uses the backend’s canonical format.
🪄 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: 4e2f036c-39ae-4ab2-b378-850b31c510fc
📒 Files selected for processing (27)
apps/timo-web/api/tag/tag-schema.tsapps/timo-web/app/[locale]/(main)/settings/_components/.gitkeepapps/timo-web/app/[locale]/(main)/settings/_components/account/SettingsProfileView.tsxapps/timo-web/app/[locale]/(main)/settings/_components/withdrawal/SettingsWithdrawalView.tsxapps/timo-web/app/[locale]/(main)/settings/_containers/SettingsTabsContainer.tsxapps/timo-web/app/[locale]/(main)/settings/_containers/account/SettingsLanguageSectionContainer.tsxapps/timo-web/app/[locale]/(main)/settings/_containers/account/SettingsLogoutModalContainer.tsxapps/timo-web/app/[locale]/(main)/settings/_containers/account/SettingsProfileContainer.tsxapps/timo-web/app/[locale]/(main)/settings/_containers/account/SettingsTagsSectionContainer.tsxapps/timo-web/app/[locale]/(main)/settings/_containers/terms/SettingsTermsContainer.tsxapps/timo-web/app/[locale]/(main)/settings/_containers/withdrawal/SettingsWithdrawalContainer.tsxapps/timo-web/app/[locale]/(main)/settings/_hooks/account/useSettingsLanguageParam.tsapps/timo-web/app/[locale]/(main)/settings/_hooks/account/useSettingsProfile.tsxapps/timo-web/app/[locale]/(main)/settings/_hooks/account/useSettingsProfileLabels.tsapps/timo-web/app/[locale]/(main)/settings/_hooks/useSettingsLanguageParam.tsapps/timo-web/app/[locale]/(main)/settings/_hooks/useSettingsProfileLabels.tsapps/timo-web/app/[locale]/(main)/settings/_types/account/profile-type.tsapps/timo-web/app/[locale]/(main)/settings/_types/withdrawal/withdrawal-type.tsapps/timo-web/app/[locale]/(main)/settings/account/_components/.gitkeepapps/timo-web/app/[locale]/(main)/settings/account/_containers/.gitkeepapps/timo-web/app/[locale]/(main)/settings/account/_queries/.gitkeepapps/timo-web/app/[locale]/(main)/settings/account/page.tsxapps/timo-web/app/[locale]/(main)/settings/withdrawal/_queries/.gitkeepapps/timo-web/components/todo-modal/CreateTodoModalContent.tsxapps/timo-web/hooks/todo-modal/use-tag-field.tsxapps/timo-web/messages/en.jsonapps/timo-web/messages/ko.json
💤 Files with no reviewable changes (3)
- apps/timo-web/app/[locale]/(main)/settings/_hooks/useSettingsLanguageParam.ts
- apps/timo-web/app/[locale]/(main)/settings/account/page.tsx
- apps/timo-web/app/[locale]/(main)/settings/_hooks/useSettingsProfileLabels.ts
| <Modal.Trigger | ||
| ref={modalTriggerRef} | ||
| className="hidden" | ||
| aria-hidden="true" | ||
| tabIndex={-1} | ||
| /> |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
숨김 버튼을 활용한 모달 트리거 제어 개선 제안
현재 두 곳 모두 useRef로 숨겨진 Modal.Trigger 컴포넌트를 참조하여 강제로 .click() 이벤트를 발생시켜 모달을 열고 있습니다. 이러한 구조는 리액트의 선언적 패러다임과 맞지 않아 아쉽습니다. 😅 또한 사용자가 '확인' 버튼을 클릭하여 API 작업이 완료된 후, 모달이 스스로 닫히지 않고 열려 있을 가능성도 존재합니다.
가능하다면 다음과 같이 선언적인 방식을 적용해 보시는 건 어떨까요? (만약 디자인 시스템 제약으로 불가피한 구조라면 편하게 무시해 주셔도 됩니다!)
apps/timo-web/app/[locale]/(main)/settings/_containers/account/SettingsLogoutModalContainer.tsx#L29-L34:Modal.Trigger에asChild속성이 있다면 숨김 버튼 없이 기존PillButton을 직접 감싸도록 수정하고, 동작 후 모달이 닫히는지 확인해 주세요.apps/timo-web/app/[locale]/(main)/settings/_containers/account/SettingsTagsSectionContainer.tsx#L65-L70: 여러 칩 요소에서 동적으로 모달을 열어야 하므로, 상태(isOpen,onClose) 기반의 제어(controlled) 컴포넌트 방식을 고려해 보고 삭제 액션 성공 시 명시적으로 모달을 닫아주는 로직을 확인해 주세요.
📍 Affects 2 files
apps/timo-web/app/[locale]/(main)/settings/_containers/account/SettingsLogoutModalContainer.tsx#L29-L34(this comment)apps/timo-web/app/[locale]/(main)/settings/_containers/account/SettingsTagsSectionContainer.tsx#L65-L70
🤖 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)/settings/_containers/account/SettingsLogoutModalContainer.tsx
around lines 29 - 34, Replace the hidden-trigger and imperative click flow in
SettingsLogoutModalContainer.tsx at lines 29-34 with a declarative Modal.Trigger
using asChild around the existing PillButton, if supported, and ensure the modal
closes after the action completes. In SettingsTagsSectionContainer.tsx at lines
65-70, use controlled isOpen/onClose state for dynamically selected chips and
explicitly close the modal after a successful delete action.
…pi-integration # Conflicts: # apps/timo-web/app/[locale]/(main)/settings/_containers/withdrawal/SettingsWithdrawalContainer.tsx # apps/timo-web/app/[locale]/(main)/settings/_hooks/useSettingsProfile.ts
- 로그아웃과 탈퇴 두 곳에서만 쓰이는 훅이라 전역 hooks가 아닌 settings/_hooks로 옮겼습니다 - use-logout.ts, use-withdraw.ts의 import 경로를 갱신했습니다
- setQueryData로 응답 값을 캐시에 직접 병합하던 로직을 제거했습니다 - updateLanguage 성공 후 getMyProfile 쿼리를 invalidate해 서버의 최신 상태를 다시 받아오도록 했습니다
- Toast.languageChangeFailed 메시지 키를 추가했습니다 - useSettingsProfile의 window.alert를 제거하고 토스트 상태로 교체했습니다 - SettingsProfileContainer에 언어 변경 실패 토스트를 렌더링했습니다
- useSettingsLanguageParam.ts, useSettingsProfileLabels.ts, useClearSession.ts, useSettingsTab.ts를 kebab-case로 리네임했습니다 - 컴포넌트 파일을 제외한 파일은 kebab-case를 쓰는 프로젝트 컨벤션에 맞췄습니다
- 태그 생성 모달(overlay.open + CreateTagModalContainer)을 SettingsProfileContainer로 옮겼습니다
- 캘린더 연결 여부, 태그/언어 에러 토스트 등 UI state를 컨테이너가 소유하도록 했습니다
- 훅은 각 액션에 { onSuccess, onError } 핸들러를 받아 데이터/뮤테이션 로직만 담당하도록 정리했습니다
- JSX가 사라져 useSettingsProfile.tsx를 use-settings-profile.ts로 리네임했습니다
- 홈 모달, 설정 페이지 등 여러 도메인이 공유하는 스키마임을 명확히 하기 위해 api/tag, api/todo에서 api/common으로 옮겼습니다 - 소비하는 11개 파일의 import 경로를 갱신했습니다
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 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)/settings/_containers/account/SettingsProfileContainer.tsx:
- Around line 19-21: Remove the local isCalendarConnected useState in
SettingsProfileContainer and use profileState.calendarConnected as the single
source of truth. Update any calendar connection toggle or rendering logic to
read from that React Query-backed value; use an optimistic update only if
immediate UI feedback is required.
In `@apps/timo-web/app/`[locale]/(main)/settings/_hooks/use-settings-tab.ts:
- Line 5: Rename the union type alias SettingsTab to SettingsTabTypes, then
update the import and all related return-type references in
SettingsNavContainer.tsx to use the new name.
- Around line 14-18: Wrap SettingsTabsContainer with a React Suspense boundary
in page.tsx or layout.tsx, positioned above where useSettingsTab is invoked; do
not rely on the inner AsyncBoundary. Preserve the existing useSettingsTab
behavior and provide a suitable Suspense fallback to avoid static-rendering
bailout/build failures from useSearchParams.
🪄 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: 929303e0-4e6e-4491-8081-9f022ef7f76c
📒 Files selected for processing (14)
apps/timo-web/app/[locale]/(main)/settings/_containers/SettingsNavContainer.tsxapps/timo-web/app/[locale]/(main)/settings/_containers/SettingsTabsContainer.tsxapps/timo-web/app/[locale]/(main)/settings/_containers/account/SettingsProfileContainer.tsxapps/timo-web/app/[locale]/(main)/settings/_containers/withdrawal/SettingsWithdrawalContainer.tsxapps/timo-web/app/[locale]/(main)/settings/_hooks/account/use-settings-language-param.tsapps/timo-web/app/[locale]/(main)/settings/_hooks/account/use-settings-profile-labels.tsapps/timo-web/app/[locale]/(main)/settings/_hooks/account/use-settings-profile.tsapps/timo-web/app/[locale]/(main)/settings/_hooks/use-clear-session.tsapps/timo-web/app/[locale]/(main)/settings/_hooks/use-settings-tab.tsapps/timo-web/app/[locale]/(main)/settings/_queries/.gitkeepapps/timo-web/app/[locale]/(main)/settings/_queries/account/use-logout.tsapps/timo-web/app/[locale]/(main)/settings/_queries/withdrawal/use-withdraw.tsapps/timo-web/messages/en.jsonapps/timo-web/messages/ko.json
There was a problem hiding this comment.
Caution
Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.
Actionable comments posted: 3
🤖 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)/settings/_containers/account/SettingsProfileContainer.tsx:
- Around line 19-21: Remove the local isCalendarConnected useState in
SettingsProfileContainer and use profileState.calendarConnected as the single
source of truth. Update any calendar connection toggle or rendering logic to
read from that React Query-backed value; use an optimistic update only if
immediate UI feedback is required.
In `@apps/timo-web/app/`[locale]/(main)/settings/_hooks/use-settings-tab.ts:
- Line 5: Rename the union type alias SettingsTab to SettingsTabTypes, then
update the import and all related return-type references in
SettingsNavContainer.tsx to use the new name.
- Around line 14-18: Wrap SettingsTabsContainer with a React Suspense boundary
in page.tsx or layout.tsx, positioned above where useSettingsTab is invoked; do
not rely on the inner AsyncBoundary. Preserve the existing useSettingsTab
behavior and provide a suitable Suspense fallback to avoid static-rendering
bailout/build failures from useSearchParams.
🪄 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: 929303e0-4e6e-4491-8081-9f022ef7f76c
📒 Files selected for processing (14)
apps/timo-web/app/[locale]/(main)/settings/_containers/SettingsNavContainer.tsxapps/timo-web/app/[locale]/(main)/settings/_containers/SettingsTabsContainer.tsxapps/timo-web/app/[locale]/(main)/settings/_containers/account/SettingsProfileContainer.tsxapps/timo-web/app/[locale]/(main)/settings/_containers/withdrawal/SettingsWithdrawalContainer.tsxapps/timo-web/app/[locale]/(main)/settings/_hooks/account/use-settings-language-param.tsapps/timo-web/app/[locale]/(main)/settings/_hooks/account/use-settings-profile-labels.tsapps/timo-web/app/[locale]/(main)/settings/_hooks/account/use-settings-profile.tsapps/timo-web/app/[locale]/(main)/settings/_hooks/use-clear-session.tsapps/timo-web/app/[locale]/(main)/settings/_hooks/use-settings-tab.tsapps/timo-web/app/[locale]/(main)/settings/_queries/.gitkeepapps/timo-web/app/[locale]/(main)/settings/_queries/account/use-logout.tsapps/timo-web/app/[locale]/(main)/settings/_queries/withdrawal/use-withdraw.tsapps/timo-web/messages/en.jsonapps/timo-web/messages/ko.json
🛑 Comments failed to post (3)
apps/timo-web/app/[locale]/(main)/settings/_containers/account/SettingsProfileContainer.tsx (1)
19-21: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value
서버 상태와 로컬 상태의 중복 관리 방지
현재
isCalendarConnected를useState로 관리하여 서버 상태(profileState.calendarConnected)를 복사해서 사용하고 계시네요.현재는 API가 모킹되어 있어 즉각적인 UI 반영을 위해 임시로 추가하신 것으로 보입니다만, 향후 실제 캘린더 연동 API를 구현하실 때는 아키텍처 가이드라인에 맞춰 로컬 상태 복제를 피하고 React Query를 단일 출처(Single Source of Truth)로 유지하는 것을 추천해 드려요! 클릭 시 즉각적인 반응이 필요하다면 React Query의 낙관적 업데이트(Optimistic Updates) 패턴을 활용하시면 좋습니다. 😊
🤖 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)/settings/_containers/account/SettingsProfileContainer.tsx around lines 19 - 21, Remove the local isCalendarConnected useState in SettingsProfileContainer and use profileState.calendarConnected as the single source of truth. Update any calendar connection toggle or rendering logic to read from that React Query-backed value; use an optimistic update only if immediate UI feedback is required.Source: Path instructions
apps/timo-web/app/[locale]/(main)/settings/_hooks/use-settings-tab.ts (2)
5-5: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
union 타입 alias 이름을
Types접미사 규칙에 맞춰 주세요.새로 추가된
SettingsTab은 union용typealias인데, 경로 지침은 이런 alias에Types접미사를 요구합니다.SettingsTabTypes로 변경하고SettingsNavContainer.tsx의 import 및 관련 반환 타입도 함께 갱신해 주세요.제안
-export type SettingsTab = "account" | "policy" | "privacy" | "withdrawal"; +export type SettingsTabTypes = + | "account" + | "policy" + | "privacy" + | "withdrawal";경로 지침의 “
typealias는 유니언·튜플·리터럴에만 사용, 접미사Types” 규칙을 적용해야 합니다.📝 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.export type SettingsTabTypes = | "account" | "policy" | "privacy" | "withdrawal";🤖 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)/settings/_hooks/use-settings-tab.ts at line 5, Rename the union type alias SettingsTab to SettingsTabTypes, then update the import and all related return-type references in SettingsNavContainer.tsx to use the new name.Source: Path instructions
14-18: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/usr/bin/env bash set -euo pipefail rg -n -C 5 'useSettingsTab|SettingsTabsContainer|<Suspense|dynamic' \ apps/timo-web/appRepository: Team-Timo/Timo-client
Length of output: 6402
🏁 Script executed:
#!/usr/bin/env bash set -euo pipefail cat -n .agents/skills/quality/timo-review/SKILL.mdRepository: Team-Timo/Timo-client
Length of output: 2559
🏁 Script executed:
#!/usr/bin/env bash set -euo pipefail printf '\n== page.tsx ==\n' cat -n apps/timo-web/app/[locale]/(main)/settings/page.tsx printf '\n== SettingsTabsContainer.tsx ==\n' cat -n apps/timo-web/app/[locale]/(main)/settings/_containers/SettingsTabsContainer.tsx printf '\n== layout files around settings ==\n' find apps/timo-web/app/[locale]/(main) -maxdepth 3 \( -name 'layout.tsx' -o -name 'page.tsx' \) | sortRepository: Team-Timo/Timo-client
Length of output: 228
🏁 Script executed:
#!/usr/bin/env bash set -euo pipefail printf '\n== page.tsx ==\n' cat -n "apps/timo-web/app/[locale]/(main)/settings/page.tsx" printf '\n== SettingsTabsContainer.tsx ==\n' cat -n "apps/timo-web/app/[locale]/(main)/settings/_containers/SettingsTabsContainer.tsx" printf '\n== layout/page files under settings ==\n' find "apps/timo-web/app/[locale]/(main)/settings" -maxdepth 3 \( -name 'layout.tsx' -o -name 'page.tsx' \) | sort | xargs -r -I{} sh -c 'printf "\n-- %s --\n" "$1"; cat -n "$1"' _ {}Repository: Team-Timo/Timo-client
Length of output: 2729
🏁 Script executed:
#!/usr/bin/env bash set -euo pipefail printf '\n== search for Suspense / dynamic in settings route tree ==\n' rg -n -C 3 'Suspense|dynamic\s*=|useSearchParams|SettingsTabsContainer' "apps/timo-web/app/[locale]/(main)/settings" "apps/timo-web/app/[locale]/(main)"Repository: Team-Timo/Timo-client
Length of output: 10176
🏁 Script executed:
#!/usr/bin/env bash set -euo pipefail printf '\n== AsyncBoundary definition ==\n' rg -n -C 4 'export .*AsyncBoundary|function AsyncBoundary|const AsyncBoundary' "apps/timo-web" "components" "packages" printf '\n== file candidates ==\n' fd -a 'AsyncBoundary*' "apps/timo-web" "components" "packages"Repository: Team-Timo/Timo-client
Length of output: 924
🏁 Script executed:
#!/usr/bin/env bash set -euo pipefail FILE="$(fd -a 'AsyncBoundary*' apps/timo-web components packages | head -n 1 || true)" if [ -n "$FILE" ]; then printf '== %s ==\n' "$FILE" cat -n "$FILE" fiRepository: Team-Timo/Timo-client
Length of output: 1074
useSearchParams()는SettingsTabsContainer보다 상위에서 Suspense로 감싸세요.
AsyncBoundary가 있더라도useSettingsTab()호출은 그보다 먼저 실행됩니다.page.tsx(또는layout.tsx)에서<Suspense>로SettingsTabsContainer를 감싸야 정적 렌더링에서의 bailout/build 이슈를 피할 수 있습니다. Next.jsuseSearchParams문서의 Suspense 요구사항도 같이 참고해 주세요.🤖 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)/settings/_hooks/use-settings-tab.ts around lines 14 - 18, Wrap SettingsTabsContainer with a React Suspense boundary in page.tsx or layout.tsx, positioned above where useSettingsTab is invoked; do not rely on the inner AsyncBoundary. Preserve the existing useSettingsTab behavior and provide a suitable Suspense fallback to avoid static-rendering bailout/build failures from useSearchParams.
…pi-integration # Conflicts: # apps/timo-web/components/todo-modal/create/CreateTodoModalContent.tsx
ISSUE 🔗
close #177
What is this PR? 🔍
홈 모달과 설정 페이지에서 태그 관련 mock 데이터를 실제 태그 API(목록 조회·생성·삭제)로 교체하고, 설정 페이지에 확인 모달을 추가하면서 탭 단위로 폴더 구조를 재정리했습니다.
배경
getTags/createTag/deleteTagAPI를 실제로 연동하고, 두 도메인이 공유해야 하는 스키마·쿼리 훅·생성 모달을 앱 공유 위치(api/tag,queries/tag,components/tag)로 추출했습니다. 이어서 태그 삭제·로그아웃에도 언어 변경과 동일한 패턴의 확인 모달을 추가하고, 탭(계정/약관/탈퇴)이 늘어나며 평평하게 섞여 있던 설정 폴더를 탭 단위로 재구조화했습니다.공유 인프라 (
api/tag,queries/tag,components/tag)getTags/createTag/deleteTag를 감싸는 React Query 훅, 태그 생성 모달을 앱 공유 위치에 추가했습니다.CreateTagModalContainer와 관련 스키마는 홈 도메인 폴더(home/_containers,home/_types) 안에 있어 설정 페이지에서 그대로 import할 수 없었습니다.useCreateTag/useDeleteTag는 각각useMutation내부에서 성공 시getGetTagsQueryKey()로 캐시를 무효화하도록 했습니다. 소비하는 쪽(홈/설정)은mutate(variables, { onSuccess, onError })로 각자 필요한 UI 반응(토스트, 모달 닫기, 필드 선택)만 처리합니다.useTags는 생성된useGetTags를 그대로 감싸고select에서 로컬 zod 스키마로.parse()해 백엔드 계약 변경을 조기에 감지하도록 했습니다.CreateTagModalContainer는 삭제하고components/tag/로 이동했습니다(내용 변경 없음).홈 모달 태그 필드
use-tag-field.tsx의 고정 프리셋(TAG_NAMES/TAG_ID_BY_NAME)과 로컬customTagsstate를 제거하고 실제useTags()/useCreateTag()로 교체했습니다.useTags()결과를{ id: tagId, label: name }으로 매핑해 드롭다운을 채웁니다. 태그 생성 시createTag({ name: label })을 호출하고, 성공하면 응답을tagCreateDataSchema.safeParse로 검증한 뒤 반환된tagId를 바로 폼 필드에 선택 상태로 반영하고 모달을 닫습니다. 실패하면 토스트(Toast.tagCreateFailed)를 띄웁니다.deleteTag연동은 이번 PR 범위에서 제외했습니다.설정 페이지 태그 관리
useSettingsProfile의 태그 mock 배열·react-hook-form 스테이징 로직을 제거하고, 실제 목록 조회·생성·삭제 API로 교체했습니다.window.prompt임시 구현이었습니다.getTags응답에는isDefault필드가 없어(/v3/api-docs실제 확인) 기본 태그 5종(Common.tag.*)의 번역된 이름과 매칭해 삭제 가능 여부를 판별합니다 — 백엔드가 기본 태그와 같은 이름의 태그 생성을 막아주므로 이름 매칭으로도 안전합니다. "태그 추가"는 홈 모달과 동일한CreateTagModalContainer를 재사용합니다.설정 확인 모달 (언어 변경 · 태그 삭제 · 로그아웃)
SettingsLanguageSectionContainer,SettingsTagsSectionContainer,SettingsLogoutModalContainer)가 숨김 처리된Modal.Trigger버튼의 ref를 코드에서 직접 클릭해 모달을 열고, 모달의 확인 버튼에서만 실제 액션을 실행합니다. 모달 UI는 기존 타이머 종료/중단 모달과 동일하게Modal.Icon(타이모 로고) +Title+Description+Footer조합을 그대로 재사용합니다.설정 페이지 폴더 구조 (탭 단위)
SettingsProfileForm을SettingsProfileView로 이름을 바꾸고(폼 제출 로직이 없어 Form이라는 이름이 더 이상 맞지 않았습니다), 계정/약관/탈퇴 각 탭의 컴포넌트·컨테이너·훅·타입을account/,terms/,withdrawal/하위 폴더로 재구조화했습니다._components/_containers/_hooks/_types안에 서로 다른 탭의 파일이 평평하게 섞여 있어 어디에 무엇이 속하는지 파악하기 어려웠습니다.home/today폴더에서 쓰이던 "타입 폴더(_components/_containers/_hooks) 안에 기능별 하위 폴더를 두는" 컨벤션을 그대로 따랐습니다(예:_containers/account/,_containers/withdrawal/). 여러 탭이 공유하는useClearSession(로그아웃·탈퇴 두 곳에서만 사용)은 전역hooks/가 아닌settings/_hooks/로 옮겨 탭 공용 위치에 두었습니다.develop에는 이미settings/account/,settings/withdrawal/를 실제 라우트 폴더로 스캐폴딩(page.tsx+ 빈_components/_containers)해 두는 다른 방향의 구조가 있었습니다. 이 브랜치는 이미 그 스캐폴딩을 걷어내고 쿼리 파라미터 탭 방식으로 되돌린 상태였고, 이번 PR도 그 기존 방향(타입 폴더 우선 + 쿼리 파라미터 탭)을 유지하는 쪽을 택했습니다. develop을 머지하며 들어온 실제 로그아웃/탈퇴 API(useLogoutAction,useWithdrawAction)는 이 구조에 맞게 재배치해 통합했습니다.언어 변경 안정성 개선
setQueryData로 프로필 캐시를 직접 병합하던 로직을invalidateQueries로 교체하고, 실패 시window.alert대신 토스트를 띄우도록 했습니다.language필드를 그대로 캐시에 병합하는 방식은 응답 형태가 프로필 조회 응답과 다르면 캐시가 실제 서버 상태와 어긋날 위험이 있었고,window.alert는 다른 실패 케이스들과 달리 앱의 토스트 UI 컨벤션을 따르지 않고 있었습니다.updateLanguage성공 후getGetMyProfileQueryKey()로invalidateQueries를 호출해 프로필을 서버에서 다시 받아오도록 했습니다. 실패 시에는 다른 액션들과 동일하게isLanguageErrorToastOpen상태 +AnimatedToast(Toast.languageChangeFailed)로 처리합니다.To Reviewers
isDefault판별을 이름 매칭으로 처리한 부분(DEFAULT_TAG_NAME_KEYS)이 다소 우회적인 방식이라 한번 봐주세요 — 백엔드TagResponse에isDefault필드가 없어서 나온 임시방편이며, 추후 백엔드 스펙에 필드가 추가되면 정리가 필요합니다.설정 폴더를
develop의 라우트 우선 스캐폴딩과 다른 방향(타입 폴더 우선)으로 유지하기로 한 판단이 맞는지 확인 부탁드립니다 — 두 구조가 계속 공존하면 이후 병합 때마다 이번처럼 충돌이 반복될 수 있어서, 팀 차원의 방향 정리가 필요해 보입니다.홈 모달의 태그 삭제는 UI가 없어 이번 범위에서 의도적으로 제외했습니다.
Screenshot 📷
실제 화면(설정 페이지 태그 추가 모달, 확인 모달, 삭제 버튼 동작)은 이번 세션에서 브라우저로 캡처하지 않아 스크린샷은 첨부하지 못했습니다.
Test Checklist ✔
pnpm check-types통과pnpm lint통과pnpm build:web통과 (번들 크기 경고 없음)