Skip to content

feat(product): 구매자 매장 상품 목록·카테고리 조회 API#158

Merged
chanwoo7 merged 5 commits into
developfrom
feat/store-detail-products
Jun 23, 2026
Merged

feat(product): 구매자 매장 상품 목록·카테고리 조회 API#158
chanwoo7 merged 5 commits into
developfrom
feat/store-detail-products

Conversation

@chanwoo7

Copy link
Copy Markdown
Member

Summary

구매자 매장 상품 탭 조회 API를 신설한다. 매장 상세 화면의 상품 영역(좌측 카테고리 사이드바 + 카테고리별 상품 카드 + 매장 내 검색)을 담당한다. store-detail 도메인 작업의 2단계.

Scope

  • 신규 Query (둘 다 개인화 없는 public 쿼리)
    • storeProducts(input: StoreProductsInput!): StoreProductConnection!
      • 활성 상품(+활성 매장)만, categoryId 필터 / search(상품명·태그 부분일치) / cursor 페이지네이션
      • 카드: 대표 이미지(sort_order 최소) thumbnailUrl, discountRate(정가·할인가 기반 계산), categoryIds(FE 섹션 그룹핑용)
    • storeProductCategories(storeId: ID!): [StoreProductCategory!]!
      • 매장이 보유한 활성 상품의 카테고리만(빈 카테고리 제외), productCount + sortOrder 정렬
  • product feature에 구매자 조회 레이어 신설(resolver/service/mapper/dto/types/constants). 기존 repository만 있던 상태에서 확장
  • ProductRepositorylistActiveProductsByStore·listStoreProductCategories 추가
  • CategoryType enum을 SDL에 정의(기존 Prisma enum이나 GraphQL 미노출이었음)

진행 상황

  • store-detail 도메인 2/3 (PR1 storeDetail은 머지 완료, 다음 PR3 후기 탭)
  • 상품 상세(storeProduct)는 store-detail spec에 화면이 없어 제외(별도 figma) — 합의된 Q1

Impact

  • 신규 조회 API 추가. 스키마 변경 없음(기존 product/category 모델만 사용)
  • 기존 동작/계약 변경 없음

Test plan

  • 단위(mapper): 할인율 경계(정상/null/정가이상/0), 카드 매핑(대표이미지·카테고리ids), 카테고리 매핑
  • 통합(service, real DB): 활성 상품 필터(비활성/삭제 제외) · 매장 비활성 시 빈 결과 · 대표이미지/할인율 · categoryId 필터 · search(상품명·태그) · 커서 hasMore/nextCursor · 카테고리 빈것 제외/productCount/정렬/비활성 상품 제외
  • 통합(resolver, real DB): 위임 검증
  • yarn validate 전체 통과: 165 suites / 1381 tests, lint·tsc·dto:check·arch:check 통과, 커버리지 임계 충족

- storeProducts(input): 활성 상품(+활성 매장)만, 카테고리/매장내 검색(상품명·태그)/커서 페이지네이션. 대표 이미지 thumbnail + 할인율 계산
- storeProductCategories(storeId): 매장 보유 활성 상품의 카테고리만(빈 카테고리 제외) + productCount + sort_order 정렬
- product feature에 구매자 조회 레이어 신설(resolver/service/mapper/dto/types/constants), ProductRepository에 listActiveProductsByStore·listStoreProductCategories 추가
- 개인화 없는 public 쿼리. 단위(mapper)·통합(service/resolver) 테스트 19건
@coderabbitai

coderabbitai Bot commented Jun 23, 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

Run ID: b97fb1a4-8cfd-4f87-9bcb-7d637727d7d2

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
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/store-detail-products

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

github-actions Bot commented Jun 23, 2026

Copy link
Copy Markdown

🩺 NestJS Doctor — 89/100 (Good)

진단 259건 (error 0).

Category error warning info
architecture 0 0 13
correctness 0 109 0
performance 0 24 16
schema 0 0 84
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

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 703b5ae235

ℹ️ 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".

