Skip to content

feat(user): 찜 목록 카드 확장(매장별 필터·평점·할인율)과 매장별 그룹 조회 - #214

Merged
chanwoo7 merged 2 commits into
developfrom
feat/product-wishlist-figma
Aug 26, 2026
Merged

feat(user): 찜 목록 카드 확장(매장별 필터·평점·할인율)과 매장별 그룹 조회#214
chanwoo7 merged 2 commits into
developfrom
feat/product-wishlist-figma

Conversation

@chanwoo7

@chanwoo7 chanwoo7 commented Aug 26, 2026

Copy link
Copy Markdown
Member

배경

찜 화면 시안 중 상품 찜 목록·매장별 보기·매장별 찜 상품 화면의 백엔드 API입니다.
기존 myWishlist 카드에는 지역·평점·할인율·매장 ID가 없었고, 찜 상품을 매장 단위로 묶어 보는 API도 없었습니다.
PR #213(매장 찜 목록 + Store.profile_image_url)에 이어지는 작업으로, 매장별 보기 행의 프로필 이미지는 그 컬럼을 사용합니다.
스펙 문서가 없는 화면이라 정책은 사전에 사용자 문답으로 전부 확정했습니다.

주요 결정 사항

  • 특정 매장의 찜 상품 화면은 별도 쿼리 대신 MyWishlistInput.storeId 옵션 필터로 처리.
    카드 구성이 상품 찜 목록과 완전히 동일해 응답 타입과 매핑을 재사용합니다.
  • 매장별 그룹(myWishlistStoreGroups)은 찜 상품 수 desc, 동점 시 최근 찜 순.
    WishlistItem에는 store_id가 없어(product 경유) Prisma groupBy로 매장 단위 집계가 불가능합니다.
    최소 필드만 조회해 service에서 그룹핑했습니다(찜은 사용자당 소규모 전제, 근거는 코드 주석에 남겼습니다).
  • 가시성 조건은 기존 visibleWishlistWhere를 그룹핑에도 공유.
    상품 찜 목록 totalCount와 그룹 카운트 합이 항상 일치합니다(회귀 테스트로 고정).
  • 상품 평점·할인율은 기존 로직 재사용.
    aggregateProductReviewStats(product), calcDiscountRate(product 배럴에 공개), buildRegionLabel(store 배럴).
    리뷰 없으면 0.0/0건, 소수 첫째 자리 반올림 — 매장 카드 표기와 동일 정책입니다.
  • PR feat(store): 찜한 매장 목록 조회(myWishlistedStores)와 매장 프로필 이미지 #213 Codex 리뷰에서 매장 찜에 지적된 created_at 이슈 2건을 상품 찜에도 동일하게 반영.
    soft-delete 복원 시에만 created_at을 재찜 시점으로 갱신하고, active 찜 중복 요청(더블 탭·재시도)은 건드리지 않습니다.

검증

  • yarn validate 통과 (190 suites / 1,635 tests).
  • 회귀 테스트: service 10건(카드 필드 매핑 / 평점 0건 / storeId 필터 / 재찜 최상단·중복 추가 created_at 불변 / 그룹 집계·정렬·동점 처리 / 가시성 일치 / 그룹 페이지네이션 / 빈 목록), resolver 통합 1건, input spec 4건.

찜 화면 시안(상품 찜 목록·매장별 보기) 기준.
상품 카드에 지역 뱃지·상품 평점(리뷰 수)·할인율을 표시하고,
'매장별 보기'는 찜 상품을 매장 단위로 묶어 프로필·찜 상품 수를 보여준다.
스펙 문서가 없어 정책은 사용자 문답으로 확정했다.

정책 결정(시안 외, 사용자 확정):
- 특정 매장의 찜 상품 화면은 별도 쿼리 대신 MyWishlistInput.storeId 옵션 필터로 처리.
- 매장 그룹 정렬은 찜 상품 수 desc, 동점 시 최근 찜 순.
- 상품 평점은 리뷰 없으면 0.0/0건, 소수 첫째 자리 반올림(매장 평점 표기와 동일).
- 할인율은 서버 제공(discountRate 0~100) — PopularCake 등 기존 카드 컨벤션과 통일.
- 가시성(비활성·삭제 상품/매장 제외)은 기존 visibleWishlistWhere를 그룹핑에도 공유해
  상품 찜 목록 totalCount와 그룹 카운트 합이 일치한다.

변경점:
- SDL: WishlistItemSummary에 storeId·discountRate·regionLabel·ratingAverage·reviewCount 추가,
  MyWishlistInput에 storeId 추가, myWishlistStoreGroups 쿼리·타입 신설
- UserRepository: visibleWishlistWhere에 storeId 필터, findWishlistItems select 확장,
  findVisibleWishlistItemsForGrouping 신설 (WishlistItem에 store_id가 없어 groupBy 불가
  → 최소 필드 조회 후 service 그룹핑, 찜은 사용자당 소규모 전제)
- UserWishlistService: 카드 매핑 확장(aggregateProductReviewStats·calcDiscountRate·
  buildRegionLabel 재사용), myWishlistStoreGroups 구현
