토이 프로젝트 백엔드 서비스의 스프링 부트 버전을 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
- [공식문서] 스프링부트 3.0 마이그레이션 가이드
https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-3.0-Migration-Guide
사전 작업
문서를 확인했다면, 사전 준비 작업을 하자.
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
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
'Java | spring > Spring' 카테고리의 다른 글
스프링 AOP 기본 개념 잡기! (1) | 2024.01.03 |
---|---|
스프링 트랜잭션 @Transactional 개념 (+주요 설정값) (0) | 2021.06.04 |
알고는 써야지! 기본 spring annotation 종류 (0) | 2021.05.17 |
Model 2 방식과 스프링 MVC (0) | 2021.05.14 |
스프링 MVC의 주요 구성 요소 (0) | 2021.05.14 |
댓글