[Swagger] Spring eGovFramework 환경에서 Swagger 적용하기

EUN JY·2024년 3월 25일
1

JAVA

목록 보기
7/7
post-thumbnail

0. Swagger

  • Swagger 는 Java, Spring 전용 프레임워크가 아닌 OAS(OpenAPI Specification)를 위한 프레임워크
  • Spring 환경에서 Swagger 를 사용하려면 Swagger UI의 설정, Swagger 어노테이션으로 API 메타데이터를 읽는 과정 등을 직접 구현해줘야 함
    • 이런 작업을 대신해주는 라이브러리, SpringDoc 또는 SpringFox 를 사용
    • SpringDoc과 SpringFox : Swagger UI를 만들고, Swagger 어노테이션을 유저가 쓰기 쉽게 제공하는 라이브러리
    • SpringFox는 2020년 이후로 업데이트를 멈춘 반면, SpringDoc은 최근까지도 계속 업데이트를 진행
  • 1-2. 번 + 1-2-2. 번 Swagger Springfox 세팅으로 진행하였음

1. pom.xml

  • pom.xml 파일을 계속 수정하므로, 아래 절차를 모두 수행하고 나는 오류를 확인해야 함
  • Maven Clean > Maven Install > Update Project > Project Clean > Build

1-1. Swagger Springdoc 세팅

  • Springdoc 사용을 위해 아래 의존성 추가
implementation("org.springdoc:springdoc-openapi-ui:1.6.11")
<dependency>
    <groupId>org.springdoc</groupId>
    <artifactId>springdoc-openapi-ui</artifactId>
    <version>1.6.11</version>
</dependency>
  • Spring Boot 3부터는 위의 의존성 라이브러리 추가 후 Swagger UI 접속 시, 404 에러가 발생하기 때문에 다음 코드를 추가해야 함
implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.0.2")
<dependency>
    <groupId>org.springdoc</groupId>
    <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
    <version>2.0.2</version>
</dependency>

1-2. Swagger Springfox 세팅

  • Springfox 사용을 위해 아래 의존성 추가
		<!-- swagger -->
		<dependency>
			<groupId>io.springfox</groupId>
			<artifactId>springfox-swagger-ui</artifactId>
			<version>2.9.2</version>
		</dependency>
		<dependency>
			<groupId>io.springfox</groupId>
			<artifactId>springfox-swagger2</artifactId>
			<version>2.9.2</version>
		</dependency>

1-2-1. Swagger Springfox 세팅

  • 위의 의존성으로 되지 않으면 아래 의존성으로 변경
        <!-- swagger -->
		<dependency>
			<groupId>io.springfox</groupId>
			<artifactId>springfox-swagger-ui</artifactId>
			<version>3.0.0</version>
		</dependency>
		<dependency>
			<groupId>io.springfox</groupId>
			<artifactId>springfox-swagger2</artifactId>
			<version>3.0.0</version>
		</dependency>
		<dependency>
			<groupId>io.springfox</groupId>
			<artifactId>springfox-boot-starter</artifactId>
			<version>3.0.0</version>
		</dependency>

1-2-2. java.lang.NoClassDefFoundError: com/fasterxml/jackson/databind/ObjectMapper

  • pom.xml 에 아래 내용 추가 (오류 발생 시에만 진행)
		<dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.9.9</version>
		</dependency>
		<dependency>
			<groupId>com.fasterxml.jackson.core</groupId>
			<artifactId>jackson-databind</artifactId>
			<version>2.9.9</version>
		</dependency>
	    <dependency>
	        <groupId>com.fasterxml.jackson.core</groupId>
	        <artifactId>jackson-annotations</artifactId>
	        <version>2.9.9</version>
	        <scope>compile</scope>
	    </dependency>
	    <dependency>
	        <groupId>com.fasterxml</groupId>
	        <artifactId>classmate</artifactId>
	        <version>1.5.1</version>
	        <scope>compile</scope>
	    </dependency>
  • 5-1. 5-2. 7-3. 등의 오류가 발생하면 버전 문제일 가능성이 큼
    • <version>2.9.9</version> -> <version>2.0.0</version> 으로 변경
    • Swagger 버전도 2.9.2 > 2.5.0 로 변경!!

1-2-3. Caused by: java.lang.ClassNotFoundException

  • 프로젝트 clean 시 정상 실행됨

2. SwaggerConfig.java 설정

2-1. springfox 2.9.2 버전용

package egovframework.com.cmm;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.annotation.RequestMethod;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.builders.ResponseMessageBuilder;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.service.ResponseMessage;
import springfox.documentation.service.VendorExtension;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

import java.util.ArrayList;

@Configuration
//@EnableSwagger2
@EnableWebMvc
public class SwaggerConfig {

