Skip to content

feat: 위시 단건 조회 응답에 가격 이력 추가 및 응답 타입 서버 계약 기준으로 정리 - #435

Open
iOdiO89 wants to merge 5 commits into
devfrom
feat/416-item-edit-2
Open

feat: 위시 단건 조회 응답에 가격 이력 추가 및 응답 타입 서버 계약 기준으로 정리#435
iOdiO89 wants to merge 5 commits into
devfrom
feat/416-item-edit-2

Conversation

@iOdiO89

@iOdiO89 iOdiO89 commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

작업 요약

  • 위시 단건 조회 응답에 가격 이력(priceHistory) 타입을 추가합니다
  • 위시 단건 조회·수정 응답 타입을 서버 DTO 기준으로 분리하고 nullable 규칙을 바로잡습니다
  • 내 위시 페이지의 불필요한 client wrapper 컴포넌트를 제거합니다

작업 세부 내용

서버 레포의 응답 DTO를 직접 대조하며 작업했습니다.

가격 이력(priceHistory) 타입 추가

서버가 GET /wishlists/{id}/history 를 없애고 가격 이력을 단건 조회 응답에 흡수시켜, 그에 맞춰 타입을 추가했습니다.

  • types/item.tsPriceHistoryT, PriceHistorySourceT 추가
  • GetWishResponseTpriceHistory 필드 추가 (최신순, 최대 50건)
  • source 는 값의 출처를 나타내는 맥락 표시입니다 — SERVER(파서) / SERVER_LLM(LLM) / MANUAL(수기)
  • editedByMe 는 수기 값 한정으로, 같은 상품을 담은 다른 사용자가 입력한 값이면 false 입니다

위시 단건 조회·수정 응답 타입 정정

서버 응답 DTO(WishDetailResponse / WishItemResponse)와 대조해 어긋난 부분을 고쳤습니다.

  • PENDING 을 값이 있는 갈래에서 값이 없는 갈래로 이동 — 서버는 PENDING·PROCESSING 동안 name·price·imageUrl 이 비어 있습니다. 특히 "다시 불러오기" 를 누르면 활성 스냅샷이 즉시 빈 PENDING 으로 교체되므로 실제로 닿는 경로입니다
  • READYcurrencystring | null 로 변경READY 가 보장하는 건 name·price·imageUrl·extractedAt 네 개뿐이고, currency 는 ISO 4217 정규화에 실패하면 null 로 내려옵니다
  • item.source 추가 — 지금 화면에 보이는 값의 출처입니다. 수기 배지 노출에 쓸 수 있고, 출처 기록 도입 전 데이터는 null 입니다
  • sourcePlatform 추가, sourceUrl 은 status 와 무관하므로 공통부로 옮기고 // 확인필 주석을 정리했습니다
  • PatchWishResponseTGetWishResponseT 별칭에서 분리 — 수정 응답은 조회와 다른 DTO 라서 priceHistory 가 없고, 대신 등록 전용 필드인 refreshNeeded·reused 가 항상 null 로 옵니다

내 위시 페이지 client wrapper 제거

page → WishContent(RSC) → WishContentClient(client) 3중 구조에서 가운데 한 겹이 하는 일이 prefetch 뿐이라, page(RSC) → WishContent(client) 2중 구조로 줄였습니다.

  • prefetch·HydrationBoundarypage.tsx 로 이동
  • WishContentClient.tsx 삭제하고 내용을 WishContent.tsx 로 통합

연관 이슈

related to #416

Summary by CodeRabbit

  • 새 기능
    • 위시리스트에 커서 기반 페이지네이션을 적용해 더 많은 항목을 원활하게 불러올 수 있습니다.
    • 상품별 가격 변동 이력을 확인할 수 있도록 관련 정보가 지원됩니다.
    • 위시리스트 삭제 모드, 항목 전체 선택, 삭제 확인 및 상품 추가 화면을 개선했습니다.
  • 개선 사항
    • 처리 중이거나 실패한 항목은 삭제 대상에서 제외해 실수로 삭제하는 상황을 줄였습니다.
    • 알림과 위시리스트 데이터를 더 일관된 방식으로 불러오도록 응답 처리를 개선했습니다.

