feat: 달력 월별 조회 및 감정일기 날짜별 조회 API 구현 - #98
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI (base), Organization UI (inherited) Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (4)
🚧 Files skipped from review as they are similar to previous changes (4)
📝 WalkthroughWalkthrough캘린더 월별 레코드, 날짜별 일기, 날짜별 분쟁 조회 API 엔드포인트를 신규 추가하고, 대응하는 React Query 훅 및 API 함수를 구현했습니다. 캘린더 뷰, 기록 목록, 감정일기 목록 컴포넌트를 더미 데이터에서 실데이터로 전환했으며, Prisma 스키마의 enum 값 2개도 변경되었습니다. Changes캘린더·일기·분쟁 실데이터 연동
Prisma 스키마 enum 값 변경
Sequence Diagram(s)sequenceDiagram
participant CalendarPage as Calendar Page
participant useCalendarRecords
participant CalendarRoute as GET /api/calendar
participant DB as Prisma DB
CalendarPage->>useCalendarRecords: year, month 전달
useCalendarRecords->>CalendarRoute: fetch /api/calendar?year&month
CalendarRoute->>DB: emotionDiary 조회 (월 범위)
CalendarRoute->>DB: dispute 조회 (CLOSED, 참여자 포함)
DB-->>CalendarRoute: diaries[], disputes[]
CalendarRoute->>CalendarRoute: diaryMap, disputeMap 집계 → records 구성
CalendarRoute-->>useCalendarRecords: { success:true, data: { records } }
useCalendarRecords-->>CalendarPage: calendarData, isLoading
CalendarPage->>CalendarPage: 로딩 시 오버레이 스피너, 완료 시 CalendarView + 화해횟수 렌더링
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 8
🧹 Nitpick comments (4)
src/domains/dispute/dispute.hooks.ts (1)
32-37: ⚡ Quick winstaleTime 설정 추가를 권장합니다.
useCalendarRecords와useDiariesByDate는 모두staleTime: 1000 * 60 * 5(5분)을 설정하고 있지만, 이 훅에는 staleTime이 없어 기본값 0이 사용됩니다. 일관성을 위해 동일한 staleTime을 설정하는 것이 좋습니다.♻️ 권장 수정안
export function useDisputesByDate(date: string, status?: DisputeStatus) { return useQuery({ queryKey: disputeKeys.byDate(date, status), queryFn: () => fetchDisputesByDate(date, status), enabled: !!date, + staleTime: 1000 * 60 * 5, }); }🤖 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 `@src/domains/dispute/dispute.hooks.ts` around lines 32 - 37, The useDisputesByDate hook is missing the staleTime configuration that is present in similar hooks like useCalendarRecords and useDiariesByDate. Add staleTime: 1000 * 60 * 5 (5 minutes) to the options object passed to the useQuery call in the useDisputesByDate function to maintain consistency across the codebase and prevent unnecessary refetches.src/domains/diary/diary.api.ts (1)
4-9: ⚡ Quick winURLSearchParams 사용을 권장합니다.
현재 템플릿 리터럴로 쿼리 파라미터를 구성하고 있는데,
date값에 특수문자가 포함될 경우 URL 인코딩 문제가 발생할 수 있습니다.dispute.api.ts의fetchDisputesByDate처럼URLSearchParams를 사용하는 것이 더 안전하고 일관성 있습니다.♻️ 권장 수정안
export async function fetchDiariesByDate(date: string): Promise<DiaryItem[]> { - const res = await fetch(`/api/diary?date=${date}`) + const params = new URLSearchParams({ date }) + const res = await fetch(`/api/diary?${params.toString()}`) const json: ApiResponse<{ items: DiaryItem[] }> = await res.json() if (!json.success || !json.data) throw new Error(json.error?.message ?? '감정일기 조회 실패') return json.data.items }🤖 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 `@src/domains/diary/diary.api.ts` around lines 4 - 9, In the fetchDiariesByDate function, replace the template literal approach for constructing the query parameter with URLSearchParams to properly handle URL encoding. Instead of using backticks with `date=${date}` directly in the URL, create a URLSearchParams object, append the date parameter to it, and pass it to the fetch URL. This ensures special characters in the date value are properly encoded and maintains consistency with similar patterns used in other API files like dispute.api.ts.src/components/calendar/CalendarView.tsx (2)
86-86: 💤 Low value선택적 성능 최적화: recordMap 메모이제이션을 고려하세요.
recordMap변환이 매 렌더링마다 실행되는데,calendarData가 변경될 때만 재계산하도록useMemo로 감쌀 수 있습니다. 월별 레코드 수가 적다면 현재 구현도 충분하지만, 성능 개선을 원한다면 고려해보세요.♻️ 선택적 개선안
+import { useMemo } from 'react'; + export default function CalendarView({ onDateChange, selectedDate, calendarData = [], }: CalendarViewProps) { - const recordMap = new Map(calendarData.map((r) => [r.date, r])); + const recordMap = useMemo( + () => new Map(calendarData.map((r) => [r.date, r])), + [calendarData] + );🤖 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 `@src/components/calendar/CalendarView.tsx` at line 86, The recordMap variable in CalendarView.tsx is being recreated on every render regardless of whether calendarData has changed. Wrap the Map creation with the useMemo hook to memoize the recordMap transformation, passing calendarData as a dependency so that the Map is only recalculated when calendarData actually changes. This will prevent unnecessary object creation on subsequent renders with the same data.
70-70: ⚖️ Poor tradeoff타입 단언 대신 런타임 검증을 고려하세요.
as CategoryGroup타입 단언은 API 응답의category값이 항상 유효한CategoryGroup임을 가정합니다. API 계약을 신뢰한다면 현재 구현도 괜찮지만, 방어적 코드를 위해 타입 가드를 추가할 수 있습니다.🛡️ 선택적 개선안: 타입 가드 추가
+const isValidCategory = (cat: string): cat is CategoryGroup => + ['romance', 'family', 'friend', 'work'].includes(cat); + function CustomDay({ recordMap, ...pickerDayProps }: CustomDayProps) { // ... {record.dispute && ( + isValidCategory(record.dispute.category) && ( <CategoryIcon - category={record.dispute.category as CategoryGroup} + category={record.dispute.category} count={record.dispute.count} /> + ) )}🤖 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 `@src/components/calendar/CalendarView.tsx` at line 70, The type assertion `as CategoryGroup` on the `category` prop being passed to the component assumes the API response value is always a valid CategoryGroup without runtime validation. Instead of relying solely on the type assertion, implement a type guard function to validate that the `record.dispute.category` value is actually a valid CategoryGroup at runtime before passing it to the category prop. This provides defensive programming by catching invalid values from the API that might not match the expected CategoryGroup type, allowing you to handle edge cases appropriately.
🤖 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 `@src/app/api/calendar/route.ts`:
- Around line 11-35: The GET handler function in route.ts needs exception
handling to ensure all responses follow the common ApiResponse format. Wrap the
entire function body (starting from const session line through the end) in a
try-catch block, then in the catch block log the actual error details and return
an ApiResponse with appropriate error information and status code. Additionally,
locate the code around lines 104-108 (likely a database operation) and add error
logging there as well to ensure all potential exceptions throughout the handler
are properly captured with context for operational tracking.
In `@src/app/api/diary/route.ts`:
- Around line 24-33: The date validation at the regex check only verifies the
format pattern (YYYY-MM-DD) but does not validate whether the date actually
exists on the calendar, allowing invalid dates like 2026-02-31 to pass through
and potentially cause errors later in the DB query. After creating the
targetDate variable from the date string, add additional validation to ensure
the date is calendar-valid by comparing the original date string with the ISO
date string of the parsed targetDate. If they do not match, return a
NextResponse.json with a 400 status code and VALIDATION_ERROR similar to the
existing format check, indicating that the date is invalid.
In `@src/app/api/disputes/route.ts`:
- Around line 71-72: The date and rawStatus parameters extracted from
searchParams are used without validation, causing Prisma exceptions to result in
500 errors instead of proper 400 responses. Add validation immediately after
extracting these parameters to check that the date is a valid date format before
using it in the Date range operations around lines 103-107, and validate that
rawStatus matches a valid enum value before casting it at line 121. Return 400
Bad Request with appropriate error messages when validation fails to prevent
Prisma exceptions.
In `@src/components/calendar/CalendarView.tsx`:
- Around line 17-23: The EMOTION_TYPE_TO_FEEL mapping in CalendarView.tsx only
defines translations for five hardcoded emotion types (happy, sad, neutral,
annoyed, angry), but the Prisma schema allows emotionType to be any string
value, which means unmapped values could reach the EmotionIcon component and
cause rendering failures. To fix this, take one of these approaches: define an
explicit enum in the Prisma schema to restrict emotionType to only allowed
values, add validation in the API layer to enforce the allowed emotion types
before they reach the database, or identify all actual emotion values currently
stored in the database and add their Korean translations to the
EMOTION_TYPE_TO_FEEL mapping to ensure complete coverage for all possible
values.
In `@src/components/calendar/RecordList.tsx`:
- Around line 20-28: The component currently only handles the `isLoading` state
from the `useDisputesByDate` hook and treats empty data as "no records", which
masks API errors as empty results. Add error state handling from the
`useDisputesByDate` hook and insert a separate error check condition between the
loading check and the empty disputes check to display a distinct error message
when data retrieval fails, ensuring users and operators can distinguish between
actual empty records and query failures.
- Line 46: The StatusBadge component at line 46 directly accesses
STATUS_MAP[status] without runtime validation, creating a crash risk if the
server returns an unexpected status value. First, consolidate the duplicate
DisputeStatus type definitions currently split between src/types/dispute.ts and
src/components/ui/StatusBadge.tsx into a single source file. Then, add runtime
validation to the StatusBadge component by either implementing a guard function
like isValidStatus to check if a status exists in STATUS_MAP before accessing
it, or provide a sensible default fallback value when the status is not found.
This will eliminate the need for the unsafe as DisputeStatus type assertion and
prevent rendering crashes from invalid status values.
In `@src/components/diary/EmotionDiaryList.tsx`:
- Around line 12-18: The current code does not handle the error state from the
useDiariesByDate hook, so when network or server failures occur, the empty items
array is treated as "no diaries registered" instead of showing a proper error
message. Destructure an isError property from the useDiariesByDate hook
alongside data and isLoading, then add a conditional check after the isLoading
check but before the items.length === 0 check in EmotionDiaryList to display an
appropriate error message when isError is true. This way, network/server
failures will be properly distinguished from the case where no diaries have
actually been registered.
In `@src/domains/dispute/dispute.hooks.ts`:
- Around line 52-89: In all four mutation hooks (useSaveStatement,
useSubmitStatement, useRequestJudgment, and useCloseDispute), the onSuccess
callbacks currently only invalidate the detail query using
disputeKeys.detail(disputeId). You need to extend each onSuccess callback to
also invalidate the list-related queries using disputeKeys.completedList and
disputeKeys.byDate, since modifications to disputes (saving, submitting,
requesting judgment, or closing) can affect both the detail view and the list
views. Add additional queryClient.invalidateQueries calls with these additional
query keys alongside the existing detail query invalidation in each hook's
onSuccess handler.
---
Nitpick comments:
In `@src/components/calendar/CalendarView.tsx`:
- Line 86: The recordMap variable in CalendarView.tsx is being recreated on
every render regardless of whether calendarData has changed. Wrap the Map
creation with the useMemo hook to memoize the recordMap transformation, passing
calendarData as a dependency so that the Map is only recalculated when
calendarData actually changes. This will prevent unnecessary object creation on
subsequent renders with the same data.
- Line 70: The type assertion `as CategoryGroup` on the `category` prop being
passed to the component assumes the API response value is always a valid
CategoryGroup without runtime validation. Instead of relying solely on the type
assertion, implement a type guard function to validate that the
`record.dispute.category` value is actually a valid CategoryGroup at runtime
before passing it to the category prop. This provides defensive programming by
catching invalid values from the API that might not match the expected
CategoryGroup type, allowing you to handle edge cases appropriately.
In `@src/domains/diary/diary.api.ts`:
- Around line 4-9: In the fetchDiariesByDate function, replace the template
literal approach for constructing the query parameter with URLSearchParams to
properly handle URL encoding. Instead of using backticks with `date=${date}`
directly in the URL, create a URLSearchParams object, append the date parameter
to it, and pass it to the fetch URL. This ensures special characters in the date
value are properly encoded and maintains consistency with similar patterns used
in other API files like dispute.api.ts.
In `@src/domains/dispute/dispute.hooks.ts`:
- Around line 32-37: The useDisputesByDate hook is missing the staleTime
configuration that is present in similar hooks like useCalendarRecords and
useDiariesByDate. Add staleTime: 1000 * 60 * 5 (5 minutes) to the options object
passed to the useQuery call in the useDisputesByDate function to maintain
consistency across the codebase and prevent unnecessary refetches.
🪄 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: Repository UI (base), Organization UI (inherited)
Review profile: CHILL
Plan: Pro Plus
Run ID: ec9c0d04-39ca-4862-9990-ed246725859f
📒 Files selected for processing (23)
prisma/schema.prismasrc/app/(page)/calendar/page.module.scsssrc/app/(page)/calendar/page.tsxsrc/app/api/calendar/.gitkeepsrc/app/api/calendar/route.tssrc/app/api/diary/route.tssrc/app/api/disputes/route.tssrc/components/calendar/CalendarView.module.scsssrc/components/calendar/CalendarView.tsxsrc/components/calendar/RecordList.module.scsssrc/components/calendar/RecordList.tsxsrc/components/diary/EmotionDiaryList.tsxsrc/components/ui/CategoryIcon.module.scsssrc/components/ui/CategoryIcon.tsxsrc/domains/calendar/.gitkeepsrc/domains/calendar/calendar.api.tssrc/domains/calendar/calendar.hooks.tssrc/domains/diary/diary.api.tssrc/domains/diary/diary.hooks.tssrc/domains/dispute/dispute.api.tssrc/domains/dispute/dispute.hooks.tssrc/types/calendar.tssrc/types/diary.ts
| const date = searchParams.get('date') | ||
| const rawStatus = searchParams.get('status') |
There was a problem hiding this comment.
status/date 쿼리를 선검증해서 500 대신 400을 반환해주세요
Line 103~107은 date 유효성 확인 없이 Date 범위를 만들고, Line 121은 rawStatus를 enum 검증 없이 캐스팅합니다. 잘못된 값이 들어오면 Prisma 예외로 500 처리됩니다.
제안 수정안
const rawCategory = searchParams.get('categoryGroup')
const date = searchParams.get('date')
const rawStatus = searchParams.get('status')
@@
+ const parsedDate = date ? new Date(`${date}T00:00:00.000Z`) : null
+ if (date && (Number.isNaN(parsedDate!.getTime()) || parsedDate!.toISOString().slice(0, 10) !== date)) {
+ return NextResponse.json<ApiResponse>(
+ { success: false, error: { code: 'VALIDATION_ERROR', message: '유효하지 않은 date 파라미터입니다.' } },
+ { status: 400 },
+ )
+ }
+
+ const normalizedStatus = rawStatus ? rawStatus.toUpperCase() : null
+ if (normalizedStatus && !Object.values(DisputeStatus).includes(normalizedStatus as DisputeStatus)) {
+ return NextResponse.json<ApiResponse>(
+ { success: false, error: { code: 'VALIDATION_ERROR', message: '유효하지 않은 status 파라미터입니다.' } },
+ { status: 400 },
+ )
+ }
+
const where = {
@@
- ...(date ? {
+ ...(parsedDate ? {
createdAt: {
- gte: new Date(date),
- lt: new Date(new Date(date).setDate(new Date(date).getDate() + 1)),
+ gte: parsedDate,
+ lt: new Date(parsedDate.getTime() + 24 * 60 * 60 * 1000),
},
} : {}),
@@
- ...(rawStatus ? { status: rawStatus.toUpperCase() as DisputeStatus } : {}),
+ ...(normalizedStatus ? { status: normalizedStatus as DisputeStatus } : {}),
}Also applies to: 103-107, 121-121
🤖 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 `@src/app/api/disputes/route.ts` around lines 71 - 72, The date and rawStatus
parameters extracted from searchParams are used without validation, causing
Prisma exceptions to result in 500 errors instead of proper 400 responses. Add
validation immediately after extracting these parameters to check that the date
is a valid date format before using it in the Date range operations around lines
103-107, and validate that rawStatus matches a valid enum value before casting
it at line 121. Return 400 Bad Request with appropriate error messages when
validation fails to prevent Prisma exceptions.
| const EMOTION_TYPE_TO_FEEL: Record<string, string> = { | ||
| happy: '기쁨', | ||
| sad: '슬픔', | ||
| neutral: '보통', | ||
| annoyed: '짜증', | ||
| angry: '화남', | ||
| }; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Prisma schema에서 emotionType enum 정의 확인
rg -A 10 'enum.*emotion' prisma/schema.prismaRepository: I5-Project/TALKY-OWL
Length of output: 46
🏁 Script executed:
#!/bin/bash
# Find Prisma schema file
fd -e prisma -type f | head -20Repository: I5-Project/TALKY-OWL
Length of output: 235
🏁 Script executed:
#!/bin/bash
# Search for emotionType definition in the codebase
rg 'emotionType' --type-list | head -20
rg -i 'enum.*emotion|emotion.*enum' -A 5 -B 1 | head -100Repository: I5-Project/TALKY-OWL
Length of output: 454
🏁 Script executed:
#!/bin/bash
# View CalendarView.tsx lines 17-23 and around line 62
fd -i 'CalendarView.tsx' -type f -exec cat -n {} \;Repository: I5-Project/TALKY-OWL
Length of output: 235
🏁 Script executed:
#!/bin/bash
# Find Prisma schema file
find . -name "schema.prisma" -o -name "*.prisma" | head -20Repository: I5-Project/TALKY-OWL
Length of output: 87
🏁 Script executed:
#!/bin/bash
# Find CalendarView.tsx
find . -name "CalendarView.tsx" -type fRepository: I5-Project/TALKY-OWL
Length of output: 107
🏁 Script executed:
#!/bin/bash
# Search for emotionType in the codebase
rg 'emotionType' -B 2 -A 2 | head -150Repository: I5-Project/TALKY-OWL
Length of output: 4834
🏁 Script executed:
#!/bin/bash
# View CalendarView.tsx with line numbers, focusing on lines 1-80
cat -n src/components/calendar/CalendarView.tsx | head -90Repository: I5-Project/TALKY-OWL
Length of output: 3574
🏁 Script executed:
#!/bin/bash
# Check EmotionType definition in diary.ts
cat -n src/types/diary.ts | head -80Repository: I5-Project/TALKY-OWL
Length of output: 1402
EMOTION_TYPE_TO_FEEL 매핑의 불완전성을 해결하세요.
Prisma 스키마에서 emotionType은 String? 타입(enum 아님)으로 정의되어 있어 이론상 80자 이내의 모든 문자열을 저장할 수 있습니다. API_SPEC.md에서도 "emotionType 허용 값 목록 및 마스터 데이터 관리 방식"이 아직 확정되지 않았다고 명시되어 있습니다.
현재 EMOTION_TYPE_TO_FEEL 매핑은 5개 값(happy, sad, neutral, annoyed, angry)만 포함하고 있는데, 스키마상 이 외의 값이 데이터베이스에 저장될 수 있습니다. 이 경우 line 62의 fallback이 매핑되지 않은 영문 문자열을 EmotionIcon에 전달하면서 렌더링이 실패할 수 있습니다.
해결 방안:
- emotionType enum을 Prisma에서 명시적으로 정의하거나
- API 레이어에서 허용 값을 제한하는 검증을 추가하거나
- 데이터베이스에 실제 존재하는 모든 emotion 값을 파악하여 매핑을 완성하세요.
🤖 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 `@src/components/calendar/CalendarView.tsx` around lines 17 - 23, The
EMOTION_TYPE_TO_FEEL mapping in CalendarView.tsx only defines translations for
five hardcoded emotion types (happy, sad, neutral, annoyed, angry), but the
Prisma schema allows emotionType to be any string value, which means unmapped
values could reach the EmotionIcon component and cause rendering failures. To
fix this, take one of these approaches: define an explicit enum in the Prisma
schema to restrict emotionType to only allowed values, add validation in the API
layer to enforce the allowed emotion types before they reach the database, or
identify all actual emotion values currently stored in the database and add
their Korean translations to the EMOTION_TYPE_TO_FEEL mapping to ensure complete
coverage for all possible values.
| <Avatar src={dispute.participants[0]?.profileImageUrl ?? undefined} alt="참여자" size="s" /> | ||
| <time className={styles.cardDate}>{formatDate(dispute.createdAt)}</time> | ||
| </div> | ||
| <StatusBadge status={dispute.status as DisputeStatus} /> |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# 상태 타입 계약(서버/훅/UI) 정합성 확인용 읽기 전용 점검
# 기대 결과:
# 1) DisputeStatus 정의와 API 응답 status 값 집합이 일치
# 2) UI에서 불필요한 `as DisputeStatus` 단언이 제거 가능하거나, 가드가 존재
rg -n -C3 --type=ts --type=tsx "export type DisputeStatus|STATUS_MAP|as DisputeStatus|useDisputesByDate|status"Repository: I5-Project/TALKY-OWL
Length of output: 92
🏁 Script executed:
# Find the RecordList.tsx file first
find . -name "RecordList.tsx" -type fRepository: I5-Project/TALKY-OWL
Length of output: 105
🏁 Script executed:
# Search for DisputeStatus definition, StatusBadge, and related code
rg "export type DisputeStatus|STATUS_MAP|as DisputeStatus|useDisputesByDate" --type ts -A 2 -B 2Repository: I5-Project/TALKY-OWL
Length of output: 4854
🏁 Script executed:
# Search for the StatusBadge component definition
find . -name "*StatusBadge*" -type fRepository: I5-Project/TALKY-OWL
Length of output: 144
🏁 Script executed:
cat -n src/components/ui/StatusBadge.tsxRepository: I5-Project/TALKY-OWL
Length of output: 1561
🏁 Script executed:
cat -n src/types/dispute.tsRepository: I5-Project/TALKY-OWL
Length of output: 3371
🏁 Script executed:
cat -n src/domains/dispute/dispute.hooks.tsRepository: I5-Project/TALKY-OWL
Length of output: 3378
StatusBadge 컴포넌트에서 런타임 유효성 검증이 없어 크래시 위험이 존재합니다.
STATUS_MAP[status]에 직접 접근하는데, 타입스크립트의 Record<DisputeStatus, ...> 정의는 런타임에 유효성을 보장하지 않습니다. 서버가 예상 외의 상태값을 반환하면 undefined에서 속성 접근 시 렌더링이 실패합니다.
src/types/dispute.ts와 src/components/ui/StatusBadge.tsx에 중복된 DisputeStatus 정의가 있어서 불필요한 as DisputeStatus 단언도 필요한 상황입니다. 타입 정의를 통합하고, StatusBadge에 런타임 가드(예: 기본값 fallback 또는 isValidStatus 함수)를 추가해 주세요.
🤖 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 `@src/components/calendar/RecordList.tsx` at line 46, The StatusBadge component
at line 46 directly accesses STATUS_MAP[status] without runtime validation,
creating a crash risk if the server returns an unexpected status value. First,
consolidate the duplicate DisputeStatus type definitions currently split between
src/types/dispute.ts and src/components/ui/StatusBadge.tsx into a single source
file. Then, add runtime validation to the StatusBadge component by either
implementing a guard function like isValidStatus to check if a status exists in
STATUS_MAP before accessing it, or provide a sensible default fallback value
when the status is not found. This will eliminate the need for the unsafe as
DisputeStatus type assertion and prevent rendering crashes from invalid status
values.
| export function useSaveStatement(disputeId: string) { | ||
| const queryClient = useQueryClient() | ||
| const queryClient = useQueryClient(); | ||
| return useMutation({ | ||
| mutationFn: (content: string) => saveStatement(disputeId, content), | ||
| onSuccess: () => { | ||
| queryClient.invalidateQueries({ queryKey: disputeKeys.detail(disputeId) }) | ||
| queryClient.invalidateQueries({ queryKey: disputeKeys.detail(disputeId) }); | ||
| }, | ||
| }) | ||
| }); | ||
| } | ||
|
|
||
| export function useSubmitStatement(disputeId: string) { | ||
| const queryClient = useQueryClient() | ||
| const queryClient = useQueryClient(); | ||
| return useMutation({ | ||
| mutationFn: () => submitStatement(disputeId), | ||
| onSuccess: () => { | ||
| queryClient.invalidateQueries({ queryKey: disputeKeys.detail(disputeId) }) | ||
| queryClient.invalidateQueries({ queryKey: disputeKeys.detail(disputeId) }); | ||
| }, | ||
| }) | ||
| }); | ||
| } | ||
|
|
||
| export function useRequestJudgment(disputeId: string) { | ||
| const queryClient = useQueryClient() | ||
| const queryClient = useQueryClient(); | ||
| return useMutation({ | ||
| mutationFn: () => requestJudgment(disputeId), | ||
| onSuccess: () => { | ||
| queryClient.invalidateQueries({ queryKey: disputeKeys.detail(disputeId) }) | ||
| queryClient.invalidateQueries({ queryKey: disputeKeys.detail(disputeId) }); | ||
| }, | ||
| }) | ||
| }); | ||
| } | ||
|
|
||
| export function useCloseDispute(disputeId: string) { | ||
| const queryClient = useQueryClient() | ||
| const queryClient = useQueryClient(); | ||
| return useMutation({ | ||
| mutationFn: () => closeDispute(disputeId), | ||
| onSuccess: () => { | ||
| queryClient.invalidateQueries({ queryKey: disputeKeys.detail(disputeId) }) | ||
| queryClient.invalidateQueries({ queryKey: disputeKeys.detail(disputeId) }); | ||
| }, | ||
| }) | ||
| }); |
There was a problem hiding this comment.
mutation 성공 시 목록 쿼리도 무효화해야 합니다.
현재 모든 mutation 훅(useSaveStatement, useSubmitStatement, useRequestJudgment, useCloseDispute)이 disputeKeys.detail(disputeId)만 무효화하고 있습니다. 하지만 분쟁을 수정/제출/종료하면 목록 데이터(disputeKeys.completedList, disputeKeys.byDate)도 변경될 수 있으므로, 이들도 함께 무효화하지 않으면 목록 화면에 오래된 데이터가 표시될 수 있습니다.
🔄 권장 수정안 (예: useCloseDispute)
export function useCloseDispute(disputeId: string) {
const queryClient = useQueryClient();
return useMutation({
mutationFn: () => closeDispute(disputeId),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: disputeKeys.detail(disputeId) });
+ queryClient.invalidateQueries({ queryKey: ['disputes'] });
},
});
}또는 더 세밀한 무효화:
queryClient.invalidateQueries({ queryKey: disputeKeys.detail(disputeId) });
+ queryClient.invalidateQueries({ queryKey: ['disputes', 'completed'] });
+ queryClient.invalidateQueries({ queryKey: ['disputes', 'byDate'] });🤖 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 `@src/domains/dispute/dispute.hooks.ts` around lines 52 - 89, In all four
mutation hooks (useSaveStatement, useSubmitStatement, useRequestJudgment, and
useCloseDispute), the onSuccess callbacks currently only invalidate the detail
query using disputeKeys.detail(disputeId). You need to extend each onSuccess
callback to also invalidate the list-related queries using
disputeKeys.completedList and disputeKeys.byDate, since modifications to
disputes (saving, submitting, requesting judgment, or closing) can affect both
the detail view and the list views. Add additional queryClient.invalidateQueries
calls with these additional query keys alongside the existing detail query
invalidation in each hook's onSuccess handler.
기능구현
GET /api/calendar — 월별 감정일기·사건 요약 반환 (달력 마킹용)
GET /api/diary — 특정 날짜 감정일기 목록 반환
GET /api/disputes — date·status 쿼리 파라미터 필터 추가
Summary by CodeRabbit
릴리스 노트