본문 바로가기
Java | spring/Spring

Spring boot 3.2.0 마이그레이션 가이드 (+끝 없는 오류 해결 방법!)

by 워니 wony 2023. 12. 21.

토이 프로젝트 백엔드 서비스의 스프링 부트 버전을 3.2.0 으로 업그레이드를 했다.

기존에 스프링 부트를 2.7 버전을 사용하고 있었기 때문에 버전을 변경하는게 간단할줄 알았는데 아니었다...!

 

아직 회사에서 사용하는 프로젝트들은 2 버전을 모두 사용하고 있기 때문에 버전을 업하는 경우 똑같은 오류를 볼 수밖에 없어서 이김에 정리하려고 한다. 그리고 생각보다 오류 관련해서 포스팅이 별로 없어서 생각보다 헤매면서 삽질을 했기 때문에 나와 같은 다른 개발자들에게 조금의 삽질이라도 덜어 줄 수 있기를! 

 

 

스프링 부트 3.0 주요 변경 사항

  • Java EE에서 Jakarta EE API 로 마이그레이션
    • 예시 - (기존) javax.servlet:javax.servlet-api  >  (변경) jakarta.servlet:jakarta.servlet-api
  • Spring framework 6.0 기반
  • Spring Boot 3부터 Spring Security의 신규 메이저 버전인 Spring Security 6 사용
  • GraalVM 기반의 native image 생성을 정식 지원
  • 의존성 버전 업그레이드
    • Gradle 8.4: Java 21 지원 및 여러 가지 성능 개선, 보안 취약점 수정

 

 

스프링 부트 3.0 변경 필수 사전 작업 

  • [필수] Java 17 이상으로 변경 필요
  • 스프링 부트 2.7에서 업그레이드 하기
  • 사전 확인
    • Spring Boot 2.x 에서 사용하지 않는 클래스, 메서드 및 속성 제외됨(업그레이드 전 deprecated 된 메서드나 클래스를 호출하고 있는지 확인 필요!)

 

참고로 스프링 부트 3.0은 오랜만의 메인 버전이 변경된 버전이다 보니 기존 버전 업보다는 더 복잡한 편이다.

또한, Sring Boot 2.7 버전 아래 버전을 사용하고 있었다면, 2.7 버전으로 업그레이드 하고 마이그레이션을 하는게 좋다고 한다.

나는 운이 좋게도 2.7 버전을 사용하고 있어서 이 작업(삽질)을 하지는 않았다.

 

그리고, 자바 최소 버전이 17이다. 그렇기 떄문에 자바 8, 11을 사용하고 있다면 JDK 업그레이드가 선행이 필요하다.

 

 

 

스프링 부트 3 버전 마이그레이션 가이드

  • 공식 문서 확인하기
  • 사전 작업 진행
  • Spring boot 3.2.0 으로 변경
  • jakarta EE로 변경
    • (기존) import javax.validation.Valid;
    • (변경) import jakarta.validation.Valid;
  • 설정 파일 변경(yml 파일의 속성 명이 바뀌거나 제거된 것들이 있음 변경 필요)
  • 기타 오류 해결!

 

위와 같은 순서로 마이그레이션이 하면 된다.

 

 

공식문서는 항상 중요!

첫째, 역시 시작 전에 공식문서를 처음에 보는게 좋다!

스프링에서 3.0 릴리즈에 대한 공식 문서를 잘 정리해 놓았다. 꼭 한번 보고 변경하는걸 추천한다.

 

나는 공식 문서를 보지 않고 버전을 변경하고 하나하나 삽질을 했다.

덕분에 아주 다양한 오류를 발견할 수 있었다!

 

 

- [공식문서] 스프링부트 3.0 릴리즈 노트

https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-3.0-Release-Notes

 

Spring Boot 3.0 Release Notes

Spring Boot. Contribute to spring-projects/spring-boot development by creating an account on GitHub.

github.com

 

- [공식문서] 스프링부트 3.0 마이그레이션 가이드

