Skip to content

fix: 릴리즈 리뷰 반영 (SDL 동작 단정 전수 정정) - #295

Merged
chanwoo7 merged 2 commits into
developfrom
fix/release-review-doc-claims
Sep 12, 2026
Merged

fix: 릴리즈 리뷰 반영 (SDL 동작 단정 전수 정정)#295
chanwoo7 merged 2 commits into
developfrom
fix/release-review-doc-claims

Conversation

@chanwoo7

@chanwoo7 chanwoo7 commented Sep 12, 2026

Copy link
Copy Markdown
Member

배경

릴리즈 PR #289의 Codex 지적 13건을 분류한 결과 12건이 같은 유형이었다 — "SDL description이 코드가 보장하지 않는 동작을 단정한다". 유일한 예외인 첫 지적(seller 대화 커서 정렬 불일치)만 실제 코드 버그였고 #290에서 해소했다.

그 12건 안에서도 하위 유형이 반복됐다: sortOrder 의미 2회, null 배타 열거 2회, 본문/링크 필드 배타성 2회. 지적된 자리만 막고 유형을 안 훑은 게 라운드가 늘어난 원인이라, CLAUDE.md 절차대로 개별 대응을 멈추고 전수 대조로 전환했다.

대조 방법

SDL description 1,352건에서 검증 가능한 동작 단정 376건을 추출해 코드와 대조했다. 검출기는 양극성 probe(잡혀야 할 자리 / 안 잡혀야 할 자리)로 자체 검증한 뒤 결과를 인용했다 — 초기 시도에서 정규식 이스케이프 오류로 정상 자리까지 전부 위반으로 찍힌 적이 있어, "0건"이든 "전건"이든 검출기부터 반증했다.

변경

지적 2건 직접 반영

  • WriteReviewInput — "이미지 최대 10장 또는 영상 1개"는 배타로 읽히지만 validateMedia는 IMAGE/VIDEO를 독립적으로 센다. 혼합 11개까지 유효.
  • WriteReviewMediaInput.sortOrderwriteReviewsort_order: i(배열 인덱스)로 저장해 입력값을 버린다. 값을 존중하도록 바꾸면 중복·결번 입력의 정규화 규칙을 새로 정해야 해서 릴리즈 범위를 넘는다. 배열 순서가 곧 노출 순서라는 현재 계약을 명시하는 쪽을 택했다.

전수 대조로 추가 발견 17건

유형 건수 내용
limit 상한 미기재 16 SDL 입력 31개 중 16개가 @Max를 안 적고 있었다. 상한은 DTO에만 있어 SDL에서 안 보이는데 초과 시 ValidationPipe가 BAD_REQUEST를 낸다
sortOrder 기본값 2 SellerCreateFaqTopicInput(모호), SellerCreateBannerInput(누락). 실제로는 둘 다 ?? 0이라 맨 뒤가 아니다
statusHistories 정렬 1 구매자 상세는 changed_at asc, 판매자 상세는 desc로 서로 반대인데 판매자 쪽만 "최신순"이 적혀 있었다

limit 16건 + sortOrder 2건 + 정렬 1건 = 19건이나, MyReviewableOrderItemsInput/MyReviewsInput이 같은 파일이라 파일 수는 13개.

반대 방향 확인도 했다 — SellerAddProductImageInput.sortOrder?? count라 "맨 뒤에 붙는다"가 정확해서 그대로 뒀다.

대조했으나 코드와 일치해 손대지 않은 것

  • 스냅샷/시점 단정 59건 — 전부 *_snapshot 컬럼을 읽는 게 맞았다 (NotificationItem.productName의 "주문 알림만 스냅샷" 분기까지 일치)
  • null 배타 열거 2건 — Codex가 짚은 그 2건이 전부였고 이미 수정됨
  • 정렬 단정 62건 / 배타·택일 12건 — orderBy와 일치

기타

