기존에는 spring boot 2.* springfox를 사용하여 swagger를 이용했지만 boot 3.* 부터는 springdoc을 더 권장하는 분위기이고 springfox는 2년 전 개발이 중단되었지만 springdoc은 계속 개발되고 있어 이번에는 boot3과 springdoc을 사용해보았다.
설정 부분에서는 크게 달라진 점 없었고 아래 종속성만 추가해주면 된다.
//swagger 연동
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.0.2'
boot3으로 넘어오면서 바뀐건지 해당 종속성 문제인지는 모르겠지만 앱 실행하면 다음과 같은 에러가 발생한다.
jakarta.validation.NoProviderFoundException: Unable to create a Configuration, because no Jakarta Bean Validation provider could be found. Add a provider like Hibernate Validator (RI) to your classpath.
at jakarta.validation.Validation$GenericBootstrapImpl.configure(Validation.java:291) ~[jakarta.validation-api-3.0.2.jar:na]
at jakarta.validation.Validation.buildDefaultValidatorFactory(Validation.java:103) ~[jakarta.validation-api-3.0.2.jar:na]
at org.hibernate.cfg.beanvalidation.TypeSafeActivator.getValidatorFactory(TypeSafeActivator.java:479) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
at org.hibernate.cfg.beanvalidation.TypeSafeActivator.activate(TypeSafeActivator.java:82) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
at org.hibernate.cfg.beanvalidation.BeanValidationIntegrator.integrate(BeanValidationIntegrator.java:137) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:287) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:415) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:1423) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:75) ~[spring-orm-6.0.13.jar:6.0.13]
at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:376) ~[spring-orm-6.0.13.jar:6.0.13]
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:409) ~[spring-orm-6.0.13.jar:6.0.13]
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:396) ~[spring-orm-6.0.13.jar:6.0.13]
at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.afterPropertiesSet(LocalContainerEntityManagerFactoryBean.java:352) ~[spring-orm-6.0.13.jar:6.0.13]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1817) ~[spring-beans-6.0.13.jar:6.0.13]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1766) ~[spring-beans-6.0.13.jar:6.0.13]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:598) ~[spring-beans-6.0.13.jar:6.0.13]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:520) ~[spring-beans-6.0.13.jar:6.0.13]
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) ~[spring-beans-6.0.13.jar:6.0.13]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-6.0.13.jar:6.0.13]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) ~[spring-beans-6.0.13.jar:6.0.13]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) ~[spring-beans-6.0.13.jar:6.0.13]
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1166) ~[spring-context-6.0.13.jar:6.0.13]
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:940) ~[spring-context-6.0.13.jar:6.0.13]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:616) ~[spring-context-6.0.13.jar:6.0.13]
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) ~[spring-boot-3.0.12.jar:3.0.12]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:733) ~[spring-boot-3.0.12.jar:3.0.12]
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:435) ~[spring-boot-3.0.12.jar:3.0.12]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:311) ~[spring-boot-3.0.12.jar:3.0.12]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1301) ~[spring-boot-3.0.12.jar:3.0.12]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1290) ~[spring-boot-3.0.12.jar:3.0.12]
at com.feathercode.FeatherCodeApplication.main(FeatherCodeApplication.java:12) ~[classes/:na]
발생 이유를 조사해본 바로는 Hibernate Validator는 validation 라이브러리를 사용하는 bean 클래스로 swagger 내부에서 사용하는데 현 프로젝트에는 포함이 되어 있지 않았다. 따라서 해당 클래스의 종속성을 추가해주기만 하면 된다.
##maven
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.2.4.Final</version>
</dependency>
##gradle
implementation 'org.springframework.boot:spring-boot-starter-validation'
잘 해결되나 싶었지만 구현과정에서 불편한 점이 발생했다. controller에서 구현한 메소드의 파라미터 이름과 swagger에서 제공하는 api ui의 파라미터 이름이 매핑이 되지 않아 파라미터가 제대로 넘어가지 않는다.
@Operation(summary = "문자열 이어붙이기") // 메소드 설명
@GetMapping("/swagger/concat")
public String concat(String str1,
@RequestParam(name = "str2") String str2) {
return str1 + " " + str2;
}
str2처럼 @RequestParam의 name속성을 지정해주면 swagger ui에서도 잘 매핑되지만, str1처럼 그대로 구현하게 되면 arg0로 넘어가기 때문에 에러가 발생한다.
물론 매번 @RequestParam을 통해 name속성을 지정해주면 해결할 수 있지만 매우 번거로운 일이고 더 큰 문제는 @RequestBody에서 나타난다. @RequestBody에는 name속성이 없어 파라미터로 값을 받아올 수 없다.. (해결법이 있을 순 있지만 저는 해결못함)
여러 자료를 찾아보다가 이미 이슈된 문제지만 딱히 해결방법은 없고 springdoc 이전 버전에서는 문제가 없었다고 해서 2.0.2 -> 2.0.0으로 다운그레이드해보았다.
//swagger 연동
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.0.0'
이제 이름을 지정해주지 않아도 파라미터 이름이 제대로 매핑되는 것을 확인할 수 있다.
@Operation(summary = "문자열 이어붙이기") // 메소드 설명
@GetMapping("/swagger/concat")
public String concat(String str1, String str2) {
return str1 + " " + str2;
}
당연히 (@RequestBody Dto dto)에 대한 파라미터에 대해서도 dto의 필드를 정확히 읽고 파라미터 이름이 매핑된다.
결론
springdoc 2.0.0 버전을 사용하자
'Error Solution' 카테고리의 다른 글
Remote host terminated the handshake, SSL peer shut down incorrectly (1) | 2025.01.05 |
---|---|
Ansible - No such file or directory: b'kubectl' (0) | 2023.08.23 |
[ssh] ssh: connect to host: Connection refused, 원격 접속 실패 (0) | 2023.08.12 |
스프링부트 3.1.1 JDK1.8 호환 에러 해결 (0) | 2023.07.17 |
maven-jetty-plugin을 통한 mvn jetty:run 에서 발생하는 500에러 (0) | 2023.07.16 |