Skip to content

feat(store): 매장 찜 토글 + 인기 매장 isWishlisted (옵셔널 JWT 가드)#146

Merged
chanwoo7 merged 3 commits into
developfrom
feat/store-wishlist
Jun 17, 2026
Merged

feat(store): 매장 찜 토글 + 인기 매장 isWishlisted (옵셔널 JWT 가드)#146
chanwoo7 merged 3 commits into
developfrom
feat/store-wishlist

Conversation

@chanwoo7

Copy link
Copy Markdown
Member

Summary

인기 매장 리스트의 매장 찜(하트) 기능인 PR3입니다(화면 01의 찜 토글). 매장 찜 추가/해제 mutation과, 인기 매장 리스트에 로그인 사용자의 찜 여부(isWishlisted)를 채우는 옵셔널 인증을 구현했습니다.

StoreWishlistItem 모델은 존재했으나 기능이 전혀 없었고, 상품 찜(WishlistItem)의 멱등 토글 패턴을 그대로 따랐습니다.

Scope

  • 매장 찜 토글(로그인 필요, 멱등): addStoreToWishlist(storeId) / removeStoreFromWishlist(storeId)
    • 추가는 upsert(soft-delete 복원), 해제는 soft-delete. 멱등(중복/없음 모두 true)
    • 존재하지 않거나 비활성 매장이면 404
  • OptionalJwtAuthGuard 신설(global/auth): 토큰 있으면 인증, 없거나 실패해도 통과 — 비로그인 허용 + 로그인 시 부가 정보용. 재사용 가능한 글로벌 가드
  • popularStores isWishlisted: 옵셔널 인증으로 로그인 시 페이지 매장의 찜 여부를 단일 IN 쿼리로 매핑(N+1 회피), 비로그인은 모두 false
  • 테스트 17개 신규: 찜 service(멱등·복원·404·비활성·BadRequest)·resolver 위임, 옵셔널 가드 단위, isWishlisted(로그인/비로그인)

진행 상황

전체 4개 PR 중 3번째:

Impact

  • DB: 스키마 변경 없음(기존 StoreWishlistItem 활용).
  • API: Mutation 2종 신규 + PopularStore.isWishlisted 필드 추가(GraphQL 비파괴적 확장).
  • 인증: OptionalJwtAuthGuard 신설(글로벌, 다른 public+개인화 쿼리에서 재사용 가능).
  • 의존성 추가 없음.

Test plan

  • yarn validate 전체 통과 — 156 suites / 1318 tests / 커버리지 임계 충족
  • PR3 신규: 찜 멱등(중복 추가·복원·없는 해제), 매장 404/비활성, 잘못된 id → BadRequest, 옵셔널 가드(user/false/null/에러), isWishlisted(로그인 true/false·비로그인 false)

후속

  • PR4 홈 전역 픽업 날짜/시간 슬롯(독립 feature)
  • (PR2에서 이월) 랭킹 캐시/배치 최적화, 위치 표기 규칙, 매장 상세 화면

@coderabbitai

coderabbitai Bot commented Jun 17, 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: fb9fc799-cbcd-4110-8f4d-a220cc66489a

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-wishlist

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 and usage tips.

@github-actions

github-actions Bot commented Jun 17, 2026

Copy link
Copy Markdown

🧹 knip — dead-code 리포트

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

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

@github-actions

github-actions Bot commented Jun 17, 2026

Copy link
Copy Markdown

🩺 NestJS Doctor — 88/100 (Good)

진단 254건 (error 0).

Category error warning info
architecture 0 0 13
correctness 0 105 0
performance 0 24 16
schema 0 0 83
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: 54344367d5

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

constructor(private readonly storeListingService: StoreListingService) {}

@Query('popularStores')
@UseGuards(OptionalJwtAuthGuard)

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 Register the optional auth guard