docs:check BASELINE을 현재 수치로 갱신했다. #290~#294 머지로 요소가 늘어(input 타입 72→73, input 필드 227→229, 출력 필드 603→618) 스냅샷이 낡아 있었다. 스크립트 주석이 "커버리지가 바뀌면 BASELINE도 함께 갱신한다"고 정해 둔 대로다.

검증

  • yarn validate 통과 — 225 suites / 1875 tests
  • 전체 스키마 buildSchema 통과 (42개 파일, 232개 타입) — 파일 단위 파싱이 놓치는 타입 누락 없음
  • docs:check 8개 카테고리 전부 100% 유지

동작 변경 없음. SDL description과 그로부터 생성되는 타입 주석만 바뀐다. 테스트를 추가하지 않은 이유도 같다.

2차 반영 (Codex 리뷰 2건)

둘 다 이 PR에서 내가 여러 자리에 동시에 퍼뜨린 문구라, 지적된 자리가 아니라 문구 전체를 훑었다.

지적 확인 전수 결과
BAD_REQUEST는 FE가 받는 코드가 아니다 graphql-exception.filter.ts:21이 400 → BAD_USER_INPUT 매핑 32건 교체 — 이 PR에서 넣은 16건 + 기존 16건. NOT_FOUND·FORBIDDEN은 매핑과 이름이 같아 유지
(맨 뒤가 아니다)는 보장 못 한다 FAQ·배너는 [sort_order asc, id asc]라 기존이 전부 0이면 새 항목이 맨 뒤. @Min 없어 음수도 허용 5건 재작성 — 같은 문구였지만 실제 정렬은 3종(옵션 3종 tie-break 없음 / FAQ id asc / 배너 구매자 조회 1건 선택)

반복 수정으로 붙은 군더더기도 함께 걷어냈다: (400) 32건, WriteReviewMediaInput 타입·필드 설명 중복, 3줄짜리 sortOrder 설명 5건 → 기존 하우스 스타일(한 줄)로.

별건 발견 (이 PR 범위 밖): ConflictException(409)이 4곳에서 던져지는데 409가 STATUS_TO_CODE에 없어 INTERNAL_SERVER_ERROR로 나간다. FE가 "이미 리뷰함"과 서버 장애를 구분할 수 없다. 릴리즈 중인 문서 PR에서 에러 계약을 바꾸지 않고 별도 이슈로 올린다.

최종 변경 규모: 22개 파일, +46 / −44.

릴리즈 PR #289의 Codex 지적 13건 중 12건이 같은 유형이었다 —
"SDL description이 코드가 보장하지 않는 동작을 단정한다".
지적된 자리만 막는 대응을 멈추고 해당 유형의 입력 공간을 전수 대조했다.

지적 2건 직접 반영:
- WriteReviewInput: "이미지 최대 10장 또는 영상 1개"는 배타로 읽히지만
  validateMedia는 IMAGE/VIDEO를 독립적으로 세므로 혼합 11개까지 유효하다.
- WriteReviewMediaInput.sortOrder: writeReview가 `sort_order: i`(배열 인덱스)로
  저장해 입력값을 버린다. 동작 변경은 릴리즈 범위 밖이라 계약을 명시하는 쪽으로 정정.

전수 대조로 추가 발견:
- sortOrder 미지정 기본값 2건 — SellerCreateFaqTopicInput("서버가 정한 기본값"이라
  모호), SellerCreateBannerInput(누락). 실제로는 둘 다 `?? 0`이라 맨 뒤가 아니다.
  SellerAddProductImageInput은 `?? count`라 "맨 뒤" 서술이 정확해 유지.
- limit 상한 16건 — SDL 입력 31개 중 16개가 @max를 안 적고 있었다. 상한은 DTO에만
  있어 SDL에서 보이지 않는데 초과하면 ValidationPipe가 BAD_REQUEST를 낸다.
  "기본 N, 1~M만 허용하며 벗어나면 BAD_REQUEST"로 통일.