by: ['category_id'],
where: {
deleted_at: null,
product: { store_id: storeId, is_active: true, deleted_at: null },

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Filter category list by active store

For an inactive or soft-deleted store that still has active products, this relation filter only checks the product itself, so the public storeProductCategories query still returns category names and productCount. That makes the sidebar inconsistent with storeProducts (which already requires store: { is_active: true, deleted_at: null }) and exposes catalog metadata for stores the buyer-facing store detail hides; include the same store active/deleted predicate here.

Useful? React with 👍 / 👎.

@codecov

codecov Bot commented Jun 23, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 98.27586% with 1 line in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...eatures/product/repositories/product.repository.ts 91.66% 0 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

@github-actions

github-actions Bot commented Jun 23, 2026

Copy link
Copy Markdown

Coverage report

St.
Category Percentage Covered / Total
🟢 Statements 97.48% 3983/4086
🟢 Branches 93.27% 1247/1337
🟢 Functions 94.8% 748/789
🟢 Lines 97.87% 3637/3716

Test suite run success

1386 tests passing in 165 suites.

Report generated by 🧪jest coverage report action from b778b39

비활성/삭제 매장이 활성 상품을 보유하면 storeProductCategories가 카테고리·productCount를 노출하던 불일치를 수정한다. groupBy 필터에 store active/deleted 술어를 추가해 storeProducts와 가시성을 일치시킨다. (Codex P2 반영)

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 24db098bc6

ℹ️ 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".

Comment on lines +777 to +778
where: { deleted_at: null },
select: { category_id: true },

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Filter product categories by visible categories

When a category is deactivated or soft-deleted while its product_category rows remain, this selection still returns that hidden category in categoryIds, even though storeProductCategories filters categories by is_active and deleted_at before rendering the sidebar. That makes the public product response inconsistent with the category list the frontend groups against; add the same category active/deleted predicate to this relation selection (and the categoryId relation filter above).

Useful? React with 👍 / 👎.

storeProductCategories는 카테고리를 is_active/deleted_at로 거르는데, storeProducts의 categoryIds와 categoryId 필터는 비활성/삭제 카테고리를 포함해 사이드바와 불일치했다. listActiveProductsByStore의 categoryId 필터와 product_categories select에 category active/deleted 술어를 추가한다. (Codex P2 :778 반영)

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e7efcdfd93

ℹ️ 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".

product_tags: {
some: {
deleted_at: null,
tag: { name: { contains: args.search } },

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Exclude soft-deleted tags from storefront search

When a storeProducts search matches through tags, this nested predicate only requires the product_tags row to be active; if the referenced tag has been soft-deleted, products still surface for that hidden tag name because the soft-delete extension does not inject filters into nested relation predicates. Add deleted_at: null to the tag condition so buyer-facing searches don't use removed tags.

Useful? React with 👍 / 👎.

search가 태그로 매칭될 때 tag.deleted_at을 확인하지 않아 삭제된 태그명으로도 상품이 검색되던 문제를 수정한다. product_tags.some.tag 조건에 deleted_at: null을 추가한다. (Codex P2 :759 반영)

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6ea5913ce5

ℹ️ 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".

Comment on lines +739 to +740
...(args.cursor ? { id: { lt: args.cursor } } : {}),
...(args.categoryId

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve zero-valued ID filters

When a client supplies categoryId: "0" (or cursor: "0"), parseId returns 0n, but these truthiness checks drop the corresponding filter before querying Prisma. That makes an invalid category filter behave like no filter and returns the store's whole product list (and a zero cursor restarts pagination), whereas passing the parsed value or rejecting non-positive IDs would return no rows/400.

Useful? React with 👍 / 👎.

parseId("0")=0n이 args.cursor/args.categoryId의 truthiness 체크에서 falsy로 떨어져, 잘못된 categoryId가 전체 목록을 반환하고 zero cursor가 페이지를 리셋하던 문제를 수정한다. 0n도 유효 인자로 다루도록 !== undefined로 분기한다. (Codex P2 :740 반영)
@chanwoo7 chanwoo7 merged commit e028054 into develop Jun 23, 2026
11 checks passed
@chanwoo7 chanwoo7 deleted the feat/store-detail-products branch June 23, 2026 19:33
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