Swagger 세팅 시 트러블 슈팅 (feat. Spring Boot 3, Spring Security)

2025. 6. 7. 11:14spring/security

이번에는 Spring Boot 3이 기본적으로 적용됨에 따라, 이에 따른 Swagger 세팅 방식을 알아보고자 한다.

 

참조 링크

시도 1. (SpringDoc OpenAPI 관련 의존성 추가)

Spring Boot 3이 기본적으로 적용됨에 따라

implementation 'io.springfox:springfox-boot-starter:3.0.0’

기존 위 의존성으로는 Swagger 세팅이 정상적으로 되지 않았다.

https://adjh54.tistory.com/561 해당 링크를 참고하여 Spring Boot 3.x 버전과 호환되는 OpenAPI (Swagger) 문서 생성 라이브러리인 SpringDoc OpenAPI Starter WebMVC UI를 추가하였다.

<https://www.baeldung.com/java-spring-security-permit-swagger-ui>

 

SpringDoc OpenAPI Starter WebMVC UI

  • Spring Boot 3.x 버전과 호환되는 OpenAPI (Swagger) 문서 생성 라이브러리
  • Spring Boot 3.x에서 WebMVC 애플리케이션을 위한 OpenAPI 3 (Swagger) 지원을 제공
  • 자동으로 API 문서를 생성하고 Swagger UI를 통해 시각화

SwaggerConfig

@Configuration
public class SwaggerConfig {
    @Bean
    public OpenAPI openAPI() {
        Info info = new Info()
                .title("데모 프로젝트 API Document")
                .version("v0.0.1")
                .description("데모 프로젝트의 API 명세서입니다.").
        return new OpenAPI()
                .components(new Components())
                .info(info);
    }
}

application.yml

springdoc:
  packages-to-scan: com.siai.api
  default-consumes-media-type: application/json;charset=UTF-8
  default-produces-media-type: application/json;charset=UTF-8
  cache:
    disabled: true              # 캐시 사용 여부, true => Swagger UI와 API 문서가 매 요청마다 새로 생성하여 최신 정보 반영
  api-docs:
    path: /api-docs
    groups:
      enabled: true
  swagger-ui:
    disable-swagger-default-url: true   #디폴트 페이지 안나오게
    enabled: true               # Swagger UI 사용여부 : 접근 경로 => <http://localhost:8080/swagger-ui/index.html>
    path: /demo-ui.html         # Swagger UI 추가 접근 경로 => <http://localhost:8080/demo-ui.html>
    tags-sorter: alpha          # alpha: 알파벳 순 태그 정렬, method: HTTP Method 순 정렬
    operations-sorter: alpha    # alpha: 알파벳 순 태그 정렬, method: HTTP Method 순 정렬

 

설정 default 설명 사용예시

springdoc.packages-to-scan * 스캔할 패키지를 지정합니다. com.example.demo
springdoc.default-consumes-media-type application/json 기본 요청 미디어 타입을 지정합니다 application/json;charset=UTF-8
springdoc.default-produces-media-type / 기본 응답 미디어 타입을 지정합니다 application/json;charset=UTF-8
springdoc.cache.disabled false 캐시 사용 여부를 지정합니다 true
springdoc.api-docs.path /v3/api-docs API 문서 JSON 경로를 지정합니다 /api-docs
springdoc.api-docs.groups.enabled false API 문서 그룹 기능 활성화를 지정합니다 true
springdoc.swagger-ui.enabled true Swagger UI 사용 여부를 지정합니다 true
springdoc.swagger-ui.path /swagger-ui.html Swagger UI 접근 경로를 지정합니다 /swagger-ui
springdoc.swagger-ui.tags-sorter alpha 태그 정렬 방식을 지정합니다 alpha
springdoc.swagger-ui.operations-sorter alpha 작업 정렬 방식을 지정합니다 method
springdoc.paths-to-match /** 문서화할 API 경로 패턴을 지정합니다 /api/, /public/
springdoc.packages-to-exclude   문서화에서 제외할 패키지를 지정합니다 com.example.internal
springdoc.paths-to-exclude   문서화에서 제외할 API 경로를 지정합니다 /admin/*, /error

시도 2. Security Config에 ‘API 문서 JSON 경로’ 허용(permitAll)

하지만 위 흐름대로 적용한 결과, 

로 애초에 Swagger페이지가 열리지 않음을 확인하였다. (Spring에서는 에러가 없었음!)

UI자체가 열리지 않은 것 같아, 현재 spring security를 적용하면서 관련 추가 설정이 없는지 확인하였다.

“spring boot security swagger ui”키워드를 통해 검색해본 결과,

https://www.baeldung.com/java-spring-security-permit-swagger-ui ,

https://jong-bae.tistory.com/12

사이트가 나와 비슷하게 경험을 겪고 있는 것을 확인하였다.

위 사이트가 적용한 코드와 다른 부분이 아래임을 확인하였고,

(swagger에서 제공하는 ‘API 문서 JSON 경로’를 SecurityConfig에 인증을 거치지 않게끔 url을 별도로 추가하는 부분이 다름을 확인)

나도 SecurityConfig에 API 문서 JSON 경로를 인증을 거치지 않게 AUTH_WHITELIST 에 추가하였다.

@Configuration 
public class SecurityConfig {
   @Bean
    public WebSecurityCustomizer webSecurityCustomizer() {
        return (web) -> web.ignoring()
          .requestMatchers("/swagger-ui/**", "/v3/api-docs*/**");
    }
}
private static final String[] AUTH_WHITELIST = {
		"/api/auth/signup",
		"/api/auth/login",
		"/api/access-token/issue",
		"/swagger-ui/**",
		"/api-docs/**",
		"/swagger-resources/**",
		"/v3/api-docs"
	};

	@Bean
	public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
		
		...

		//권한 규칙 작성
		http.authorizeHttpRequests(authorize -> authorize
			.requestMatchers(AUTH_WHITELIST).permitAll()
			.requestMatchers("/api/users/admin").hasRole("ADMIN")
			//@PreAuthorization 사용 -> 모든 경로에 대한 인증처리는 Pass
			.anyRequest().authenticated()
		);

		return http.build();
	}