- statusHistories 정렬 1건 — 구매자 상세는 changed_at asc, 판매자 상세는 desc로
  서로 반대인데 판매자 쪽만 "최신순"이 적혀 있었다. 구매자 쪽에 방향을 명시.

대조했으나 코드와 일치해 손대지 않은 것:
스냅샷/시점 단정 59건, null 배타 열거 2건, 정렬 단정 62건, 배타/택일 12건.

docs:check BASELINE을 현재 수치로 갱신(#290~#294 머지로 요소가 늘어 스냅샷이
낡아 있었다). 동작 변경 없음 — SDL description과 자동생성 타입 주석만 바뀐다.
@coderabbitai

coderabbitai Bot commented Sep 12, 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: Advanced

Run ID: 02e1b546-6bbe-42a1-96e1-8ba795a4866a

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

🩺 NestJS Doctor — 90/100 (Excellent)

진단 313건 (error 0).

Category error warning info
architecture 0 0 9
correctness 0 161 0
performance 0 27 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

🧹 knip — dead-code 리포트

Unused dependencies (1)
Duplicate exports (1)
전체 리포트
Unused dependencies (1)
graphql-ws  package.json:65:6
Duplicate exports (1)
KEYWORD_RANK_SNAPSHOT_SIZE|MAX_POPULAR_KEYWORDS_LIMIT  src/features/search/constants/search.constants.ts

청소 후보(오탐 가능) · 기준 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: 9ea195e553

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

"""EVENT 카테고리 ID. 비우면 '전체' 칩(필터 미적용)."""
categoryId: ID
"""이미지 수(3열 그리드 9개 단위figma 명세 기준)."""
"""이미지 수(기본 9 — 3열 그리드 9개 단위, figma 명세 기준). 1~30만 허용하며 벗어나면 BAD_REQUEST."""

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 Document the actual GraphQL error code

When a client supplies an out-of-range limit, the ValidationPipe raises an HTTP 400 exception, but src/global/filters/graphql-exception.filter.ts maps that status to GraphQL extensions.code = BAD_USER_INPUT, not BAD_REQUEST. The same new wording appears on every limit description changed here, so generated schema documentation directs clients to handle an error code they will never receive; use BAD_USER_INPUT or explicitly describe statusCode: 400 instead.

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.

반영: graphql-exception.filter.ts:21HttpStatus.BAD_REQUEST → 'BAD_USER_INPUT'으로 매핑한다. FE가 받는 extensions.code는 BAD_USER_INPUT이므로 지적대로 SDL 문구가 틀렸다.

같은 유형 반복이라 자리별 대응을 멈추고 SDL 전체의 에러 코드 표기를 전수 대조했다.

  • BAD_REQUEST 32건 — 이번 PR에서 내가 넣은 16건 + 기존에 이미 있던 16건. 전부 BAD_USER_INPUT(400)으로 교체. statusCode를 괄호에 붙여 둘을 혼동하지 않게 했다.
  • NOT_FOUND 56건 · FORBIDDEN 50건 — 매핑 테이블과 이름이 같아 정확. 그대로 둠.
  • 매핑에 없는 status를 쓰는 곳이 있는지도 확인했다 → 아래 별건 보고.

별건(이 PR 범위 밖, 문서가 아니라 동작 문제): ConflictException(409)이 4곳에서 던져지는데(user-profile.service.ts:53,88 닉네임 중복 · account.repository.ts:105 · user-review.service.ts:113 리뷰 중복) 409는 STATUS_TO_CODE에 없어 mapStatusToCodeINTERNAL_SERVER_ERROR를 돌려준다. FE가 "이미 리뷰함"과 서버 장애를 구분할 수 없다. 릴리즈 중인 문서 PR에서 에러 계약을 바꾸는 건 부적절해서 별도 이슈로 올린다.

"""FAQ 답변 본문(HTML)."""
answerHtml: String!
"""노출 순서. 미지정 시 서버가 정한 기본값이 들어간다."""
"""노출 순서. 미지정 시 0이 들어간다(맨 뒤가 아니다)."""

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 Remove the unsupported “not last” guarantee