@iOdiO89 iOdiO89 self-assigned this Aug 4, 2026
@vercel

vercel Bot commented Aug 4, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
piki Error Error Aug 6, 2026 5:47am

@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

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

Next review available in: 55 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

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: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 0f1c6769-b271-4947-ab87-e205f59dbc71

📥 Commits

Reviewing files that changed from the base of the PR and between 22d01ae and 1d3d2bc.

📒 Files selected for processing (9)
  • apps/web/e2e/helpers/apiResponse.ts
  • apps/web/src/apis/getWishlist.ts
  • apps/web/src/app/archive/wish/[id]/_types/wish.ts
  • apps/web/src/app/archive/wish/_components/WishContent.tsx
  • apps/web/src/app/archive/wish/_components/WishContentClient.tsx
  • apps/web/src/app/archive/wish/page.tsx
  • apps/web/src/app/notification/_apis/getNotifications.ts
  • apps/web/src/types/api.ts
  • apps/web/src/types/item.ts
📝 Walkthrough

Walkthrough

공통 API 응답에 페이지네이션 메타데이터를 추가했습니다. 위시리스트 항목과 가격 이력 타입을 확장했습니다. 위시리스트 페이지는 무한 조회 데이터를 사전 조회하고, 클라이언트 화면에서 선택·삭제·추가 상태를 관리합니다.

Changes

위시리스트 페이지네이션 및 데이터 모델

Layer / File(s) Summary
API 응답 페이지네이션 계약
apps/web/src/types/api.ts, apps/web/e2e/helpers/apiResponse.ts, apps/web/src/apis/getWishlist.ts, apps/web/src/app/notification/_apis/getNotifications.ts
ApiResponseTpageResponse를 추가했습니다. 성공 응답 생성 함수가 기본 페이지네이션 정보를 반환합니다. 위시리스트와 알림 응답 타입은 공통 응답 타입을 사용합니다.
위시리스트 항목 및 가격 이력 모델
apps/web/src/types/item.ts, apps/web/src/app/archive/wish/[id]/_types/wish.ts
항목 상태·출처를 표현하는 ItemT와 가격 이력 타입을 추가했습니다. 조회 응답에 priceHistory를 추가하고 수정 응답 타입을 분리했습니다.
위시리스트 조회 및 클라이언트 화면 흐름
apps/web/src/app/archive/wish/page.tsx, apps/web/src/app/archive/wish/_components/WishContent.tsx, apps/web/src/app/archive/wish/_components/WishContentClient.tsx
페이지가 무한 조회 데이터를 사전 조회하고 HydrationBoundary로 전달합니다. WishContent가 목록, 선택, 삭제 모드, 삭제 확인, 위시 추가 UI를 직접 관리합니다. 처리 중이거나 실패한 항목은 전체 선택에서 제외합니다.

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

Sequence Diagram(s)

sequenceDiagram
  participant ArchiveWishPage
  participant ReactQueryClient
  participant getWishlist
  participant WishContent
  ArchiveWishPage->>ReactQueryClient: 무한 조회 사전 실행
  ReactQueryClient->>getWishlist: 커서 기반 위시리스트 요청
  getWishlist-->>ReactQueryClient: 목록과 pageResponse 반환
  ReactQueryClient-->>ArchiveWishPage: dehydrated query state 반환
  ArchiveWishPage->>WishContent: HydrationBoundary로 화면 렌더링
Loading

Possibly related PRs

  • TeamPiKi/client#347: getWishlist.ts와 위시리스트 컴포넌트·타입의 변경이 직접 연결됩니다.
  • TeamPiKi/client#421: getWishlist.tsWishContent.tsx의 페이지네이션 및 사전 조회 변경과 관련됩니다.
  • TeamPiKi/client#424: API 응답 구조와 위시리스트 페이지네이션 변경이 겹칩니다.

Suggested labels: refactor