- product 배럴에 calcDiscountRate 공개(기존 aggregateProductReviewStats는 그대로 재사용)

회귀 테스트 11건:
- service 8: 카드 필드 매핑 / 평점 0건 / storeId 필터 / 그룹 집계·정렬 / 동점 최근 찜 순 /
  가시성 일치(그룹 합 == 목록 totalCount) / 그룹 페이지네이션 / 빈 목록
- resolver 통합 1: myWishlistStoreGroups 전체 경로
- input spec 2: storeId 검증 + 상속 페이지네이션

Claude-Session: https://claude.ai/code/session_01KfiGgWooJdsa4iPQPmqvBj
PR #213 Codex 리뷰(매장 찜 동일 이슈 2건)를 상품 찜에도 반영.
- soft-delete 복원 시 created_at이 최초 찜 시점으로 남아 '찜 최신순' 정렬과
  addedAt 표기가 재찜을 반영하지 못하는 문제 수정.
- 단, active 찜에 대한 중복 요청(더블 탭·재시도)은 created_at을 건드리지 않도록
  복원(updateMany, deleted_at != null)과 신규 생성(create + P2002 멱등 처리)을 분리.
회귀 테스트 2건: 재찜 상품 최상단 정렬 / 중복 추가 시 created_at 불변.

Claude-Session: https://claude.ai/code/session_01KfiGgWooJdsa4iPQPmqvBj
@coderabbitai

coderabbitai Bot commented Aug 26, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: cb9b207f-57b2-4d1c-9a0a-5716100f0a12

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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

Copy link
Copy Markdown

🧹 knip — dead-code 리포트

요약 항목 없음
전체 리포트
(knip 출력 없음 — 이슈 0이거나 실행 실패)

청소 후보(오탐 가능) · 기준 docs/guide/architecture-conventions.md

@github-actions

Copy link
Copy Markdown

🩺 NestJS Doctor — 89/100 (Good)

진단 283건 (error 0).

Category error warning info
architecture 0 0 13
correctness 0 130 0
performance 0 24 17
schema 0 0 86
security 0 13 0
architecture / security 상위 항목
  • info architecture/architecture/no-barrel-export-internals: Barrel file re-exports internal type 'IAuditLogRepository'.
  • warning security/security/no-exposed-env-vars: Direct 'process.env.NODE_ENV' access in 'AuthController'. Use ConfigService instead.
  • warning security/security/require-guards-on-endpoints: Endpoint 'start' has no @UseGuards() at class or method level.
  • warning security/security/require-guards-on-endpoints: Endpoint 'callback' has no @UseGuards() at class or method level.
  • warning security/security/require-guards-on-endpoints: Endpoint 'refresh' has no @UseGuards() at class or method level.
  • warning security/security/require-guards-on-endpoints: Endpoint 'logout' has no @UseGuards() at class or method level.
  • warning security/security/require-guards-on-endpoints: Endpoint 'sellerLogin' has no @UseGuards() at class or method level.
  • warning security/security/require-guards-on-endpoints: Endpoint 'sellerRefresh' has no @UseGuards() at class or method level.
  • warning security/security/require-guards-on-endpoints: Endpoint 'sellerLogout' has no @UseGuards() at class or method level.
  • warning security/security/require-guards-on-endpoints: Endpoint 'devIssueToken' has no @UseGuards() at class or method level.
  • info architecture/architecture/no-barrel-export-internals: Barrel file re-exports internal module '@/features/conversation/repositories/conversation.repository'.
  • info architecture/architecture/no-barrel-export-internals: Barrel file re-exports internal type 'ConversationRepository'.
  • info architecture/architecture/no-barrel-export-internals: Barrel file re-exports internal module '@/features/order/repositories/order.repository'.
  • info architecture/architecture/no-barrel-export-internals: Barrel file re-exports internal type 'OrderRepository'.
  • info architecture/architecture/no-barrel-export-internals: Barrel file re-exports internal module '@/features/product/repositories/product.repository'.

오탐 포함 가능 · 기준 docs/guide/architecture-conventions.md

@github-actions

Copy link
Copy Markdown

Coverage report

St.
Category Percentage Covered / Total
🟢 Statements 97.65% 5029/5150
🟢 Branches 94.02% 2058/2189
🟢 Functions 96.23% 969/1007
🟢 Lines 98.2% 4576/4660

Test suite run success

1635 tests passing in 190 suites.

Report generated by 🧪jest coverage report action from aaf5d9a

@chanwoo7
chanwoo7 merged commit 579e1fa into develop Aug 26, 2026
11 checks passed
@chanwoo7
chanwoo7 deleted the feat/product-wishlist-figma branch August 26, 2026 17:50
@codecov

codecov Bot commented Aug 26, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 90.00000% with 5 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...rc/features/user/services/user-wishlist.service.ts 90.32% 1 Missing and 2 partials ⚠️
src/features/user/repositories/user.repository.ts 81.81% 1 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

chanwoo7 added a commit that referenced this pull request Aug 27, 2026
feat(user): 찜 목록 카드 확장(매장별 필터·평점·할인율)과 매장별 그룹 조회
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant