fix: 하네스 제거 및 keycloak 문서 보강
This commit is contained in:
@@ -1 +0,0 @@
|
||||
../../vault/10-projects/errors/responseentityexceptionhandler-ambiguous-exception-handler-2026-06-02.md
|
||||
@@ -0,0 +1,69 @@
|
||||
---
|
||||
title: error / responseentityexceptionhandler-ambiguous-exception-handler-2026-06-02
|
||||
source_type: error-note
|
||||
status: raw
|
||||
related_branches: [feature-api-contract-baseline]
|
||||
related_projects: [ca-skeleton]
|
||||
tags: [error, ca-skeleton, spring-mvc, exception-handler, ResponseEntityExceptionHandler, api-contract]
|
||||
created: 2026-06-02
|
||||
status_label: resolved
|
||||
---
|
||||
|
||||
# error: responseentityexceptionhandler-ambiguous-exception-handler-2026-06-02
|
||||
|
||||
> Layer: `raw/errors/` — 실제 발생한 오류 / 막힘 / 트러블슈팅의 원석.
|
||||
|
||||
## Parent / 부모
|
||||
|
||||
- [[raw/branch-notes/feature-api-contract-baseline]] — D8 (413 payload-too-large) 핸들러 추가 중 발생.
|
||||
|
||||
## 증상
|
||||
|
||||
`feature-api-contract-baseline` D8 구현으로 `GlobalExceptionHandler` 에 `@ExceptionHandler(MaxUploadSizeExceededException.class) handlePayloadTooLarge(...)` 를 추가하자, `adapter-web` 의 **모든** MockMvc standalone 테스트가 `setUp()` 의 `.build()` 에서 `IllegalStateException` 으로 실패. 기존에 통과하던 `EnvelopeMetaIntegrationTest` 까지 동반 실패.
|
||||
|
||||
메시지:
|
||||
|
||||
```
|
||||
java.lang.IllegalStateException: Ambiguous @ExceptionHandler method mapped for
|
||||
[ExceptionHandler{exceptionType=org.springframework.web.multipart.MaxUploadSizeExceededException, mediaType=*/*}]:
|
||||
{public ... GlobalExceptionHandler.handlePayloadTooLarge(MaxUploadSizeExceededException),
|
||||
public final ... ResponseEntityExceptionHandler.handleException(Exception, WebRequest) ...}
|
||||
```
|
||||
|
||||
## 근본 원인 / Root cause
|
||||
|
||||
`GlobalExceptionHandler extends ResponseEntityExceptionHandler`. Spring 의 `ResponseEntityExceptionHandler.handleException(...)` 는 `@ExceptionHandler({ ... MaxUploadSizeExceededException.class, ... })` 우산(umbrella) 핸들러로, `MaxUploadSizeExceededException` 을 이미 자신의 매핑 대상으로 *선점* 한다. 같은 예외 타입에 대해 서브클래스가 별도 `@ExceptionHandler` 메서드를 추가하면 동일 (exceptionType, mediaType=*/*) 키에 두 핸들러가 등록되어 매핑이 모호(ambiguous)해지고, 핸들러 advice 등록 시점(`.build()` / 컨텍스트 기동)에 즉시 실패한다.
|
||||
|
||||
핵심: `ResponseEntityExceptionHandler` 가 *이미 다루는* 예외군(405/406/415/413 multipart/`HttpMessageNotReadable` 등)은 `@ExceptionHandler` 신규 메서드로 가로채면 안 되고, 대응하는 **protected `handleXxx(...)` 메서드를 override** 해야 한다.
|
||||
|
||||
## 해결 / Resolution
|
||||
|
||||
`@ExceptionHandler(MaxUploadSizeExceededException.class)` 메서드를 제거하고 protected 훅을 override:
|
||||
|
||||
```java
|
||||
@Override
|
||||
protected ResponseEntity<Object> handleMaxUploadSizeExceededException(
|
||||
MaxUploadSizeExceededException ex, HttpHeaders headers, HttpStatusCode status, WebRequest request) {
|
||||
return new ResponseEntity<>(
|
||||
ErrorResponseFactory.body(OperationalError.PAYLOAD_TOO_LARGE, "...", null),
|
||||
HttpStatusCode.valueOf(OperationalError.PAYLOAD_TOO_LARGE.httpStatus()));
|
||||
}
|
||||
```
|
||||
|
||||
406(`handleHttpMediaTypeNotAcceptable`) 도 동일하게 override 로 추가. 405 의 `Allow` 헤더 누락 수정 역시 기존 override 안에서 `responseHeaders.setAllow(...)` 로 처리. 수정 후 `:adapter-web:test` PASS.
|
||||
|
||||
## 회고 / Lessons
|
||||
|
||||
- **`ResponseEntityExceptionHandler` 를 상속하면, 그가 이미 선언한 예외는 `@ExceptionHandler` 가 아니라 protected override 로만 커스터마이즈한다.** 새 `@ExceptionHandler` 는 그 우산이 다루지 *않는* 예외(`MappingException`, `ConstraintViolationException`, 도메인 예외 등)에만 쓴다.
|
||||
- 실패가 한 테스트가 아니라 advice 를 쓰는 *모든* standalone MockMvc 테스트에서 `.build()` 시점에 터지는 건, 런타임 요청 처리 이전 *핸들러 등록* 단계의 정합성 문제라는 신호.
|
||||
- 어떤 예외가 우산에 포함되는지는 Spring 버전마다 늘어난다(예: `MaxUploadSizeExceededException`, `ErrorResponseException`, `HandlerMethodValidationException`). 신규 transport 핸들러 추가 시 먼저 `ResponseEntityExceptionHandler` 의 `@ExceptionHandler` 목록을 확인.
|
||||
|
||||
## 재발 가능성
|
||||
|
||||
- file-resource 브랜치가 multipart 413(`UPLOAD_SIZE_EXCEEDED`)을 추가할 때 동일 함정 가능 — override 를 더 구체화하거나 별도 advice 를 `@Order` 로 앞세우는 방식 필요.
|
||||
|
||||
## Sources / 근거
|
||||
|
||||
- `src/adapter-web/src/main/java/dev/caskeleton/adapter/web/error/GlobalExceptionHandler.java`
|
||||
- `src/adapter-web/src/test/java/dev/caskeleton/adapter/web/error/TransportErrorHandlingTest.java`
|
||||
- Spring `org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler`
|
||||
Reference in New Issue
Block a user