	// 필수항목
	@Bean
	public Docket customImplementation() {
        return new Docket(DocumentationType.SWAGGER_2)
                .useDefaultResponseMessages(false)  // 기본적인 응답메시지 미사용
            	.globalResponseMessage(RequestMethod.POST, getArrayList()) // getArrayList()함수에서 정의한 응답메시지 사용
            	.apiInfo(getApiInfo())
                .select()                   // return ApiSelectoorBuilder(화면 관리)
                .apis(RequestHandlerSelectors.basePackage("egovframework.bmigsvr.controller")) // Swagger를 적용할 패키지
                .paths(PathSelectors.any()) // url path 지정(예를들면 PathSelectors.ant("/home/**")인 경우 /home/ path를 가진 url만 공개하겠다는 의미. any인 경우 전체 url
                .build();                   // selector build
    }
	
	// 선택항목(Swagger UI에서 보여지는 정보)
	public ApiInfo getApiInfo() {
		return new ApiInfo("Service REST API Documentation",        // swagger 제목
				"REST Api Documentation",                     // swagger 설명
				"1.0",                                           // swaggeer 버전
				"localhost:8080",
				new Contact("kang-min-kyu","","aaa@gmail.com"), //작성자 정보
				"Apache 2.0", "http://www.apache.org/licenses/LICENSE-2.0",
				new ArrayList<VendorExtension>());
	}

	// 선택항목(responseMessage 리스트를 별도로 생성.(defaultResponseMessage 미사용))
	private ArrayList<ResponseMessage> getArrayList() {  
		ArrayList<ResponseMessage> lists = new ArrayList<ResponseMessage>();
		lists.add(new ResponseMessageBuilder().code(500).message("이상한요청").build());
		lists.add(new ResponseMessageBuilder().code(403).message("황당한요청").build());
		lists.add(new ResponseMessageBuilder().code(401).message("비인증된접근").build());
		return lists;
	}

}

2-2. springfox 3.0.0 버전용

  • 설정 후에도 오류가 나면 springfox 2.9.2 버전을 3.0.0 버전으로 변경해주어야 함 (오류 발생 시에만 진행)
	<dependency>
		<groupId>io.springfox</groupId>
		<artifactId>springfox-swagger-ui</artifactId>
		<version>3.0.0</version>
	</dependency>
	<dependency>
		<groupId>io.springfox</groupId>
		<artifactId>springfox-boot-starter</artifactId>
		<version>3.0.0</version>
	</dependency>
  • return new Docket(DocumentationType.SWAGGER_2)SWAGGER_2 부분 클릭하여 2.9.2 버전을 아직 사용 중이진 않은지 확인 (OAS_30 필요)
    • 계속 2.9.2 버전을 유지하고 있어서 .m2 리포지토리에서 직접 삭제함
  • springfox 3.0.0 버전용
@EnableWebMvc
@Configuration
public class SwaggerConfig {

	// 필수항목
	@Bean
	public Docket customImplementation() {
        return new Docket(DocumentationType.OAS_30)
                .useDefaultResponseMessages(false)  // 기존적인 응답메시지 미사용
//                .globalResponseMessage(RequestMethod.POST, getArrayList()) // getArrayList()함수에서 정의한 응답메시지 사용
//                .apiInfo(getApiInfo())
                .select()                   // return ApiSelectoorBuilder(화면 관리)
                .apis(RequestHandlerSelectors.basePackage("egovframework.bmigsvr.controller")) // Swagger를 적용할 패키지
                .paths(PathSelectors.any()) // url path 지정(예를들면 PathSelectors.ant("/home/**")인 경우 /home/ path를 가진 url만 공개하겠다는 의미. any인 경우 전체 url
                .build();                   // selector build
    }
	
	(globalResponseMessage, apiInfo 는 각자 용도에 맞게 수정해서 사용)
}
  • 이후 절차도 springfox 3.0.0 버전에 맞게 수정해야 함

2-3. springfox 2.5.0 버전용

package egovframework.com.cmm.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;

import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.builders.ResponseMessageBuilder;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.service.ResponseMessage;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

import java.util.ArrayList;


@Configuration
@EnableSwagger2
@EnableWebMvc
public class SwaggerConfig extends WebMvcConfigurationSupport {

    @Bean
    public Docket customImplementation() {
        return new Docket(DocumentationType.SWAGGER_2)
                .useDefaultResponseMessages(false)  // 기존적인 응답메시지 미사용
//                .globalResponseMessage(RequestMethod.POST, getArrayList()) // 정의한 응답메시지 사용
                .apiInfo(getApiInfo())
                .select()
                .apis(RequestHandlerSelectors.basePackage("egovframework.bmigsvr.controller"))
                .paths(PathSelectors.any())
                .build();
    }
    
