Skip to content

feat(user): 알림센터 알림 탭 대응 — event·연관 정보 노출, 3개월 필터, 커서 전환 - #267

Merged
chanwoo7 merged 4 commits into
developfrom
feat/notification-center-alerts
Sep 1, 2026
Merged

feat(user): 알림센터 알림 탭 대응 — event·연관 정보 노출, 3개월 필터, 커서 전환#267
chanwoo7 merged 4 commits into
developfrom
feat/notification-center-alerts

Conversation

@chanwoo7

@chanwoo7 chanwoo7 commented Sep 1, 2026

Copy link
Copy Markdown
Member

배경

figma 알림센터(알림 탭) 화면 대응 1/4. 기존 myNotifications는 초기 스캐폴드 산물이라 화면이 요구하는 이벤트 라벨·서브라인·딥링크 정보가 없었다.

변경점

  • SDL 확장: NotificationItemevent(NotificationEvent enum 노출)·orderId·storeId·productId·reviewId·storeName·productName 추가. 서브라인 조립("[매장] '상품'…")은 FE 담당.
  • 키셋 커서 전환: offset → "<createdAtMs>:<id>" 커서(created_at·id desc, id 타이브레이크). FE 실사용 전이라 breaking 전환을 지금 수행.
  • 3개월 노출 필터: 목록·totalCount·viewerCounts.unreadNotificationCountcreated_at ≥ now-3개월 공통 적용(삭제 아님).
  • 문구 갱신: figma 톤("주문이 확정되었어요." 등). 주문번호 prefix는 유지(사용자 확정 — 표시 여부는 FE 판단).
  • 연관 ID 저장: 주문 상태 알림 생성 시 store_id·product_id 저장. 과거 row는 조회 시 order.items 폴백으로 보강(상품명은 주문 시점 스냅샷).
  • 시드 재구성: 4종 이벤트 + 3개월 경과 알림.

테스트

  • service 12케이스(커서 연속 조회·타이브레이크·잘못된 커서 거절·3개월 필터·직접 연결/주문 폴백 매핑·배지 일치)
  • 매퍼 helper 순수 단위 4케이스 / input 6케이스 / resolver 통합 2케이스 / order.repository 연관 ID 저장 검증
  • yarn validate 전체 통과 (210 suites / 1799 tests)

