feat(store): 찜한 매장 목록 조회(myWishlistedStores)와 매장 프로필 이미지 - #213
Conversation
찜 화면 시안(매장 찜 목록) 기준. 카드에 매장 프로필(로고)·매장명·평점(리뷰 수)·지역·대표 이미지 3장을 표시한다. 스펙 문서가 없어 정책은 사용자 문답으로 확정했다. 정책 결정(시안 외, 사용자 확정): - 프로필 이미지는 Store 전용 컬럼(profile_image_url) 신설. 셀러 등록은 기존 sellerUpdateStoreBasicInfo에 필드 추가로 처리(null 전달 시 제거, 미전달 시 유지). - 카드 대표 이미지는 StoreImage sort_order asc 상위 3장(매장 상세 캐러셀과 동일 소스). - 정렬은 찜 최신순, offset 페이지네이션(기존 myWishlist와 통일). - 비활성·삭제 매장은 목록·카운트 제외(상품 찜 가시성 정책과 동일). - 평점은 리뷰 없으면 0.0/0건, 소수 첫째 자리 반올림(PopularStore와 동일). 변경점: - prisma: Store.profile_image_url(VarChar 2048, nullable) 마이그레이션 + 시드 반영 - SDL: myWishlistedStores 쿼리·MyWishlistedStoresConnection·WishlistedStoreSummary 신설, SellerStore/SellerUpdateStoreBasicInfoInput에 profileImageUrl 추가 - StoreWishlistRepository.findWishlistedStores: 단일 트랜잭션(목록+카운트), nested select에 deleted_at: null 명시 - StoreWishlistService.myWishlistedStores: aggregateReviewStats·buildRegionLabel 재사용 - StoreWishlistQueryResolver 신설, 모듈 등록 - store.factory에 profile_image_url override 추가 회귀 테스트 12건: - service 6: 최신순 정렬·필드 매핑 / 이미지 3장 제한·삭제 이미지 제외 / 평점 집계·0건 0.0 / 비활성·삭제 매장 및 soft-delete 찜 제외 / 타 사용자 격리 / 페이지네이션 경계 - resolver 2: 전체 경로 통합(목록 반환·빈 목록) - input 5종·seller 3: profileImageUrl 등록/수정/제거/유지, DTO 검증 Claude-Session: https://claude.ai/code/session_01KfiGgWooJdsa4iPQPmqvBj
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
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.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9ef7b0c5b4
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| this.prisma.storeWishlistItem.findMany({ | ||
| where, | ||
| // 같은 밀리초 생성 시 페이지 경계 흔들림 방지를 위해 id를 보조 정렬키로 둔다. | ||
| orderBy: [{ created_at: 'desc' }, { id: 'desc' }], |
There was a problem hiding this comment.
Sort restored wishlists by their re-add time
When a user removes and later re-adds a store, upsertStoreWishlist restores the existing row by updating updated_at while leaving created_at unchanged. Ordering here by created_at therefore places the restored store at its original, potentially much older position and returns that stale value as addedAt, contradicting the query's newest-first contract. Use the restoration timestamp for ordering/output, or reset created_at when restoring the row.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
반영: 복원(재찜) 시 created_at을 재찜 시점으로 갱신해 최신순 정렬과 addedAt 표기가 재찜을 반영하도록 수정. 회귀 테스트 추가.
상품 찜(upsertWishlistItem)도 동일 이슈 — 후속 PR(찜 목록 카드 확장)에서 같은 방식으로 맞출 예정.
🧹 knip — dead-code 리포트전체 리포트
|
🩺 NestJS Doctor — 89/100 (Good)진단 282건 (error 0).
architecture / security 상위 항목
|
Coverage report
Test suite run success1621 tests passing in 189 suites. Report generated by 🧪jest coverage report action from a87312e |
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
PR #213 Codex 리뷰 반영. soft-delete 복원 시 created_at이 최초 찜 시점으로 남아 '찜 최신순' 정렬과 addedAt 표기가 재찜을 반영하지 못하는 문제 수정. 회귀 테스트 1건 추가(재찜 매장이 목록 최상단으로 온다). Claude-Session: https://claude.ai/code/session_01KfiGgWooJdsa4iPQPmqvBj
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 760dff322b
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| update: { deleted_at: null, updated_at: args.now }, | ||
| // 복원(재찜) 시 created_at도 재찜 시점으로 갱신한다 — 목록 '찜 최신순' 정렬과 | ||
| // addedAt 표기가 재찜을 반영하도록(과거 찜 시점으로 밀리는 문제 방지). | ||
| update: { deleted_at: null, created_at: args.now, updated_at: args.now }, |
There was a problem hiding this comment.
Preserve the timestamp for duplicate active wishlist adds
When a client retries addStoreToWishlist for an already-active row, Prisma still executes this update branch and resets created_at. Since findWishlistedStores both orders by and exposes that field as addedAt, a network retry or double tap moves the store to the top and changes its displayed add time despite the mutation's idempotent contract. Update created_at only when restoring a row whose deleted_at was non-null, while leaving active conflicts unchanged.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
반영: 복원(updateMany, deleted_at != null)과 신규 생성(create + P2002 멱등 처리)을 분리. active 찜 중복 요청은 created_at을 건드리지 않음. 중복 추가 케이스에 created_at 불변 검증 추가.
PR #213 Codex 리뷰 2차 반영. upsert update 브랜치가 active 찜에도 실행되어 더블 탭·재시도가 created_at을 리셋(목록 순서·addedAt 변경)하는 문제 수정. 복원(updateMany, deleted_at != null)과 신규 생성(create + P2002 멱등 처리)을 분리해 active 찜 중복 요청은 아무것도 변경하지 않는다. 회귀 테스트: 중복 추가 멱등 케이스에 created_at 불변 검증 추가. Claude-Session: https://claude.ai/code/session_01KfiGgWooJdsa4iPQPmqvBj
PR #213 Codex 리뷰 반영. soft-delete 복원 시 created_at이 최초 찜 시점으로 남아 '찜 최신순' 정렬과 addedAt 표기가 재찜을 반영하지 못하는 문제 수정. 회귀 테스트 1건 추가(재찜 매장이 목록 최상단으로 온다).
PR #213 Codex 리뷰 2차 반영. upsert update 브랜치가 active 찜에도 실행되어 더블 탭·재시도가 created_at을 리셋(목록 순서·addedAt 변경)하는 문제 수정. 복원(updateMany, deleted_at != null)과 신규 생성(create + P2002 멱등 처리)을 분리해 active 찜 중복 요청은 아무것도 변경하지 않는다. 회귀 테스트: 중복 추가 멱등 케이스에 created_at 불변 검증 추가.
feat(store): 찜한 매장 목록 조회(myWishlistedStores)와 매장 프로필 이미지
PR #213 Codex 리뷰(매장 찜 동일 이슈 2건)를 상품 찜에도 반영. - soft-delete 복원 시 created_at이 최초 찜 시점으로 남아 '찜 최신순' 정렬과 addedAt 표기가 재찜을 반영하지 못하는 문제 수정. - 단, active 찜에 대한 중복 요청(더블 탭·재시도)은 created_at을 건드리지 않도록 복원(updateMany, deleted_at != null)과 신규 생성(create + P2002 멱등 처리)을 분리. 회귀 테스트 2건: 재찜 상품 최상단 정렬 / 중복 추가 시 created_at 불변.
배경
찜 화면 시안 중 매장 찜 목록 화면의 백엔드 API입니다.
매장 찜은 지금까지 추가/해제 뮤테이션만 있었고 목록 조회가 없었습니다.
카드에는 매장 프로필(로고)·매장명·평점(리뷰 수)·지역·대표 이미지 3장이 필요한데, 프로필 이미지는 스키마에 없어 컬럼 신설이 함께 들어갑니다.
스펙 문서가 없는 화면이라 정책은 사전에 사용자 문답으로 전부 확정했습니다.
주요 결정 사항
profile_image_url) 신설.StoreImage는 매장 상세 캐러셀 용도라 첫 장을 프로필로 겸용하지 않기로 했습니다.
셀러 등록은 기존
sellerUpdateStoreBasicInfo에 옵셔널profileImageUrl필드 추가로 처리합니다(URL 문자열 직접 전달 — 셀러 이미지 기존 패턴).null 전달 시 제거, 미전달(undefined) 시 유지.
매장 상세(StoreDetail.images)와 동일 소스입니다.
기존
myWishlist(상품 찜)와 동일한{ items, totalCount, hasMore }패턴으로 통일했습니다.상품 찜 및
findWishlistedStoreIds의 가시성 조건과 일치시켰습니다.soft-delete extension이 nested select에 닿지 않으므로
deleted_at: null을 직접 명시했습니다.aggregateReviewStats재사용.리뷰 없으면 0.0/0건, 소수 첫째 자리 반올림(PopularStore와 동일 정책).
찜 추가가 USER 전용이므로 타 계정은 자연히 빈 목록입니다.
검증
yarn validate통과 (189 suites / 1,620 tests).