https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-3.0-Migration-Guide

 

Spring Boot 3.0 Migration Guide

Spring Boot. Contribute to spring-projects/spring-boot development by creating an account on GitHub.

github.com

 

 

 

사전 작업

 

문서를 확인했다면, 사전 준비 작업을 하자.

java 버전을 확인하고, 스프링 부트가 2.7 버전이 맞는지 등을 확인해 보면 된다.

나도 자바 버전을 먼저 변경하고 서버를 띄워본 다음에 스프링 부트 버전을 업했다.(이렇게 체크포인트를 안찍으면 어떤것 때문에 에러가 난건지 체크하기 힘들다)

 

기존에 java 13을 사용하고 있었기에 java 17로 변경했다.

  • build.gradle 파일에서 java 버전을 17로 변경
java {
    sourceCompatibility = '17'
}

 

  • 인텔리제이 설정 변경
    • file > Project Structure > SDK

 

  • Settings

 

 

 

스프링 부트 버전 변경

build.gradle 파일에서 스프링 부트 버전 3.2.0으로 변경

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

...

 

 

 

jakarta EE로 변경

기존에 javax.persistence 패키지가 jakarta로 변경 되었다.

Spring boot 버전을 3.2.0으로 변경하고 그레이들 reload 시 인텔리제이에서 아주 많은 빨간줄을 마주치게 된다.

  

그런 경우 아래와 같이 변경하면 된다. 

(기존) import javax.persistence.*;
(변경) import jakarta.persistence.*;

 

 

참고로 인텔이제이에는 모든 프로젝트에서 단어를 찾아서 바꿀 수 있는 기능이 있으니 확인하면서 replace를 하면 조금은 쉽게 변경할 수 있다.

 

 

 

 

 

설정 파일 변경(yml)

사실 위의 내용을 변경하고 나면 서버가 뜰꺼라고 생각했는데, 다양한 오류를 마주했다

 

 

 

 

 

mySQL 라이브러리 오류

mysql 라이브러리 패키지 오류

갑자기 Could not find mysql:mysql-connector-java 에러가 발생했다.

스프링 버전이 업데이트 되면서 mysql 연동 라이브러리 코드가 변경 되어서 처리가 필요하다.

 

기존 버전

    runtimeOnly 'mysql:mysql-connector-java'

 

변경 버전

runtimeOnly 'com.mysql:mysql-connector-j'

 

 

 

 

 

스프링 시큐리티 변경 오류

 

HttpSecurity 에서 제거된 메서드를 변경해야 한다.

  • authorizeRequests() ➔ authorizeHttpRequests()
  • antMatchers() ➔ requestMatchers()
  • regexMatchers() ➔ RegexRequestMatchers()

 

아래와 같이 변경하면 된다고 한다.

http
    .securityMatcher("/api/**", "/app/**")
    .authorizeHttpRequests((authz) -> authz
        .requestMatchers("/api/admin/**").hasRole("ADMIN")
        .anyRequest().authenticated()
    );

- 스택오버플로 참고 : https://stackoverflow.com/questions/74753700/cannot-resolve-method-antmatchers-in-authorizationmanagerrequestmatcherregis

 

Cannot resolve method 'antMatchers()' in AuthorizationManagerRequestMatcherRegistry

I am currently following the Spring Documentation and some tutorials on Web Security. But now I have a problem, that I can't call the method antMatchers. This is the error I'm getting when building...

stackoverflow.com

 

 

 

 

 

 

JPA 설정 오류 (yml 설정 변경)

 

아래와 같은 오류가 발생한다면 yml 설정 변경이 필요하다. 

 

Unable to resolve name [org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy] as strategy [org.hibernate.boot.model.naming.PhysicalNamingStrategy]