시도 3. @RestControllerAdvice가 있는 클래스(전역 에러 담당 클래스)에 annotations = {RestController.class} 추가

하지만 여전히 Swagger가 제대로 열리지 않았다.

‘java.lang.NoSuchMethodError: 'void org.springframework.web.method.ControllerAdviceBean.<init>(java.lang.Object)’ 에러를 뱉으면서,

위와 같이 올바르게 동작을 하지 않았다.

https://dev-meung.tistory.com/entry/해커톤-HY-THON-트러블슈팅-Swagger-500-에러-Failed-to-load-API-definition

사이트에서 말하길,

💡
@ControllerAdvice 애노테이션과Swagger 사이에 일어난 충돌이다.

@ControllerAdvice가 없다면Swagger는controller class를 가져와 API 정의를 분석한다.
@ControllerAdvice가 있다면Swagger 응답이 내가 응답을 통일해 둔 방식으로 변환되고, 
Swagger 웹 페이지는응답 구조를 인식할 수 없게 된다.

프로젝트의 패키지에서 @ControllerAdvice가 적용될 곳을 명시하면 
Swagger 응답이 변환되지 않아 충돌을 해결할 수 있다.

 

@Slf4j
@RestControllerAdvice
(annotations = {RestController.class}, 
basePackages = {"com.example.HyThon.web.controller"})
public class ExceptionAdvice extends ResponseEntityExceptionHandler {
    
    ...
}

따라서, 위와 같이 수정하면 된다고 하더라.


시도 4. @RestControllerAdvice 클래스에 @Hidden 추가

그러나, 여전히 안되는 것을 확인하였다.. (나 집 가고 싶어..)

https://velog.io/@sinryuji/Swagger와-RestControllerAdvice-충돌-문제 사이트에서

@Hidden 을 추가하면, 임시적으로 해결이 된다더라.

https://velog.io/@hotdog1029/Spring-Boot-3.x-버전-Swagger-설정 에서 말하길,

Springdoc도 Springfox와 마찬가지로
application 속성의 springdoc.packages-to-scan에 설정해놓은 패키지 내부의 클래스는
모두 자동으로 Swagger 문서에 등록된다.

만약 등록하고 싶지 않은 컨트롤러가 있다면 @Hidden 어노테이션을 이용하여 숨길 수 있다.

 

해결 완료 !


다른 해결 방안. application.yml에 springdoc.override-with-generic-response: false 추가

흠, 결과적으로는 해결이 되었으나, 여전히 @Hidden 으로 임시적으로 해결된 것이지, 좀 더 나은 해결책이 없는지 찾아보았다.

https://velog.io/@jyedev/swagger-500-에러-해결 을 참조하니,

springdoc.override-with-generic-response 는 일반화된 응답 사용 여부 결정하는 속성으로

false인 경우 응답이 자동으로 변환하지 않도록 제한하기 때문에 @RestControllerAdvice와 swagger의

충돌을 방지할 수 있다더라. 

드디어 올바르게 적용 완료 ! 

 

Spring boot3이 업로드 됨에 따라, jwt로그인 방식(이전 글 내용 참조)도 swagger도 세팅에 시간을 많이 잡았다.

버전이 업로드됨에 따라 그에 맞는 템플릿 코드를 만들어 두면 좋은 것은 사실이니, 앞으로 잘 써먹어야겠다..