Suggested reviewers: soyeong0115

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목은 위시 단건 조회 응답의 가격 이력 추가와 응답 타입 정리를 명확하게 요약하며 변경 내용과 일치합니다.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/416-item-edit-2

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 requested a review from soyeong0115 August 4, 2026 07:34
@github-actions github-actions Bot added feature New feature or request WEB labels Aug 4, 2026
@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown

Discord 스레드 연동용 메타데이터입니다. discord-pr-bot 워크플로가 자동 생성하며, 수정·삭제하면 PR 과 Discord 알림 연동이 끊깁니다.

- WishContent.tsx: 브랜치의 client wrapper 삭제 리팩터링 유지
- page.tsx: dev 의 GetWishlistApiResponseT 로 prefetch 타입 정리
서버 ApiResponseBody 는 pageResponse 를 non-null 기본값(EMPTY)으로
모든 응답에 항상 포함한다. 엔드포인트별 intersection 중복을 제거하고
ApiResponseT 로 되돌린다.
@iOdiO89
iOdiO89 force-pushed the feat/416-item-edit-2 branch from 3b9eb7f to 1d3d2bc Compare August 6, 2026 05:46

@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.

🧹 Nitpick comments (7)
apps/web/src/apis/getWishlist.ts (1)

9-9: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

getWishlist 응답 타입 이름을 API 명명 규칙에 맞춰 주세요.

현재 GetWishlistApiResponseT는 함수명 getWishlist에서 파생한 {PascalCaseFunctionName}ResponseT 규칙과 다릅니다. apps/web/src/types/wish.ts의 항목 타입과 이름이 충돌하면 해당 import를 별칭으로 바꾸고, 이 모듈의 wrapper 타입을 GetWishlistResponseT로 변경해 주세요. apps/web/src/app/archive/wish/page.tsx의 import도 함께 갱신해야 합니다.

As per coding guidelines, API 요청 및 응답 타입은 {PascalCaseFunctionName}RequestT{PascalCaseFunctionName}ResponseT 형식을 사용해야 합니다.

🤖 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/web/src/apis/getWishlist.ts` at line 9, Rename the API wrapper type in
getWishlist to GetWishlistResponseT to follow the function-based naming
convention. Resolve any collision with the item type imported from
apps/web/src/types/wish.ts by aliasing that import, and update all references,
including the import in the archive wish page.

Source: Coding guidelines

apps/web/src/app/archive/wish/_components/WishContent.tsx (4)

75-75: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

고정 너비 w-[110px]를 제거하십시오.

코딩 가이드라인은 모바일 레이아웃에서 고정 너비를 피하도록 요구합니다. 이 버튼은 체크박스와 "전체선택" 텍스트만 포함합니다. 콘텐츠 너비로 충분합니다. 큰 글꼴 설정에서 고정 너비는 텍스트를 넘치게 만듭니다.

♻️ 제안 수정
-              className="flex w-[110px] cursor-pointer items-center gap-2"
+              className="flex cursor-pointer items-center gap-2"

가이드라인 근거: "avoid fixed widths in mobile layouts in favor of w-full with px-5 and optional max-w-*".

🤖 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/web/src/app/archive/wish/_components/WishContent.tsx` at line 75, Remove
the fixed w-[110px] width from the button className in WishContent, allowing its
checkbox and “전체선택” text to size naturally without overflow on larger fonts.

Source: Coding guidelines


10-11: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

상위 디렉터리 상대 경로를 절대 경로로 변경하십시오.