When popularStores is queried with a bearer token, this guard is the only code path that authenticates the request and populates req.user, but OptionalJwtAuthGuard is not registered in the Nest provider graph (checked AuthGlobalModule and the new StoreModule providers). Route-scoped guards supplied as classes are resolved from providers, so this guard is skipped/absent and @CurrentUser() stays undefined, causing isWishlisted to remain false for logged-in users until the guard is added to providers/exports (or this module's providers).

Useful? React with 👍 / 👎.

@codecov

codecov Bot commented Jun 17, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 96.55172% with 2 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...es/store/repositories/store-wishlist.repository.ts 90.90% 1 Missing ⚠️
...c/features/store/resolvers/store-query.resolver.ts 75.00% 0 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

@github-actions

github-actions Bot commented Jun 17, 2026

Copy link
Copy Markdown

Coverage report

St.
Category Percentage Covered / Total
🟢 Statements 97.4% 3782/3883
🟢 Branches 93.2% 1179/1265
🟢 Functions 94.53% 709/750
🟢 Lines 97.76% 3455/3534

Test suite run success

1319 tests passing in 156 suites.

Report generated by 🧪jest coverage report action from fd387f6

@chanwoo7

Copy link
Copy Markdown
Member Author

Codex P2 반영 (f80aaa5): OptionalJwtAuthGuard를 AuthGlobalModule providers/exports에 등록 — JwtAuthGuard와 동일 패턴. 미등록 시 가드 미주입으로 로그인 사용자의 isWishlisted가 false로 남는 문제 해소.

@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: f80aaa598f

ℹ️ 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 +25 to +29
await this.wishlistRepo.upsertStoreWishlist({
accountId,
storeId,
now: new Date(),
});

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 Restrict store wishlists to user accounts

When an authenticated SELLER or ADMIN calls addStoreToWishlist, JwtAuthGuard only verifies that the account is active and the resolver passes only the account id, so this service writes a store_wishlist_item for non-buyer accounts. Those rows are later included by aggregateWishlistCounts in the public popularity ranking, letting seller/admin accounts affect store rankings; the existing product wishlist path avoids this by calling requireActiveUser, so add an equivalent account-type/profile check before writing store wishlist rows.

Useful? React with 👍 / 👎.

@chanwoo7

Copy link
Copy Markdown
Member Author

Codex 리뷰 반영: (1) 가드 등록은 f80aaa5에서 이미 AuthGlobalModule providers/exports에 추가됨(JwtAuthGuard와 동일 패턴, resolver 미변경이라 코멘트가 라인 재매핑된 것). (2) USER 제한은 fd387f6 반영 — isActiveUserAccount로 SELLER/ADMIN 찜을 Forbidden 처리해 랭킹 무결성 보호.

@coderabbitai

coderabbitai Bot commented Jun 17, 2026

Copy link
Copy Markdown

Caution

Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted.

Error details
{"name":"HttpError","status":500,"request":{"method":"PATCH","url":"https://api.github.com/repos/CaQuick/caquick-be/issues/comments/4735382805","headers":{"accept":"application/vnd.github.v3+json","user-agent":"octokit.js/0.0.0-development octokit-core.js/7.0.6 Node.js/24","authorization":"token [REDACTED]","content-type":"application/json; charset=utf-8"},"body":{"body":"<!-- This is an auto-generated comment: summarize by coderabbit.ai -->\n<!-- This is an auto-generated comment: skip review by coderabbit.ai -->\n\n> [!IMPORTANT]\n> ## Review skipped\n> \n> Auto reviews are disabled on base/target branches other than the default branch.\n> \n> \n> \n> 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.\n> \n> <details>\n> <summary>⚙️ Run configuration</summary>\n> \n> **Configuration used**: Path: .coderabbit.yaml\n> \n> **Review profile**: CHILL\n> \n> **Plan**: Pro\n> \n> **Run ID**: `4f8947e7-699d-41a0-bc7d-dab1be0ee0ce`\n> \n> </details>\n> \n> You can disable this status message by setting the `reviews.review_status` to `false` in the CodeRabbit configuration file.\n> \n> Use the checkbox below for a quick retry:\n> - [ ] <!-- {\"checkboxId\": \"e9bb8d72-00e8-4f67-9cb2-caf3b22574fe\"} --> 🔍 Trigger review\n\n<!-- end of auto-generated comment: skip review by coderabbit.ai -->\n\n<!-- finishing_touch_checkbox_start -->\n\n<details>\n<summary>✨ Finishing Touches</summary>\n\n<details>\n<summary>🧪 Generate unit tests (beta)</summary>\n\n- [ ] <!-- {\"checkboxId\": \"f47ac10b-58cc-4372-a567-0e02b2c3d479\", \"radioGroupId\": \"utg-output-choice-group-unknown_comment_id\"} -->   Create PR with unit tests\n- [ ] <!-- {\"checkboxId\": \"6ba7b810-9dad-11d1-80b4-00c04fd430c8\", \"radioGroupId\": \"utg-output-choice-group-unknown_comment_id\"} -->   Commit unit tests in branch `feat/store-wishlist`\n\n</details>\n\n</details>\n\n<!-- finishing_touch_checkbox_end -->\n<!-- tips_start -->\n\n---\n\nThanks for using [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=CaQuick/caquick-be&utm_content=146)! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.\n\n<details>\n<summary>❤️ Share</summary>\n\n- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)\n- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)\n- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)\n- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)\n\n</details>\n\n\n<sub>Comment `@coderabbitai help` to get the list of available commands and usage tips.</sub>\n\n<!-- tips_end -->"},"request":{"retryCount":3,"signal":{},"retries":3,"retryAfter":16}}}

@chanwoo7 chanwoo7 merged commit 52e1d5a into develop Jun 17, 2026
11 checks passed
@chanwoo7 chanwoo7 deleted the feat/store-wishlist branch June 17, 2026 21:43
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