2023-12-21T21:21:06.439+09:00 ERROR 70356 --- [book] [           main] o.s.boot.SpringApplication               : Application run failed

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [com/won/book/common/config/jpa/ApplicationConfiguration.class]: Unable to resolve name [org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy] as strategy [org.hibernate.boot.model.naming.PhysicalNamingStrategy]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1775) ~[spring-beans-6.1.1.jar:6.1.1]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:601) ~[spring-beans-6.1.1.jar:6.1.1]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) ~[spring-beans-6.1.1.jar:6.1.1]
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) ~[spring-beans-6.1.1.jar:6.1.1]
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-6.1.1.jar:6.1.1]
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) ~[spring-beans-6.1.1.jar:6.1.1]
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) ~[spring-beans-6.1.1.jar:6.1.1]
	at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1173) ~[spring-context-6.1.1.jar:6.1.1]
	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:936) ~[spring-context-6.1.1.jar:6.1.1]
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:616) ~[spring-context-6.1.1.jar:6.1.1]
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) ~[spring-boot-3.2.0.jar:3.2.0]
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:753) ~[spring-boot-3.2.0.jar:3.2.0]
	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:455) ~[spring-boot-3.2.0.jar:3.2.0]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:323) ~[spring-boot-3.2.0.jar:3.2.0]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1342) ~[spring-boot-3.2.0.jar:3.2.0]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1331) ~[spring-boot-3.2.0.jar:3.2.0]
	at com.won.book.BookApplication.main(BookApplication.java:12) ~[main/:na]
Caused by: org.hibernate.boot.registry.selector.spi.StrategySelectionException: Unable to resolve name [org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy] as strategy [org.hibernate.boot.model.naming.PhysicalNamingStrategy]
	at org.hibernate.boot.registry.selector.internal.StrategySelectorImpl.selectStrategyImplementor(StrategySelectorImpl.java:154) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]
	at org.hibernate.boot.registry.selector.internal.StrategySelectorImpl.resolveStrategy(StrategySelectorImpl.java:236) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]
	at org.hibernate.boot.registry.selector.internal.StrategySelectorImpl.resolveDefaultableStrategy(StrategySelectorImpl.java:180) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]
	at org.hibernate.boot.registry.selector.internal.StrategySelectorImpl.resolveDefaultableStrategy(StrategySelectorImpl.java:167) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]
	at org.hibernate.boot.internal.MetadataBuilderImpl$MetadataBuildingOptionsImpl.<init>(MetadataBuilderImpl.java:740) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]
	at org.hibernate.boot.internal.MetadataBuilderImpl.<init>(MetadataBuilderImpl.java:140) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]
	at org.hibernate.boot.MetadataSources.getMetadataBuilder(MetadataSources.java:164) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]
	at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.<init>(EntityManagerFactoryBuilderImpl.java:277) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]
	at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.<init>(EntityManagerFactoryBuilderImpl.java:198) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]
	at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:63) ~[spring-orm-6.1.1.jar:6.1.1]
	at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:376) ~[spring-orm-6.1.1.jar:6.1.1]
	at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:409) ~[spring-orm-6.1.1.jar:6.1.1]
	at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:396) ~[spring-orm-6.1.1.jar:6.1.1]
	at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.afterPropertiesSet(LocalContainerEntityManagerFactoryBean.java:352) ~[spring-orm-6.1.1.jar:6.1.1]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1822) ~[spring-beans-6.1.1.jar:6.1.1]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1771) ~[spring-beans-6.1.1.jar:6.1.1]
	... 16 common frames omitted
Caused by: org.hibernate.boot.registry.classloading.spi.ClassLoadingException: Unable to load class [org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy]
	at org.hibernate.boot.registry.classloading.internal.ClassLoaderServiceImpl.classForName(ClassLoaderServiceImpl.java:126) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]
	at org.hibernate.boot.registry.selector.internal.StrategySelectorImpl.selectStrategyImplementor(StrategySelectorImpl.java:150) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]
	... 31 common frames omitted
Caused by: java.lang.ClassNotFoundException: Could not load requested class : org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy
	at org.hibernate.boot.registry.classloading.internal.AggregatedClassLoader.findClass(AggregatedClassLoader.java:216) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]
	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:592) ~[na:na]
	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:525) ~[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 org.hibernate.boot.registry.classloading.internal.ClassLoaderServiceImpl.classForName(ClassLoaderServiceImpl.java:123) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]
	... 32 common frames omitted
Caused by: java.lang.Throwable: null
	at org.hibernate.boot.registry.classloading.internal.AggregatedClassLoader.findClass(AggregatedClassLoader.java:209) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]
	... 37 common frames omitted
	Suppressed: java.lang.ClassNotFoundException: org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy
		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:525) ~[na:na]
		at org.hibernate.boot.registry.classloading.internal.AggregatedClassLoader.findClass(AggregatedClassLoader.java:206) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]
		... 37 common frames omitted
	Suppressed: java.lang.ClassNotFoundException: org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy
		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:525) ~[na:na]
		at org.hibernate.boot.registry.classloading.internal.AggregatedClassLoader.findClass(AggregatedClassLoader.java:206) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]
		... 37 common frames omitted
	Suppressed: java.lang.ClassNotFoundException: org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy
		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:525) ~[na:na]
		at org.hibernate.boot.registry.classloading.internal.AggregatedClassLoader.findClass(AggregatedClassLoader.java:206) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]
		... 37 common frames omitted

 

 

(기존)  physical_naming_strategy 패키지명

physical_naming_strategy: org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy

 

(변경) physical_naming_strategy 패키지명

physical_naming_strategy: org.hibernate.boot.model.naming.CamelCaseToUnderscoresNamingStrategy
spring:
    jpa:
        hibernate:
          ddl-auto: create
        properties:
          hibernate:
            show_sql: true
            format_sql: true
            dialect: org.hibernate.dialect.MySQL57Dialect
            physical_naming_strategy: org.hibernate.boot.model.naming.CamelCaseToUnderscoresNamingStrategy
            default_batch_fetch_size: 500
        database: mysql

 

 

 

 

 

MySQL 설정 항목 오류

로드를 할 수 없다는 오류가 나와서 찾아 봤더니 이것도 패키지명이 변경되었다...!

Could not load requested class : org.hibernate.dialect.MySQL5InnoDBDialec

Caused by: java.lang.ClassNotFoundException: Could not load requested class : org.hibernate.dialect.MySQL5InnoDBDialect
	at org.hibernate.boot.registry.classloading.internal.AggregatedClassLoader.findClass(AggregatedClassLoader.java:216) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]
	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:592) ~[na:na]
	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:525) ~[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 org.hibernate.boot.registry.classloading.internal.ClassLoaderServiceImpl.classForName(ClassLoaderServiceImpl.java:123) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]
	... 42 common frames omitted
Caused by: java.lang.Throwable: null
	at org.hibernate.boot.registry.classloading.internal.AggregatedClassLoader.findClass(AggregatedClassLoader.java:209) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]
	... 47 common frames omitted
	Suppressed: java.lang.ClassNotFoundException: org.hibernate.dialect.MySQL5InnoDBDialect
		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:525) ~[na:na]
		at org.hibernate.boot.registry.classloading.internal.AggregatedClassLoader.findClass(AggregatedClassLoader.java:206) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]
		... 47 common frames omitted
	Suppressed: java.lang.ClassNotFoundException: org.hibernate.dialect.MySQL5InnoDBDialect
		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:525) ~[na:na]
		at org.hibernate.boot.registry.classloading.internal.AggregatedClassLoader.findClass(AggregatedClassLoader.java:206) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]
		... 47 common frames omitted
	Suppressed: java.lang.ClassNotFoundException: org.hibernate.dialect.MySQL5InnoDBDialect
		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:525) ~[na:na]
		at org.hibernate.boot.registry.classloading.internal.AggregatedClassLoader.findClass(AggregatedClassLoader.java:206) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]
		... 47 common frames omitted

 

 

(기존)

dialect: org.hibernate.dialect.MySQL5InnoDBDialect

 

 

(변경)

dialect: org.hibernate.dialect.MySQL57Dialect
반응형

댓글