코딩 가이드라인은 상대 경로를 동일 디렉터리 파일에만 허용합니다. ../_hooks/는 상위 디렉터리를 참조합니다. @/* 절대 경로를 사용하십시오. 12-15행의 ./ import는 동일 디렉터리이므로 그대로 두십시오.

♻️ 제안 수정
-import { useDeleteWishes } from '../_hooks/useDeleteWishes';
-import { useShareIntentWish } from '../_hooks/useShareIntentWish';
+import { useDeleteWishes } from '`@/app/archive/wish/_hooks/useDeleteWishes`';
+import { useShareIntentWish } from '`@/app/archive/wish/_hooks/useShareIntentWish`';

절대 경로 import이므로 8행 아래 @/ 그룹으로 이동시키십시오.

가이드라인 근거: "Use @/* absolute imports for project modules and relative imports only for files in the same directory."

🤖 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/web/src/app/archive/wish/_components/WishContent.tsx` around lines 10 -
11, Update the useDeleteWishes and useShareIntentWish imports in WishContent.tsx
to use the `@/`* absolute path alias instead of ../_hooks/ relative paths, and
place them with the existing `@/`* import group. Leave the same-directory ./
imports unchanged.

Source: Coding guidelines


124-136: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

onOpenChange의 pending 가드가 중복입니다.

apps/web/src/components/common/confirm-dialog/index.tsx 35-38행의 handleOpenChange가 이미 동일한 가드를 수행합니다. isPending을 prop으로 전달하므로, 호출부의 가드는 효과가 없습니다.

♻️ 제안 수정
-        onOpenChange={open => {
-          if (!open && isDeleteWishesPending) return;
-          setIsDeleteDialogOpen(open);
-        }}
+        onOpenChange={setIsDeleteDialogOpen}
🤖 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/web/src/app/archive/wish/_components/WishContent.tsx` around lines 124 -
136, Remove the redundant pending-state guard from the onOpenChange handler in
WishContent’s ConfirmDialog usage, and pass setIsDeleteDialogOpen directly while
retaining isPending={isDeleteWishesPending} so ConfirmDialog.handleOpenChange
remains responsible for blocking changes during deletion.

39-44: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

상태 문자열 리터럴 대신 ITEM_STATUS 상수를 사용하고 조건을 단순화하십시오.

apps/web/src/consts/item.tsPENDING, PROCESSING, READY, FAILED 네 가지 상태만 정의합니다. 현재 조건은 그중 세 개를 제외하므로 READY만 남습니다. 긍정 조건 하나로 표현하면 의도가 명확해집니다.

또한 리터럴을 직접 비교하면 상태 값이 변경될 때 타입 검사기가 불일치를 잡지 못합니다. apps/web/src/app/archive/wish/[id]/_types/wish.tsItemTREADY 분기에서만 nameprice를 non-null로 정의합니다. 선택 가능 조건과 의미가 일치합니다.

♻️ 제안 수정
+import { ITEM_STATUS } from '`@/consts/item`';
   const { wishlistData } = useGetWishlist();
   const selectableIds = wishlistData
-    .filter(
-      ({ item }) =>
-        item.status !== 'FAILED' && item.status !== 'PENDING' && item.status !== 'PROCESSING'
-    )
+    .filter(({ item }) => item.status === ITEM_STATUS.READY)
     .map(({ wish }) => wish.id);
🤖 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/web/src/app/archive/wish/_components/WishContent.tsx` around lines 39 -
44, Update the selectableIds filter in WishContent to use the ITEM_STATUS
constant and positively retain only the READY status, replacing the three
literal exclusions. Keep the existing mapping to wish.id unchanged.
apps/web/src/app/archive/wish/[id]/_types/wish.ts (1)

7-7: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

| null이 중복입니다.

PriceHistorySourceT는 정의 자체에 null을 포함합니다(apps/web/src/types/item.ts 34행). 따라서 PriceHistorySourceT | nullPriceHistorySourceT와 동일한 타입입니다. 중복 표기는 독자가 별도의 nullable 계층이 있다고 오해하게 만듭니다.

item.sourcePriceHistorySourceT와 다른 값 집합을 가진다면, 별도 타입을 정의하십시오.

♻️ 중복 nullable 제거
-  source: PriceHistorySourceT | null;
+  source: PriceHistorySourceT;
🤖 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/web/src/app/archive/wish/`[id]/_types/wish.ts at line 7, Remove the
redundant | null from the source field declaration in the wish type, using
PriceHistorySourceT directly. Preserve the existing type behavior; only
introduce a separate type if item.source is intentionally meant to support
values outside PriceHistorySourceT.
apps/web/src/app/archive/wish/page.tsx (1)

12-18: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

공유 옵션 팩토리로 위시리스트 무한 쿼리 설정을 한 곳에 두세요.

queryClient.prefetchInfiniteQueryuseGetWishlist가 동일한 queryKey, queryFn, initialPageParam, getNextPageParam을 직접 정의하고 있습니다. TanStack Query v5 infiniteQueryOptions 팩토리가 현재 구조와 타입 추론에 적합하지만, 현재 코드에서 프론트엔드 재시도/하이드레이션 오류가 발생한다는 사실은 없습니다.

🤖 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/web/src/app/archive/wish/page.tsx` around lines 12 - 18, Extract the
shared wishlist infinite-query configuration into an infiniteQueryOptions
factory, then reuse it in both prefetchInfiniteQuery and useGetWishlist. Keep
queryKey, queryFn, initialPageParam, and getNextPageParam defined only in the
factory so TanStack Query v5 preserves consistent options and type inference.
🤖 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.

Nitpick comments:
In `@apps/web/src/apis/getWishlist.ts`:
- Line 9: Rename the API wrapper type in getWishlist to GetWishlistResponseT to
follow the function-based naming convention. Resolve any collision with the item
type imported from apps/web/src/types/wish.ts by aliasing that import, and
update all references, including the import in the archive wish page.

In `@apps/web/src/app/archive/wish/_components/WishContent.tsx`:
- Line 75: Remove the fixed w-[110px] width from the button className in
WishContent, allowing its checkbox and “전체선택” text to size naturally without
overflow on larger fonts.
- Around line 10-11: Update the useDeleteWishes and useShareIntentWish imports
in WishContent.tsx to use the `@/`* absolute path alias instead of ../_hooks/
relative paths, and place them with the existing `@/`* import group. Leave the
same-directory ./ imports unchanged.
- Around line 124-136: Remove the redundant pending-state guard from the
onOpenChange handler in WishContent’s ConfirmDialog usage, and pass
setIsDeleteDialogOpen directly while retaining isPending={isDeleteWishesPending}
so ConfirmDialog.handleOpenChange remains responsible for blocking changes
during deletion.
- Around line 39-44: Update the selectableIds filter in WishContent to use the
ITEM_STATUS constant and positively retain only the READY status, replacing the
three literal exclusions. Keep the existing mapping to wish.id unchanged.

In `@apps/web/src/app/archive/wish/`[id]/_types/wish.ts:
- Line 7: Remove the redundant | null from the source field declaration in the
wish type, using PriceHistorySourceT directly. Preserve the existing type
behavior; only introduce a separate type if item.source is intentionally meant
to support values outside PriceHistorySourceT.

In `@apps/web/src/app/archive/wish/page.tsx`:
- Around line 12-18: Extract the shared wishlist infinite-query configuration
into an infiniteQueryOptions factory, then reuse it in both
prefetchInfiniteQuery and useGetWishlist. Keep queryKey, queryFn,
initialPageParam, and getNextPageParam defined only in the factory so TanStack
Query v5 preserves consistent options and type inference.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 37db91ed-5cce-4561-83ff-aacd82b4b760

📥 Commits

Reviewing files that changed from the base of the PR and between e659d6b and 3b9eb7f.

📒 Files selected for processing (9)
  • apps/web/e2e/helpers/apiResponse.ts
  • apps/web/src/apis/getWishlist.ts
  • apps/web/src/app/archive/wish/[id]/_types/wish.ts
  • apps/web/src/app/archive/wish/_components/WishContent.tsx
  • apps/web/src/app/archive/wish/_components/WishContentClient.tsx
  • apps/web/src/app/archive/wish/page.tsx
  • apps/web/src/app/notification/_apis/getNotifications.ts
  • apps/web/src/types/api.ts
  • apps/web/src/types/item.ts
💤 Files with no reviewable changes (1)
  • apps/web/src/app/archive/wish/_components/WishContentClient.tsx

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature New feature or request WEB

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant