Swagger와 @RestControllerAdvice 간섭 이슈 해결

Spring Boot 3.x 환경에서 Swagger(Springdoc)를 설정하다 보면, 전역 예외 처리를 담당하는 @RestControllerAdvice와 충돌해 Swagger UI가 제대로 표시되지 않는 경우가 있다. 이 글에서는 그 원인과 해결 과정을 정리한다.
1. 현상 (The Problem)
Swagger UI(http://localhost:8080/swagger-ui/index.html)에 접속하면 API 목록이 전혀 나타나지 않고 아래와 같은 에러가 발생한다.
에러 메시지
No API definition provided콘솔 로그
Resolved [jakarta.servlet.ServletException: Handler dispatch failed: java.lang.NoSuchMethodError: 'void org.springframework.web.method.ControllerAdviceBean.<init>(java.lang.Object)']
2. 사용 환경 (Environment)
- Spring Boot: 3.5.0
- Java: 17
- Springdoc:
springdoc-openapi-starter-webmvc-ui:2.5.0
3. 원인 분석 (Cause)
이 충돌의 핵심 원인은 응답 형식의 간섭이다.
- Swagger의 작동 방식 — Swagger(Springdoc)는
/v3/api-docs엔드포인트에서 API 명세를 JSON으로 받아 온 뒤 화면에 시각화한다. - @RestControllerAdvice의 개입 — 전역 예외 처리기는 모든
@RestController를 감시하다가 예외가 발생하면 직접 정의한 커스텀 에러 JSON을 반환한다. - 충돌 발생 — Swagger 내부 요청도 결국 Spring 컨트롤러 메커니즘을 거친다. 이때 예외 처리기가 끼어들어, Swagger가 기대하는 'API 명세 JSON' 대신 '커스텀 에러 JSON'을 돌려준다. 그래서 Swagger UI가 데이터를 파싱하지 못하고 오류를 띄운다.
4. 시도했던 방법들 (Failed Attempts)
흔히 다음 방법을 시도하지만, 근본적으로 해결되지 않거나 구현이 복잡해지는 경우가 많다.
basePackages지정:@RestControllerAdvice(basePackages = "...")assignableTypes활용- 특정 URI 패턴(
/swagger,/v3/api-docs)을 수동으로 제외하는 로직 추가
// 필터나 인터셉터 단계의 문제까지 대응하기 어렵고 유지보수성이 떨어진다.
if (uri.startsWith("/swagger") || uri.startsWith("/v3/api-docs")) {
return ResponseEntity.ok().build();
}5. 최종 해결책 (The Solution)
가장 확실하고 깔끔한 방법은 @RestControllerAdvice 클래스에 Swagger의 @Hidden 애노테이션을 붙이는 것이다.
import io.swagger.v3.oas.annotations.Hidden;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@Hidden // Swagger 분석 대상에서 완전히 제외
@RestControllerAdvice(annotations = {RestController.class})
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
// ... 예외 처리 로직 ...
}💡 왜 @Hidden인가?
@Hidden을 붙이면 Swagger가 해당 클래스나 메서드를 문서 생성·분석 과정에서 완전히 제외한다. 덕분에 예외 처리기가 Swagger 내부 동작에 끼어드는 것을 원천적으로 막을 수 있다.
📌 참고: 버전 호환성도 확인해 보자
콘솔에 찍힌
NoSuchMethodError: ControllerAdviceBean.<init>(Object)는 Spring Framework 6.2(Spring Boot 3.4 이상)에서ControllerAdviceBean생성자가 바뀌면서, springdoc 2.6 이하 버전과 호환되지 않아 생기는 에러이기도 하다. springdoc을 2.7.0 이상으로 올리면@Hidden없이도 해결되는 경우가 많으니 함께 확인해 보자.
6. 요약 (Summary)
| 항목 | 내용 |
|---|---|
| 핵심 문제 | @RestControllerAdvice가 Swagger의 내부 API 통신을 가로채면서 생기는 충돌 |
| 해결 방법 | @RestControllerAdvice 클래스에 @Hidden 애노테이션 추가 (springdoc 2.7.0 이상으로 업그레이드도 함께 검토) |
| 참고 사항 | Spring Boot 3.x + Springdoc 2.x 환경에서 꼭 확인해야 할 포인트 |
Swagger 설정 후 API 목록이 보이지 않는다면, 가장 먼저 전역 예외 처리기와의 충돌 여부와 springdoc 버전을 확인해 보는 것이 좋다.