figma 알림센터(알림 탭) 화면 대응. 알림 항목이 이벤트 라벨(주문확정/제작완료/
픽업완료/리뷰 좋아요)과 "[매장] '상품'…" 서브라인, 딥링크를 구성할 수 있도록
기존 범용 myNotifications API를 확장한다. 하단 안내 문구("최근 3개월 내의
알림만 확인할 수 있어요")에 맞춰 노출 범위도 서버가 강제한다.

변경점
- SDL: NotificationItem에 event·orderId·storeId·productId·reviewId·
  storeName·productName 추가, NotificationEvent enum 노출
- 페이지네이션: offset → 키셋 커서("<createdAtMs>:<id>", created_at·id desc)
  전환. FE 실사용 전이라 breaking 전환을 지금 수행(레포 컨벤션 정합)
- 3개월 노출 필터: myNotifications 목록·totalCount와
  viewerCounts.unreadNotificationCount에 created_at >= now-3개월 공통 적용
  (삭제 아님 — 조회 필터만, 사용자 확정 정책)
- 문구 상수를 figma 톤으로 갱신("주문이 확정되었어요." 등). 주문번호 prefix는
  식별 필요 가능성에 대비해 유지(사용자 확정 — 표시 여부는 FE 판단)
- 주문 상태 알림 생성 시 store_id·product_id 저장(신규 row부터). 과거 row는
  조회 시 order.items 폴백으로 매장·상품 정보 보강, 상품명은 주문 시점 스냅샷
- 시드: 4종 이벤트 + 3개월 경과 알림으로 재구성

회귀 테스트
- service spec 12케이스(커서 연속 조회·타이브레이크, 잘못된 커서 거절,
  3개월 필터, 직접 연결·주문 폴백 매핑, 배지 수 일치)
- 매퍼 helper 순수 단위 4케이스, input spec 6케이스, resolver 통합 2케이스,
  order.repository spec에 연관 ID 저장 검증 추가
@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown

🩺 NestJS Doctor — 90/100 (Excellent)

진단 288건 (error 0).

Category error warning info
architecture 0 0 9
correctness 0 140 0
performance 0 24 16
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

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown

🧹 knip — dead-code 리포트

Duplicate exports (1)
전체 리포트
Duplicate exports (1)
KEYWORD_RANK_SNAPSHOT_SIZE|MAX_POPULAR_KEYWORDS_LIMIT  src/features/search/constants/search.constants.ts

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

@codecov

codecov Bot commented Sep 1, 2026

Copy link
Copy Markdown

Codecov Report

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

Files with missing lines Patch % Lines
...rc/features/order/repositories/order.repository.ts 50.00% 0 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

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

ℹ️ 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 +18 to +19
const productName =
row.product?.name ?? orderItem?.product_name_snapshot ?? 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 Prefer the order snapshot for order-notification product names

When a product is renamed after checkout, new order-status notifications expose the renamed product rather than the name captured by the order. updateOrderStatusBySeller now stores product_id on every such notification, so row.product?.name always wins over product_name_snapshot here; this contradicts the GraphQL contract that order notifications use the order-time snapshot. Select the snapshot whenever the notification is backed by an order, while retaining the live product name for directly linked review notifications.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

반영: a48c907 — 주문 연결 알림은 product 직접 연결이 있어도 product_name_snapshot 우선으로 수정. 주문 없는 알림(리뷰 좋아요)만 현재 상품명 사용. 매퍼 spec에 동시 존재 케이스 추가.

@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown

Coverage report

St.
Category Percentage Covered / Total
🟢 Statements 97.89% 5521/5640
🟢 Branches 94.08% 2210/2349
🟢 Functions 96.6% 1080/1118
🟢 Lines 98.47% 5017/5095

Test suite run success

1800 tests passing in 210 suites.

Report generated by 🧪jest coverage report action from df00c03

Codex 지적 반영: 주문 상태 알림에 product_id를 저장하면서 현재 상품명이
주문 시점 스냅샷을 덮어쓰게 된 문제. 주문 연결 알림은 직접 연결(product)이
있어도 order.items의 product_name_snapshot을 우선해 SDL 계약(주문 시점
스냅샷)을 지킨다. 주문이 없는 알림(리뷰 좋아요)만 현재 상품명을 쓴다.

- 매퍼 단위 spec에 "주문 연결 + 직접 연결 동시 존재 → 스냅샷 우선" 케이스 추가

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

ℹ️ 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 +117 to +121
const createdAtMs = Number(match[1]);
if (!Number.isSafeInteger(createdAtMs)) {
throw new BadRequestException(USER_NOTIFICATION_ERRORS.INVALID_CURSOR);
}
return { createdAt: new Date(createdAtMs), id: BigInt(match[2]) };

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 Reject cursor timestamps outside the Date range

A cursor such as 9000000000000000:1 passes Number.isSafeInteger, but JavaScript converts that timestamp to an Invalid Date because it exceeds the supported Date range. The invalid value then reaches the Prisma created_at filters and produces a Prisma validation/internal GraphQL error instead of the declared Invalid notification cursor bad-request response. Validate the constructed date with Number.isNaN(date.getTime()) before returning it.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

반영: Date 변환 후 getTime() NaN 검사 추가로 BadRequest 거부. spec에 9000000000000000:1 케이스 추가.

Codex 지적 반영: 안전 정수라도 Date 지원 범위(±8.64e15ms)를 넘는 커서
timestamp는 Invalid Date가 되어 Prisma 필터에서 내부 오류로 번진다.
new Date 변환 후 getTime() NaN 검사로 형식 오류(BadRequest)로 선제 거부.

- 커서 거절 spec에 "9000000000000000:1" 케이스 추가

@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: 05bcf0138b

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

if (Number.isNaN(createdAt.getTime())) {
throw new BadRequestException(USER_NOTIFICATION_ERRORS.INVALID_CURSOR);
}
return { createdAt, id: BigInt(match[2]) };

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 Reject cursor IDs outside the database range

A cursor such as 1700000000000:999999999999999999999999999999 passes the regex and is converted by BigInt without any bounds check, but the value cannot be represented by the database BigInt/UnsignedBigInt query parameter used in the id < cursor.id filter. This causes a Prisma connector/value-out-of-range error instead of the declared Invalid notification cursor bad-request response. Validate the ID against the supported database range before returning the parsed cursor.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

반영: 커서 id를 UNSIGNED BIGINT 상한(2^64-1)과 비교해 초과 시 BadRequest 거부. spec 케이스 추가.

Codex 지적 반영: 커서 id 세그먼트가 DB UNSIGNED BIGINT 상한(2^64-1)을
넘으면 Prisma 커넥터 범위 오류로 번진다. 파싱 시 상한 초과를 형식
오류(BadRequest)로 선제 거부한다.

- MAX_UNSIGNED_BIGINT 상수 추가(user.constants), 커서 거절 spec 케이스 추가
@chanwoo7
chanwoo7 merged commit b40f8a7 into develop Sep 1, 2026
10 checks passed
@chanwoo7
chanwoo7 deleted the feat/notification-center-alerts branch September 1, 2026 18:20
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