When existing FAQ topics also have the default sortOrder of 0, omitting this field gives the new topic 0 and SellerRepository.listFaqTopics orders ties by ascending id, placing the newly created topic last. Negative sort orders are also accepted, so 0 cannot generally guarantee a non-final position. The equivalent banner wording is likewise unsupported; describe only the actual default value rather than asserting (맨 뒤가 아니다).

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.

반영: 지적이 맞다. listFaqTopicsorderBy: [{ sort_order: 'asc' }, { id: 'asc' }]라 기존 항목이 전부 0이면 새 항목이 맨 뒤로 간다. @IsInt()만 있고 @Min이 없어 음수도 허용되므로 0이 비최종 위치를 보장하지도 못한다.

(맨 뒤가 아니다)는 앞선 라운드에서 내가 쓴 표현인데, 이번 PR에서 2자리에 더 퍼뜨렸다. 전수로 걷어내고 목록별 실제 정렬에 맞춰 다시 썼다 — 5자리가 같은 문구였지만 뒤의 정렬은 3종으로 달랐다.

자리 실제 정렬 새 문구
옵션 그룹 · 옵션 아이템 · 커스텀 문구 토큰 sort_order asc (동률 tie-break 없음) 동률 순서는 보장하지 않는다고 명시
FAQ 토픽 [sort_order asc, id asc] 기존이 모두 0이면 맨 뒤에 온다
배너 구매자 조회가 [sort_order asc, id asc]로 1건만 선택 기존이 모두 0이면 새 배너는 뽑히지 않는다

배너는 판매자 목록(listBanners)이 id desc라 sort_order를 아예 안 쓴다는 것도 이번에 확인해서, 구매자 노출 기준임을 문구에 박았다.

SellerAddProductImageInput.sortOrder?? count라 "맨 뒤에 붙는다"가 실제로 정확해 그대로 뒀다.

@codecov

codecov Bot commented Sep 12, 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 Sep 12, 2026

Copy link
Copy Markdown

Coverage report

St.
Category Percentage Covered / Total
🟢 Statements 97.62% 5915/6059
🟢 Branches 93.15% 2324/2495
🟢 Functions 96.47% 1176/1219
🟢 Lines 98.2% 5388/5487

Test suite run success

1875 tests passing in 225 suites.

Report generated by 🧪jest coverage report action from 558c6a0

Codex 2차 지적 2건. 둘 다 이 PR에서 내가 여러 자리에 동시에 퍼뜨린 문구라
지적된 자리가 아니라 문구 전체를 훑었다.

- BAD_REQUEST → BAD_USER_INPUT (32건): graphql-exception.filter가 400을
  BAD_USER_INPUT으로 매핑하므로 FE가 받는 extensions.code와 달랐다.
  이 PR에서 넣은 16건 외에 기존 16건이 더 있었다. NOT_FOUND·FORBIDDEN은
  매핑과 이름이 같아 그대로.
- "(맨 뒤가 아니다)" 제거 (5건): FAQ·배너는 [sort_order asc, id asc]라
  기존이 전부 0이면 새 항목이 맨 뒤로 가고, @min이 없어 음수도 허용된다.
  5자리가 같은 문구였지만 실제 정렬은 3종이라 각각에 맞춰 다시 썼다.
  (옵션 3종: tie-break 없음 / FAQ: id asc / 배너: 구매자 조회 1건 선택)

하우스 스타일에 맞춰 장황한 문구도 걷어냈다 — 반복 수정으로 붙은 (400) 32건,
WriteReviewMediaInput 타입·필드 설명 중복, 3줄짜리 sortOrder 설명 5건.
@chanwoo7
chanwoo7 merged commit 3dbf99b into develop Sep 12, 2026
13 of 14 checks passed
@chanwoo7
chanwoo7 deleted the fix/release-review-doc-claims branch September 12, 2026 14:52
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