    // 선택항목(Swagger UI에서 보여지는 정보)
    public ApiInfo getApiInfo() {
        return new ApiInfoBuilder()
                .title("API")
                .description("API Documentation")
                .version("1.0")
                .build();
    }

    /** Swagger UI 를 Resource Handler 에 등록 */ 
    // @Override 
    // public void addResourceHandlers(ResourceHandlerRegistry registry) { 
    //     registry.addResourceHandler("swagger-ui.html") .addResourceLocations("classpath:/META-INF/resources/"); 
    //     registry.addResourceHandler("/webjars/**") .addResourceLocations("classpath:/META-INF/resources/webjars/"); 
    // } 

}

3. egov-com-servlet.xml

  • 전자정부프레임워크에 swagger 2.9.2 적용하기
  • /src/main/webapp/WEB-INF/config/egovframework/springmvc/egov-com-servlet.xml<bean>, <mvc:resources> 추가
    • 직접 정의한 egovframework.com.cmm.config.SwaggerConfig 를 연결하거나,
    • 기본값인 springfox.documentation.swagger2.configuration.Swagger2DocumentationConfiguration 로 설정해도 무방
	<!-- 패키지 내 Controller, Service, Repository 클래스의 auto detect를 위한 mvc 설정 -->
    <context:component-scan base-package="egovframework">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/>
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
    </context:component-scan>
    
	<bean id="swaggerConfig" class="egovframework.com.cmm.config.SwaggerConfig"/>
	<!-- <bean id="swagger2Config" class="springfox.documentation.swagger2.configuration.Swagger2DocumentationConfiguration" /> -->

	<mvc:resources location="classpath:/META-INF/resources/" mapping="swagger-ui.html" />
	<mvc:resources location="classpath:/META-INF/resources/webjars/" mapping="/webjars/**" />
	
	<!-- annotation-driven 설정. -->
	<mvc:annotation-driven />
	
	<!-- efault servlet 은 .html .css .js -->
	<mvc:default-servlet-handler />
  • <mvc:interceptors> 설정
    <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/cop/com/*.do"/>
            <mvc:mapping path="/cop/bbs/*Master*.do"/>
            <mvc:mapping path="/uat/uia/*.do"/>
            <mvc:exclude-mapping path="/uat/uia/actionLogin.do"/>
            <mvc:exclude-mapping path="/uat/uia/egovLoginUsr.do"/>
            <mvc:exclude-mapping path="/swagger-ui.html"/>
            <mvc:exclude-mapping path="/swagger-ui.html/**"/>
            <mvc:exclude-mapping path="/swagger-ui/**"/>
            <mvc:exclude-mapping path="/configuration/**"/>
            <mvc:exclude-mapping path="/swagger-resources/**"/>
            <mvc:exclude-mapping path="/swagger-resources/configuration/**"/>
            <mvc:exclude-mapping path="/swagger-resources/configuration/ui"/>
            <mvc:exclude-mapping path="/v2/api-docs"/>
            <mvc:exclude-mapping path="/webjars/**"/>
            <mvc:exclude-mapping path="/webjars/springfox-swagger-ui/*.{js,css}"/>
		    <bean class="egovframework.com.cmm.interceptor.AuthenticInterceptor" />
        </mvc:interceptor>
    </mvc:interceptors>

4. web.xml

  • (다시 세팅했을 때, 생략해도 실행된 것으로 보아 생략해도 무방)
  • web.xml 에 아래 내용 추가
	<servlet>
		<servlet-name>action</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>/WEB-INF/config/egovframework/springmvc/*.xml</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>action</servlet-name>
		<url-pattern>*.do</url-pattern>
	</servlet-mapping>
	
	<servlet>
		<servlet-name>swagger</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>/WEB-INF/config/egovframework/springmvc/*.xml</param-value>
		</init-param>
		<load-on-startup>2</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>swagger</servlet-name>
	 	<url-pattern>/swagger-ui.html</url-pattern>
		<url-pattern>/webjars/**</url-pattern>
		<url-pattern>/</url-pattern>
	</servlet-mapping>

4-1. Error creating bean with name 'documentationPluginsBootstrapper' defined in URL

  • SwaggerConfig.java@EnableSwagger2, @EnableWebMvc 를 추가해주면 됨
Error creating bean with name 'documentationPluginsBootstrapper' defined in URL
Error creating bean with name 'webMvcRequestHandlerProvider' defined in URL
  • pom.xml 은 scope 추가
		<dependency>
			<groupId>io.springfox</groupId>
			<artifactId>springfox-swagger-ui</artifactId>
			<version>2.9.2</version>
            <scope>compile</scope>
		</dependency>
		<dependency>
			<groupId>io.springfox</groupId>
			<artifactId>springfox-swagger2</artifactId>
			<version>2.9.2</version>
            <scope>compile</scope>
		</dependency>

5. globals.properties 설정

  • globals.properties 에 아래 내용 추가
# Swagger
springdoc.api-docs.path=/api-docs
springdoc.swagger-ui.path=/swagger-ui.html 
spring.mvc.pathmatch.matching-strategy=ant_path_matcher

5-1. Failed to start bean 'documentationPluginsBootstrapper'

  • globals.properties > spring.mvc.pathmatch.matching-strategy=ant_path_matcher 를 추가하면 해결됨
    • 대부분 spring boot 2.6.0부터 요청 경로를 ControllerHandler에 매칭시키기 위한 전략의 기본값이 ant_path_matcher 전략 -> path_pattern_parser 전략으로 변경되었기 때문
org.springframework.context.ApplicationContextException: Failed to start bean 'documentationPluginsBootstrapper'; nested exception is java.lang.NoSuchMethodError: org.springframework.web.bind.annotation.RequestMapping.path()[Ljava/lang/String;
  • 그럼에도 java.lang.IllegalStateException: 불허되는 접근: 이 웹 애플리케이션 인스턴스는 이미 중지되었습니다 오류가 발생할 수 있음
  • 재빌드 후에도 오류가 발생하면 https://betweencloud.tistory.com/154 참조

5-2. java.lang.IllegalStateException: 불허되는 접근

  • mysql-connector-java-8.0.28.jar 파일을 C:\server\apache-tomcat-8.5.98\lib 경로에 추가
org.apache.catalina.loader.WebappClassLoaderBase checkStateForResourceLoading
정보: 불허되는 접근: 이 웹 애플리케이션 인스턴스는 이미 중지되었습니다. []을(를) 로드할 수 없습니다. 디버그 목적 및 불허되는 접근을 발생시킨 해당 쓰레드를 종료시키기 위한 시도로서, 다음 스택 트레이스가 생성됩니다.
java.lang.IllegalStateException: 불허되는 접근: 이 웹 애플리케이션 인스턴스는 이미 중지되었습니다. []을(를) 로드할 수 없습니다. 디버그 목적 및 불허되는 접근을 발생시킨 해당 쓰레드를 종료시키기 위한 시도로서, 다음 스택 트레이스가 생성됩니다.

6. Swagger 접속

  • Tomcat 실행 후 아래 url 로 접속
  • Swagger 2 : http://://swagger-ui.html
  • Swagger 3 : http://://swagger-ui/index.html

6-1. Failed to load API definition

No mapping found for HTTP request with URI [/swagger-ui/index.html]
No mapping found for HTTP request with URI [/null/swagger-resources/configuration/ui] in DispatcherServlet with name 'swagger'
  • 아래 오류가 Swagger 화면에 보여짐
Failed to load API definition.
Fetch error > undefined http://192.168.1.217:8060/v2/api-docs

6-1-1. No mapping found for HTTP request with URI [/csrf] in DispatcherServlet with name 'swagger'

  • WARN [org.springframework.web.servlet.PageNotFound] No mapping found for HTTP request with URI [/csrf] in DispatcherServlet with name 'swagger'
  • egov-com-servlet.xml<mvc:annotation-driven />, mvc:default-servlet-handler /> 가 있는지 확인

6-1-2. java.lang.NoSuchMethodError: org.springframework.web.util.UriComponentsBuilder

  • java.lang.NoSuchMethodError: org.springframework.web.util.UriComponentsBuilder.fromHttpRequest(Lorg/springframework/http/HttpRequest;)Lorg/springframework/web/util/UriComponentsBuilder;

6-1-3. Spring 버전 차이로 발생되는 오류

  • Spring 버전 차이로 발생할 수 있음
  • maven dependency 버전 수정 필요
java.lang.NoSuchMethodError: org.springframework.beans.factory.support.RootBeanDefinition.setTargetType
java.lang.IllegalStateException: LifecycleProcessor not initialized
또는
java.lang.NoSuchMethodError: org.springframework.core.annotation.AnnotationUtils.findAnnotation

6-2. 빈 화면이 뜨는 경우

  • F12 개발자 도구 확인 > Failed to load resource: the server responded with a status of 404 ()
  • swagger-ui 화면을 구성하는 css, js, png 등의 파일을 불러오지 못한 것

7. 관련 링크

7-1. 접속 경로

7-2. Spring-Boot 및 Spring 에 Swagger 연동

7-3. 전자정부프레임워크에 Swagger 연동

profile
개린이

0개의 댓글