티스토리 뷰

목차

    배경

    사이드 프로젝트를 진행하면서 테스트를 위해 개발해두었던 Mock API 서버를 실제 API 서버로 전환 작업 중에 API 명세를 작성해두면 추후에 관리하기 편할 것 같아서 전환 작업중에 함께 명세 작성 또한 진행하였다.
    API 명세는 잘 알려진 Swagger를 적용했는데, 생각보다 이 설정이 잘 안되었고 자잘한 이슈가 생겼다.
    2022년 말에 릴리즈된 스프링부트 3버전 이상에서 Swagger를 사용할 경우 생길 수 있는 이슈라 챗 지피티한테 물어봐도 적절한 대답을 얻지 못하였고, 구글링을 해봐도 해결책을 쉽게 얻지 못해서 누군가가 이글을 보고 도움을 얻었으면 하는 마음으로 기록해둔다.
     

    2023년 8월 기준으로, chatGPT는 2021년 9월까지의 정보만으로만 학습이 되어있다고한다.

     
    먼저 현재 내가 작업중인 프로젝트의 스프링 부트 버전은 3.1.0(3.X.X 버전 이상)이다.
    (글 후반에 나오겠지만, 이 글에서 다루는 이슈는 3 버전 이상의 스프링 부트 프로젝트 Swagger를 사용할때 발생할 수 있는 이슈다)

    plugins {
    	id 'java'
    	id 'org.springframework.boot' version '3.1.0'
    	id 'io.spring.dependency-management' version '1.1.0'
    }

    Swagger 등록 - 첫번째 시도

    swagger 설정을 위해 구글링했을때 가장 많은 설명이 있었던 springfox 3.0.0 라이브러리와 @EnableSwagger2 어노테이션을 적용한 방법이다.
    build.gradle에 의존성을 등록하고 SwaggerConfig.java 파일을 작성하여 Swagger를 적용하는 방법을 가장 먼저 시도했다.
     
    build.gradle 의존성설정

    dependencies {
    	.. 기존 의존성들
        
    	// springfox 의존성 등록
    	implementation('io.springfox:springfox-boot-starter:3.0.0')
    	implementation('io.springfox:springfox-swagger-ui:3.0.0')
    }

     
    SwaggerConfig.java

    package com.booklog.book.config;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import springfox.documentation.builders.ApiInfoBuilder;
    import springfox.documentation.builders.PathSelectors;
    import springfox.documentation.builders.RequestHandlerSelectors;
    import springfox.documentation.service.ApiInfo;
    import springfox.documentation.spi.DocumentationType;
    import springfox.documentation.spring.web.plugins.Docket;
    import springfox.documentation.swagger2.annotations.EnableSwagger2;
    
    @Configuration
    @EnableSwagger2
    public class SwaggerConfig {
    	@Bean
    	public Docket api() {
    		return new Docket(DocumentationType.OAS_30)
    			.useDefaultResponseMessages(true)
    			.apiInfo(apiInfo())
    			.select()
    			.apis(RequestHandlerSelectors.basePackage("booklog-book.src"))
    			.paths(PathSelectors.any())
    			.build();
    	}
    
    	public ApiInfo apiInfo() {
    		return new ApiInfoBuilder()
    			.title("SpringBoot Rest API Documentation")
    			.description("SpringBoot Rest API Documentation")
    			.version("1.0")
    			.build();
    	}
    }

     
    하지만 프로젝트 실행조차 실패하였고 다음과 같은 에러를 뱉어냈다.

    /Library/Java/JavaVirtualMachines/jdk-17.jdk/Contents/Home/bin/java -XX:TieredStopAtLevel=1 -Dspring.output.ansi.enabled=always -Dcom.sun.management.jmxremote -Dspring.jmx.enabled=true -Dspring.liveBeansView.mbeanDomain -Dspring.application.admin.enabled=true -Dmanagement.endpoints.jmx.exposure.include=* -javaagent:/Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar=49741:/Applications/IntelliJ IDEA.app/Contents/bin -Dfile.encoding=UTF-8 -classpath /Users/ohminhyeok/Desktop/BookLOG/booklog-book/build/classes/java/main:/Users/ohminhyeok/Desktop/BookLOG/booklog-book/build/resources/main:/Users/ohminhyeok/.gradle/caches/modules-2/files-2.1/org.springframework.boot/spring-boot-starter-web/3.1.0/36a8666047ea49114e0974bece35e2ef68cf975f/spring-boot-starter-web-3.1.0.jar:/Users/ohminhyeok/.gradle/caches/modules-2/files-2.1/org.projectlombok/lombok/1.18.26/8f8cf0372abf564913e9796623aac4c8ea44025a/lombok-1.18.26.jar:/Users/ohminhyeok/.gradle/caches/modules-2/files-2.1/org.slf4j/jcl-over-slf4j/2.0.7/f127fe5ee53404a8b3697cdd032dd1dd6a29dd77/jcl-over-slf4j-2.0.7.jar:/Users/ohminhyeok/.gradle/caches/modules-2/files-2.1/ch.qos.logback/logback-classic/1.4.7/307944865579a6d490e6a4cbb5082dc8f36536ca/logback-classic-1.4.7.jar:/Users/ohminhyeok/.gradle/caches/modules-2/files-2.1/io.springfox/springfox-boot-starter/3.0.0/5486365e263f8acca014b97efa50c3419d58e8f6/springfox-boot-starter-3.0.0.jar:/Users/ohminhyeok/.gradle/caches/modules-2/files-2.1/io.springfox/springfox-swagger-ui/3.0.0/1e665fbe22148f7c36fa8a08e515a0047cd4390b/springfox-swagger-ui-3.0.0.jar:/Users/ohminhyeok/.gradle/caches/modules-2/files-2.1/org.springframework.boot/spring-boot-starter-json/3.1.0/546bd372bc070339c4255ed37f4f0ff5aafa2648/spring-boot-starter-json-3.1.0.jar:/Users/ohminhyeok/.gradle/caches/modules-2/files-2.1/org.springframework.boot/spring-boot-starter/3.1.0/2960a1f899f4ee3eb815dc85986b0428c1a5289f/spring-boot-starter-3.1.0.jar:/Users/ohminhyeok/.gradle/caches/modules-2/files-2.1/org.springframework.boot/spring-boot-starter-tomcat/3.1.0/e7ea53de5a58aaf63f0e3a80d438f7dfc0b9806b/spring-boot-starter-tomcat-3.1.0.jar:/Users/ohminhyeok/.gradle/caches/modules-2/files-2.1/org.springframework/spring-webmvc/6.0.9/e127c07a23403832d0c6292f4a0bf8c7a2b7329f/spring-webmvc-6.0.9.jar:/Users/ohminhyeok/.gradle/caches/modules-2/files-2.1/org.springframework/spring-web/6.0.9/2837dec8a75ecfdad367d6c30ce9cbdfc89caa7a/spring-web-6.0.9.jar:/Users/ohminhyeok/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/2.0.7/41eb7184ea9d556f23e18b5cb99cad1f8581fc00/slf4j-api-2.0.7.jar:/Users/ohminhyeok/.gradle/caches/modules-2/files-2.1/ch.qos.logback/logback-core/1.4.7/a2948dae4013d0e9486141b4d638d8951becb767/logback-core-1.4.7.jar:/Users/ohminhyeok/.gradle/caches/modules-2/files-2.1/io.springfox/springfox-oas/3.0.0/e7bc9c1319cf1b64ae714a249c3db3b8fe01e42b/springfox-oas-3.0.0.jar:/Users/ohminhyeok/.gradle/caches/modules-2/files-2.1/io.springfox/springfox-data-rest/3.0.0/40f5e834d6696ae1d3212fa5a2d5e1ec406bedc0/springfox-data-rest-3.0.0.jar:/Users/ohminhyeok/.gradle/caches/modules-2/files-2.1/io.springfox/springfox-bean-validators/3.0.0/80c646fdebe5f2b2b337a5a686e540fee0b7304f/springfox-bean-validators-3.0.0.jar:/Users/ohminhyeok/.gradle/caches/modules-2/files-2.1/io.springfox/springfox-swagger2/3.0.0/7bcb18d496576eff76ef7bb72684e149cbb75c1d/springfox-swagger2-3.0.0.jar:/Users/ohminhyeok/.gradle/caches/modules-2/files-2.1/org.springframework.plugin/spring-plugin-metadata/2.0.0.RELEASE/6fb3a1fc0f05dc826687b7686ad8a5960ecdd57c/spring-plugin-metadata-2.0.0.RELEASE.jar:/Users/ohminhyeok/.gradle/caches/modules-2/files-2.1/org.springframework.plugin/spring-plugin-core/2.0.0.RELEASE/95fc8c13037630f4aba9c51141f535becec00fe6/spring-plugin-core-2.0.0.RELEASE.jar:/Users/ohminhyeok/.gradle/caches/modules-2/files-2.1/com.fasterxml/classmate/1.5.1/3fe0bed568c62df5e89f4f174c101eab25345b6c/classmate-1.5.1.jar:/Users/ohminhyeok/.gradle/caches/modules-2/files-2.1/com.fasterxml.jackson.datatype/jackson-datatype-jsr310/2.15.0/3c3a0cfba09271fab4603224f2c2e21c6ddf6dc4/jackson-datatype-jsr310-2.15.0.jar:/Users/ohminhyeok/.gradle/caches/modules-2/files-2.1/com.fasterxml.jackson.module/jackson-module-parameter-names/2.15.0/b900bd44597ba9a8b348b49428f457d5f16fc302/jackson-module-parameter-names-2.15.0.jar:/Users/ohminhyeok/.gradle/caches/modules-2/files-2.1/com.fasterxml.jackson.datatype/jackson-datatype-jdk8/2.15.0/ddfd375d4df3a4c149bf7d5d71c45b91909cf31f/jackson-datatype-jdk8-2.15.0.jar:/Users/ohminhyeok/.gradle/caches/modules-2/files-2.1/com.fasterxml.jackson.core/jackson-databind/2.15.0/d41caa3a4e9f85382702a059a65c512f85ac230/jackson-databind-2.15.0.jar:/Users/ohminhyeok/.gradle/caches/modules-2/files-2.1/org.springframework.boot/spring-boot-starter-logging/3.1.0/4784b6e2adfe32720a4e2c009a62650835bba391/spring-boot-starter-logging-3.1.0.jar:/Users/ohminhyeok/.gradle/caches/modules-2/files-2.1/org.springframework.boot/spring-boot-autoconfigure/3.1.0/b06d1f0b08f6f8a2636e364c8941b2dabc4f0b77/spring-boot-autoconfigure-3.1.0.jar:/Users/ohminhyeok/.gradle/caches/modules-2/files-2.1/org.springframework.boot/spring-boot/3.1.0/efa941e9a2162a3dd8c5e4679f46a24af9e5769f/spring-boot-3.1.0.jar:/Users/ohminhyeok/.gradle/caches/modules-2/files-2.1/jakarta.annotation/jakarta.annotation-api/2.1.1/48b9bda22b091b1f48b13af03fe36db3be6e1ae3/jakarta.annotation-api-2.1.1.jar:/Users/ohminhyeok/.gradle/caches/modules-2/files-2.1/org.springframework/spring-core/6.0.9/284ed111fa0b49b29f6fea6ac0afa402b809e427/spring-core-6.0.9.jar:/Users/ohminhyeok/.gradle/caches/modules-2/files-2.1/org.yaml/snakeyaml/1.33/2cd0a87ff7df953f810c344bdf2fe3340b954c69/snakeyaml-1.33.jar:/Users/ohminhyeok/.gradle/caches/modules-2/files-2.1/org.apache.tomcat.embed/tomcat-embed-websocket/10.1.8/7abb8ab966dd135924c8e69cb2f1db735dee5f23/tomcat-embed-websocket-10.1.8.jar:/Users/ohminhyeok/.gradle/caches/modules-2/files-2.1/org.apache.tomcat.embed/tomcat-embed-core/10.1.8/ec4b884806c65c80c86bb3db134f6f6f99e79ed8/tomcat-embed-core-10.1.8.jar:/Users/ohminhyeok/.gradle/caches/modules-2/files-2.1/org.apache.tomcat.embed/tomcat-embed-el/10.1.8/6f3a4ae2ae37270eeb6e9bec4e7207facdc9e8fa/tomcat-embed-el-10.1.8.jar:/Users/ohminhyeok/.gradle/caches/modules-2/files-2.1/org.springframework/spring-context/6.0.9/be88c57829b9ec038774b47c241ac45673352a55/spring-context-6.0.9.jar:/Users/ohminhyeok/.gradle/caches/modules-2/files-2.1/org.springframework/spring-aop/6.0.9/8c1025bf9c1dc66f5268639866b5a45ed9bc62ef/spring-aop-6.0.9.jar:/Users/ohminhyeok/.gradle/caches/modules-2/files-2.1/org.springframework/spring-beans/6.0.9/745619eee32c8ead88a21c97748d2416f1db8dd9/spring-beans-6.0.9.jar:/Users/ohminhyeok/.gradle/caches/modules-2/files-2.1/org.springframework/spring-expression/6.0.9/f50a1df7ed038ee7ca85528aff652cef4ff4883b/spring-expression-6.0.9.jar:/Users/ohminhyeok/.gradle/caches/modules-2/files-2.1/io.micrometer/micrometer-observation/1.11.0/b3d1b34d16e7e8fa9087c5d51ec39bc3005e2733/micrometer-observation-1.11.0.jar:/Users/ohminhyeok/.gradle/caches/modules-2/files-2.1/io.springfox/springfox-swagger-common/3.0.0/2e2fae840984cfcabfd50e1b4b1c23422135ba12/springfox-swagger-common-3.0.0.jar:/Users/ohminhyeok/.gradle/caches/modules-2/files-2.1/io.springfox/springfox-spring-webmvc/3.0.0/7ed22363fdfd651cd811c0b2391f16bddb91db8b/springfox-spring-webmvc-3.0.0.jar:/Users/ohminhyeok/.gradle/caches/modules-2/files-2.1/io.springfox/springfox-spring-web/3.0.0/a76f2fbe805bfd2798e20dc8f2cfbfad554d52da/springfox-spring-web-3.0.0.jar:/Users/ohminhyeok/.gradle/caches/modules-2/files-2.1/io.springfox/springfox-schema/3.0.0/32c5d6965617830ef6480fadb9030008945bcd9c/springfox-schema-3.0.0.jar:/Users/ohminhyeok/.gradle/caches/modules-2/files-2.1/io.springfox/springfox-spi/3.0.0/bae0b820d4b5a922063d34a42aaf4f763308b828/springfox-spi-3.0.0.jar:/Users/ohminhyeok/.gradle/caches/modules-2/files-2.1/io.springfox/springfox-core/3.0.0/7c3367ce577c8acd9bf64c74488c9269253516c9/springfox-core-3.0.0.jar:/Users/ohminhyeok/.gradle/caches/modules-2/files-2.1/io.springfox/springfox-spring-webflux/3.0.0/efccbcfe1d23f2ba520bd87cc156bf2b81f3568e/springfox-spring-webflux-3.0.0.jar:/Users/ohminhyeok/.gradle/caches/modules-2/files-2.1/io.swagger.core.v3/swagger-models/2.1.2/e7edeed6456a16d707542c003af03a594ecafe3a/swagger-models-2.1.2.jar:/Users/ohminhyeok/.gradle/caches/modules-2/files-2.1/io.swagger.core.v3/swagger-annotations/2.1.2/d407a33aa71444802c640080eb4d5f499b027f96/swagger-annotations-2.1.2.jar:/Users/ohminhyeok/.gradle/caches/modules-2/files-2.1/io.swagger/swagger-models/1.5.20/fb3a23bad80c5ed84db9dd150db2cba699531458/swagger-models-1.5.20.jar:/Users/ohminhyeok/.gradle/caches/modules-2/files-2.1/io.swagger/swagger-annotations/1.5.20/16051f93ce11ca489a5313775d825f82fcc2cd6c/swagger-annotations-1.5.20.jar:/Users/ohminhyeok/.gradle/caches/modules-2/files-2.1/com.fasterxml.jackson.core/jackson-annotations/2.15.0/89b0fd554928425a776a6e97ed010034312af21d/jackson-annotations-2.15.0.jar:/Users/ohminhyeok/.gradle/caches/modules-2/files-2.1/com.fasterxml.jackson.core/jackson-core/2.15.0/12f334a1dc9c6d2854c43ae314024dde8b3ad572/jackson-core-2.15.0.jar:/Users/ohminhyeok/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-to-slf4j/2.20.0/d37f81f8978e2672bc32c82712ab4b3f66624adc/log4j-to-slf4j-2.20.0.jar:/Users/ohminhyeok/.gradle/caches/modules-2/files-2.1/org.slf4j/jul-to-slf4j/2.0.7/a48f44aeaa8a5ddc347007298a28173ac1fbbd8b/jul-to-slf4j-2.0.7.jar:/Users/ohminhyeok/.gradle/caches/modules-2/files-2.1/org.springframework/spring-jcl/6.0.9/88d9ddfc6bbbf4047c2a8de8de94a425b06f636a/spring-jcl-6.0.9.jar:/Users/ohminhyeok/.gradle/caches/modules-2/files-2.1/io.micrometer/micrometer-commons/1.11.0/5de5da6be4f01128ab3995acdf86f2844137d4e4/micrometer-commons-1.11.0.jar:/Users/ohminhyeok/.gradle/caches/modules-2/files-2.1/io.github.classgraph/classgraph/4.8.83/7be289f451cedf9e35ed97caba3953226b4e6d9/classgraph-4.8.83.jar:/Users/ohminhyeok/.gradle/caches/modules-2/files-2.1/net.bytebuddy/byte-buddy/1.14.4/20498aaec9b00a5cfdb831e7bf68feafa833ce4b/byte-buddy-1.14.4.jar:/Users/ohminhyeok/.gradle/caches/modules-2/files-2.1/ch.randelshofer/fastdoubleparser/0.8.0/85c25540369921659556ead85e02c99ef0d24280/fastdoubleparser-0.8.0.jar:/Users/ohminhyeok/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.20.0/1fe6082e660daf07c689a89c94dc0f49c26b44bb/log4j-api-2.20.0.jar:/Users/ohminhyeok/.gradle/caches/modules-2/files-2.1/org.mapstruct/mapstruct/1.3.1.Final/6ab184bbc7a7029738277a244e4c535fcdc3f558/mapstruct-1.3.1.Final.jar com.booklog.book.BookApplication
    
      .   ____          _            __ _ _
     /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
    ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
     \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
      '  |____| .__|_| |_|_| |_\__, | / / / /
     =========|_|==============|___/=/_/_/_/
     :: Spring Boot ::                (v3.1.0)
    
    2023-08-22T13:02:30.502+09:00  INFO 1981 --- [           main] com.booklog.book.BookApplication         : Starting BookApplication using Java 17.0.7 with PID 1981 (/Users/ohminhyeok/Desktop/BookLOG/booklog-book/build/classes/java/main started by ohminhyeok in /Users/ohminhyeok/Desktop/BookLOG/booklog-book)
    2023-08-22T13:02:30.503+09:00  INFO 1981 --- [           main] com.booklog.book.BookApplication         : No active profile set, falling back to 1 default profile: "default"
    2023-08-22T13:02:30.798+09:00 ERROR 1981 --- [           main] o.s.boot.SpringApplication               : Application run failed
    
    java.lang.TypeNotPresentException: Type javax.servlet.http.HttpServletRequest not present
    	at java.base/sun.reflect.generics.factory.CoreReflectionFactory.makeNamedType(CoreReflectionFactory.java:117) ~[na:na]
    	at java.base/sun.reflect.generics.visitor.Reifier.visitClassTypeSignature(Reifier.java:125) ~[na:na]
    	at java.base/sun.reflect.generics.tree.ClassTypeSignature.accept(ClassTypeSignature.java:49) ~[na:na]
    	at java.base/sun.reflect.generics.visitor.Reifier.reifyTypeArguments(Reifier.java:68) ~[na:na]
    	at java.base/sun.reflect.generics.visitor.Reifier.visitClassTypeSignature(Reifier.java:138) ~[na:na]
    	at java.base/sun.reflect.generics.tree.ClassTypeSignature.accept(ClassTypeSignature.java:49) ~[na:na]
    	at java.base/sun.reflect.generics.repository.ClassRepository.computeSuperInterfaces(ClassRepository.java:117) ~[na:na]
    	at java.base/sun.reflect.generics.repository.ClassRepository.getSuperInterfaces(ClassRepository.java:95) ~[na:na]
    	at java.base/java.lang.Class.getGenericInterfaces(Class.java:1211) ~[na:na]
    	at org.springframework.core.ResolvableType.getInterfaces(ResolvableType.java:500) ~[spring-core-6.0.9.jar:6.0.9]
    	at org.springframework.core.ResolvableType.as(ResolvableType.java:448) ~[spring-core-6.0.9.jar:6.0.9]
    	at org.springframework.core.ResolvableType.forClass(ResolvableType.java:1048) ~[spring-core-6.0.9.jar:6.0.9]
    	at org.springframework.plugin.core.config.PluginRegistriesBeanDefinitionRegistrar.getTargetType(PluginRegistriesBeanDefinitionRegistrar.java:101) ~[spring-plugin-core-2.0.0.RELEASE.jar:2.0.0.RELEASE]
    	at org.springframework.plugin.core.config.PluginRegistriesBeanDefinitionRegistrar.registerBeanDefinitions(PluginRegistriesBeanDefinitionRegistrar.java:71) ~[spring-plugin-core-2.0.0.RELEASE.jar:2.0.0.RELEASE]
    	at org.springframework.context.annotation.ImportBeanDefinitionRegistrar.registerBeanDefinitions(ImportBeanDefinitionRegistrar.java:86) ~[spring-context-6.0.9.jar:6.0.9]
    	at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.lambda$loadBeanDefinitionsFromRegistrars$1(ConfigurationClassBeanDefinitionReader.java:373) ~[spring-context-6.0.9.jar:6.0.9]
    	at java.base/java.util.LinkedHashMap.forEach(LinkedHashMap.java:721) ~[na:na]
    	at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsFromRegistrars(ConfigurationClassBeanDefinitionReader.java:372) ~[spring-context-6.0.9.jar:6.0.9]
    	at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsForConfigurationClass(ConfigurationClassBeanDefinitionReader.java:148) ~[spring-context-6.0.9.jar:6.0.9]
    	at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitions(ConfigurationClassBeanDefinitionReader.java:120) ~[spring-context-6.0.9.jar:6.0.9]
    	at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:427) ~[spring-context-6.0.9.jar:6.0.9]
    	at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:287) ~[spring-context-6.0.9.jar:6.0.9]
    	at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:344) ~[spring-context-6.0.9.jar:6.0.9]
    	at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:115) ~[spring-context-6.0.9.jar:6.0.9]
    	at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:771) ~[spring-context-6.0.9.jar:6.0.9]
    	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:589) ~[spring-context-6.0.9.jar:6.0.9]
    	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) ~[spring-boot-3.1.0.jar:3.1.0]
    	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:733) ~[spring-boot-3.1.0.jar:3.1.0]
    	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:435) ~[spring-boot-3.1.0.jar:3.1.0]
    	at org.springframework.boot.SpringApplication.run(SpringApplication.java:311) ~[spring-boot-3.1.0.jar:3.1.0]
    	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1305) ~[spring-boot-3.1.0.jar:3.1.0]
    	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1294) ~[spring-boot-3.1.0.jar:3.1.0]
    	at com.booklog.book.BookApplication.main(BookApplication.java:12) ~[main/:na]
    Caused by: java.lang.ClassNotFoundException: javax.servlet.http.HttpServletRequest
    	at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641) ~[na:na]
    	at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) ~[na:na]
    	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:520) ~[na:na]
    	at java.base/java.lang.Class.forName0(Native Method) ~[na:na]
    	at java.base/java.lang.Class.forName(Class.java:467) ~[na:na]
    	at java.base/sun.reflect.generics.factory.CoreReflectionFactory.makeNamedType(CoreReflectionFactory.java:114) ~[na:na]
    	... 32 common frames omitted
    
    
    Process finished with exit code 1

     

    두번째 시도

    위 에러로그와 관련하여 @EnableSwagger2 어노테이션 대신 @EnableWebMvc 어노테이션을 적용한 방법이다.
    (springfox 3.0.0 라이브러리 + @EnableWebMvc 어노테이션)
     
    build.gradle
    위와 동일
     
    SwaggerConfig.java
    위 SwaggerConfig.java에서 @EnableSwagger2을 @EnableWebMvc로 변경하였다.

    package com.booklog.book.config;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    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.service.ApiInfo;
    import springfox.documentation.spi.DocumentationType;
    import springfox.documentation.spring.web.plugins.Docket;
    
    @Configuration
    @EnableWebMvc
    public class SwaggerConfig {
    	@Bean
    	public Docket api() {
    		return new Docket(DocumentationType.OAS_30)
    			.useDefaultResponseMessages(true)
    			.apiInfo(apiInfo())
    			.select()
    			.apis(RequestHandlerSelectors.basePackage("booklog-book.src"))
    			.paths(PathSelectors.any())
    			.build();
    	}
    
    	public ApiInfo apiInfo() {
    		return new ApiInfoBuilder()
    			.title("SpringBoot Rest API Documentation")
    			.description("SpringBoot Rest API Documentation")
    			.version("1.0")
    			.build();
    	}
    }

     

    서버 실행은 성공하였으나, 스웨거 페이지(http://localhost:8080/swagger-ui/index.html)에 접속하였을때 Whitelable Error Page가 출력되었다.
     
     Whitelable Error Page 를 뱉는게 의아해서 구글링을 하던 중에 다음과 같은 글을 봤다.
     
    작업하고 계신 Spring Boot의 버전이 2.7.x 이하인지 확인해 보실 수 있을까요? Spring Boot3로 작업하실 경우 Swagger가 정상적으로 작동하지 않을 수 있습니다. sprongfox swagger에서 아직 jakarta 패키지를 지원하지 않는 것 같습니다. Spring Boot의 버전과 Swagger의 버전을 확인해 보시고 다시 실행해 보시기 바랍니다. 최신 소스 코드는 아래 git에서 확인하실 수 있습니다.
    출처 : https://www.inflearn.com/questions/771081/swagger-%EC%98%A4%EB%A5%98
     
    👉 내가 사용했던 springfox swagger 라이브러리에서 아직 Spring Boot 3 프로젝트에 대한 대응(jakarta 패키지)이 이뤄지지 않았다는 글을 보았고, 프로젝트 버전으로 인한 문제임을 직감했다. 따라서 springfox swagger 라이브러리 대신에 Spring Boot 3을 지원하는 springdoc으로 수정하였다.
     

    세번째 시도

    위 답변을 통해 구체적인 원인을 파악하였고, 해결방안을 찾기위해 구글링하던 도중 나와 같은 이슈를 다뤘던 블로그 글이 있어 그 내용을 참고하였다. 
    springfox 3.0.0 라이브러리를 springdoc 라이브러리로 변경하였다. 이때 SwaggerConfig 파일은 필요없다고하여 제거 하였다.
     
    build.gradle

    dependencies {
    	... 기존 의존성들
        
    	// spring doc 라이브러리
    	implementation 'org.springdoc:springdoc-openapi-ui:1.6.14'
    }

     

    여전히 Whitelabel Error Page를 뱉어냈으나, Spring doc에서 제공하는 openapi 라이브러리를 사용하면 된다고하여 라이브러리를 수정하였다.
     

    네번째 시도(성공)

    springdoc 라이브러리를 springdoc-openapi 라이브러리로 변경하였다. 위와 마찬가지로 SwaggerConfig 파일은 작성하지 않았다.
    (이때 Swagger 종속성 추가시 아래와 같이 버전을 2.x.x 이상으로 명시해줘야 한다고 합니다
    implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.0.2'
    }
    )

     
    build.gradle

    dependencies {
    	... 기존 의존성들
        
    	// springdoc-openapi 라이브러리 등록
    	implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.0.2'
    }

     
    4번의 시도 끝에 Swagger 페이지(http://localhost:8080/swagger-ui/index.html)에 정상 접속하였다.

    API 호출 & 응답도 정상적으로 이루어짐을 볼 수 있었다.


    참고한곳

    'Spring' 카테고리의 다른 글

    Spring Boot - AWS EC2, Maven, Git 으로 프로젝트 배포하기  (0) 2021.06.08
    Comments