feat(filter): GraphQL 전용 예외 필터 (P1-3)#117
Conversation
GraphQL 컨텍스트의 에러에 extensions 을 부착해 FE 에서 분기/추적 가능하게 한다. 협의 #4 (비파괴 확장) 대상. - 신규 GraphQLExceptionFilter (helper class) - format(exception, host) → GraphQLError with extensions - extensions: code / statusCode / requestId / operation / fieldName - HTTP status → GraphQL code 매핑 (mapStatusToCode) - 400 BAD_USER_INPUT / 401 UNAUTHENTICATED / 403 FORBIDDEN - 404 NOT_FOUND / 그 외 INTERNAL_SERVER_ERROR - txError 로 구조화 로그(LogContext.GRAPHQL) 기록 - HttpExceptionFilter: graphql context 시 GraphQLExceptionFilter 에 위임 - NestJS 글로벌 필터는 host type 별 1 회만 매칭되므로 단일 진입점에서 분기 - main.ts: GraphQLExceptionFilter 인스턴스를 HttpExceptionFilter 에 주입 FE 영향: 비파괴 확장. 기존 errors[].message 는 그대로, extensions 필드만 추가.
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9b854fa2ad
ℹ️ 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".
| this.logger.txError({ | ||
| userId, | ||
| requestId, | ||
| request: gqlRequest, | ||
| error: { statusCode: status, message, stack }, | ||
| processingTimeInMs: duration, | ||
| context: LogContext.GRAPHQL, | ||
| }); |
There was a problem hiding this comment.
Avoid double-logging GraphQL resolver errors
For GraphQL errors thrown from resolvers/services, the globally registered GqlLoggingInterceptor already records a txError in its tap.error path before rethrowing, and then this filter records another txError for the same request/error here. That means ordinary resolver failures will produce duplicate transaction-error logs with the same requestId/field, which can inflate error counts and confuse alerting; either the interceptor or this filter should own error logging for that path.
Useful? React with 👍 / 👎.
Coverage report
Test suite run success1191 tests passing in 143 suites. Report generated by 🧪jest coverage report action from 9b854fa |
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
fix(filter): GraphQL 에러 중복 로깅 제거 (#117 follow-up)
Summary
GraphQL 컨텍스트에서 발생한 에러에
extensions를 부착해 FE 에서 분기·추적이 가능하게 한다.NestJS 글로벌 필터는 host type 별로 1 회만 매칭되므로, 단일 진입점인
HttpExceptionFilter에서 graphql context 시 신규GraphQLExceptionFilter에 위임한다.FE 영향: 비파괴 확장 (협의 #4) — 기존
errors[].message응답 형태는 그대로,extensions필드만 추가.Scope
GraphQLExceptionFilter(src/global/filters/graphql-exception.filter.ts)format(exception, host): GraphQLError— HTTP 예외를 GraphQLError 로 변환하며 extensions 부착mapStatusToCode(status)— HTTP status → GraphQL code 매핑code/statusCode/requestId/operation/fieldNamerequestId는 incomingx-request-id헤더 우선, 없으면 신규 UUIDtxError로 구조화 로그 기록 (LogContext.GRAPHQL)HttpExceptionFilter수정GraphQLExceptionFilter의존성 추가catch진입 직후host.getType<GqlContextType>() === 'graphql'분기 →gqlFilter.format()결과 반환main.ts:GraphQLExceptionFilter인스턴스를 생성해HttpExceptionFilter에 주입graphql-exception.filter.spec.ts(16 cases — status→code 매핑 / 4xx 예외별 extensions / requestId / mutation / log)global-exception.filter.spec.ts— 위임 분기 케이스 갱신 (graphql → gqlFilter.format, rpc → super.catch)FE 협의 #4 — 액션 요청
GraphQL 에러 응답의
extensions에 다음 필드가 추가 된다 (기존 필드 제거 X, 비파괴 확장).Before / After
Before:
{ "errors": [ { "message": "Not Found" } ], "data": null }After:
{ "errors": [ { "message": "Not Found", "extensions": { "code": "NOT_FOUND", "statusCode": 404, "requestId": "9b1deb4d-...-43b6-9c4e-...", "operation": "query", "fieldName": "sellerProduct" } } ], "data": null }code 매핑표
BAD_USER_INPUTUNAUTHENTICATEDFORBIDDENNOT_FOUNDINTERNAL_SERVER_ERRORFE 액션 권장 (필수 아님)
extensions.code기반 분기 (예:UNAUTHENTICATED→ 재로그인 유도)extensions.requestId를 에러 UI/Sentry tag 등에 표시 → 백엔드 로그와 cross-referenceImpact
Test plan
graphql-exception.filter.spec(16 cases) 통과global-exception.filter.spec— 위임 분기 갱신, 기존 HTTP 케이스 그대로 통과