Skip to content

fix: 관리자 알림 이력·공개 카테고리의 null 필터 500 수정과 관리자 Query null 입력 게이트 - #484

Merged
chanwoo7 merged 2 commits into
developfrom
fix/admin-null-filters-sweep
Oct 4, 2026
Merged

chanwoo7 merged 2 commits into
developfrom
fix/admin-null-filters-sweep

Conversation

@chanwoo7

@chanwoo7 chanwoo7 commented Oct 4, 2026

Copy link
Copy Markdown
Member

배경

  • 관리자 웹의 '알림' 메뉴에서 "서버 오류가 발생했습니다"가 표시됩니다(운영 재현).
  • 관리자 FE는 '전체' 필터를 adminNotificationBroadcasts(input: { type: null, targetKind: null })처럼 null로 보냅니다. service가 null을 그대로 넘겨 Prisma where에 type: null이 들어갔고 Argument \type` must not be null`로 500이 났습니다.
  • fix: 관리자 매장·상품·배너 목록의 isActive null 필터를 미지정으로 처리 #434(매장·상품·배너 목록의 isActive null)에 이은 두 번째 같은 유형이라 관리자 Query 전체를 점검하고 재발을 막는 게이트를 추가했습니다.

변경

  • 알림 이력 목록의 type·targetKind를 ?? undefined로 정규화했습니다.
    • DTO AdminNotificationBroadcastListInput의 두 필드를 | null로 선언(정규화가 빠지면 tsc가 막음)
  • 공개 categories(input: { type: null })도 같은 이유로 500이라 함께 고쳤습니다(홈 칩·카테고리 진입 화면이 쓰는 조회).
  • input 객체를 받는 관리자 Query를 전부 점검했습니다.
    • 대상: Query 17개, nullable 입력 필드 70곳
    • 고친 곳: 2곳(알림 이력 type·targetKind)
    • 그대로 둔 곳: 68곳(limit·cursor 27, keyword 7, ID 필터 13, 기본값 false 불리언 4, 날짜 5, enum 필터 8은 repository가 truthy·일치 비교로 거름, 신고 status 1은 명시적 null이 '전체'인 의도된 계약, isActive 3은 #434에서 처리)
  • 회귀 게이트 src/test/admin-query-null-inputs.spec.ts를 추가했습니다.
    • 입력 공간: SDL의 admin 접두 Query 중 input 객체 인자를 받는 필드 전부(현재 17개, 개수 고정)
    • nullable 필드는 전부 null, 필수 필드는 최소 유효값, input 인자 자체가 null인 경우도 확인
    • AppModule(api) + main.ts와 같은 ValidationPipe·전역 필터 + 실DB·실Redis, 관리자 토큰으로 실행해 5xx·INTERNAL_ERROR가 없고 리졸버까지 닿는지 단언
  • createTestingModuleWithRealDb에 PrismaService override를 추가했습니다(AppModule 트리도 테스트 클라이언트를 쓰게).

테스트

  • 반증 실측 결과입니다.
    • 알림 정규화를 되돌리면 게이트 중 adminNotificationBroadcasts 1건만 INTERNAL_ERROR(500)로 실패하고 로그에 운영과 같은 Prisma 오류가 찍힘
    • #434의 isActive 3곳을 되돌리면 adminStores·adminProducts·adminBanners 3건 실패
    • categories 정규화를 되돌리면 새 케이스가 category_type must not be null로 실패
  • yarn validate:push가 통과했습니다(테스트 인프라 변경으로 full: 355 suites, 3722 tests).

플랜 대조

항목 상태
알림 이력 null 필터 정규화 + DTO | null 한 것
관리자 Query 전수 점검(점검·수정·보류 수와 사유) 한 것
SDL 기반 회귀 게이트(실앱·실DB) 한 것
공개 categories type null 500 한 것
리뷰 목록 cursor null → 400 안 한 것: 500이 아니고 FE 호출 방식 확인이 필요해 #483으로 기록

관리자 FE가 알림 이력 '전체' 필터를 type: null, targetKind: null로 보내면 service가 null을 그대로 repository로
넘겨 Prisma where에 type: null이 들어가 500(Argument `type` must not be null). 매장·상품·배너 isActive(#434)에 이은
두 번째 같은 유형이라, 이번에는 admin Query 전수를 SDL에서 읽어 실제 앱으로 보내는 게이트로 막는다.

- adminNotificationBroadcasts: type·targetKind를 ?? undefined로 정규화, DTO를 | null로 선언해 다음 누락은 tsc가 잡게 함
- 전수 점검: input 객체를 받는 admin Query 17개, nullable 입력 필드 70자리
  - 고침 2(알림 이력 type·targetKind)
  - 그대로 둠 68: 페이지 limit·cursor 27, keyword trim 7, ID parseOptionalId 13, 기본값 false 불리언 4, 날짜 toDate 5,
    repository가 truthy·일치 비교로 거르는 enum 8, 명시적 null = 전체인 신고 status 1, 이미 정규화된 isActive 3
- 게이트 src/test/admin-query-null-inputs.spec.ts
  - nullable 필드 전부 null, input 인자 자체 null 두 경우를 main.ts와 같은 파이프·필터·가드·실DB로 보내 5xx·INTERNAL_ERROR 없음,
    리졸버까지 닿아 data가 옴을 단언(17 + 16건)
  - 대상 수 17 고정, 입력 오류는 VALIDATION_FAILED(400)로 나가는지 대조, 판정기·입력 생성기 반증
  - 반증 실측: 알림 정규화를 되돌리면 adminNotificationBroadcasts 1건만 실패(운영과 같은 Prisma 오류),
    isActive 3자리를 되돌리면 adminStores·adminProducts·adminBanners 3건 실패
- createTestingModuleWithRealDb가 PrismaService를 override해 AppModule 트리도 실DB 테스트 클라이언트를 쓰게 함
- 회귀: 알림 service spec·DTO spec에 null 필터 케이스 추가
- 전수 점검 중 발견: categories(input: { type: null })이 category_type: null을 Prisma에 넘겨 500(홈 칩·카테고리 진입 화면이 쓰는 조회)
- service ?? undefined 정규화 + DTO | null
- 회귀: type null이면 전체(수정 전 'category_type must not be null'로 실패 확인)
@coderabbitai

coderabbitai Bot commented Oct 4, 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: Repository: CaQuick/caquick-be/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 16a454ba-23b4-4bff-a389-97a0271d618d

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
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

github-actions Bot commented Oct 4, 2026

Copy link
Copy Markdown

🩺 NestJS Doctor — 90/100 (Excellent)

진단 470건 (error 12).

Category error warning info
architecture 1 1 42
correctness 0 261 0
performance 0 30 28
schema 0 0 75
security 11 21 0
architecture / security 상위 항목
  • error architecture/architecture/no-manual-instantiation: Manual instantiation of 'OutboxRepository' detected. Use dependency injection instead.
  • 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.
  • warning security/security/require-guards-on-endpoints: Endpoint 'adminLogin' has no @UseGuards() at class or method level.
  • warning security/security/require-guards-on-endpoints: Endpoint 'adminRefresh' has no @UseGuards() at class or method level.
  • warning security/security/require-guards-on-endpoints: Endpoint 'adminLogout' has no @UseGuards() at class or method level.
  • warning security/security/require-guards-on-endpoints: Endpoint 'getJwks' has no @UseGuards() at class or method level.

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

@github-actions

github-actions Bot commented Oct 4, 2026

Copy link
Copy Markdown

🧹 knip — dead-code 리포트

Unused exported types (1)
전체 리포트
Unused exported types (1)
RateLimitPolicy  type  src/global/rate-limit/index.ts:4:8

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

@codecov

codecov Bot commented Oct 4, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@github-actions

github-actions Bot commented Oct 4, 2026

Copy link
Copy Markdown

Coverage report

St.❔
Category Percentage Covered / Total
🟢 Statements 97.87% 10040/10258
🟢 Branches 92.33% 3780/4094
🟢 Functions 97.4% 2020/2074
🟢 Lines 98.47% 9134/9276

Test suite run success

3723 tests passing in 355 suites.

Report generated by 🧪jest coverage report action from 62b8441

@chanwoo7
chanwoo7 merged commit 9c15e94 into develop Oct 4, 2026
13 checks passed
@chanwoo7
chanwoo7 deleted the fix/admin-null-filters-sweep branch October 4, 2026 10:27
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