안녕하세요. Dooray 에서 인증 서버를 개발하고 있는 김병부입니다.
Dooray 인증서버가 사용하는 Spring Boot 프레임 워크 버전을 업그레이드 하면서 겪은 몇가지 내용들을 공유하려고 합니다.
2022년 11월에 기존의 Spring framework 5 와 Spring Boot 2.X 버전을 대체하는 Spring framework 6와 Spring Boot 3 가 릴리즈 되었습니다.
새로운 스프링 부트 3.0은 새로 업그레이드된 수많은 라이브러리들을 포함하고 있지만, 다음과 같은 큰 특징이 있습니다.
- Java17과 Java19 지원
- GraalVM 지원
- Spring framework 6.0 기반
새로운 스프링 부트 3.0 는 Java17 을 기반으로 작성된 새로운 프레임 워크입니다.
만약 여러분들이 아직도 Java8 을 사용하고 있다면, 스프링 부트 3.0을 사용할 수 없습니다.
그래서 스프링에서도 일정 기간동안 Spring Boot 2.7과 3.0을 계속 유지 보수할 계획이라고 합니다.
다음 그림을 보면 보안 업데이트나 버그 수정은 2023년 말까지 지원한다고 하니 가능하면 빠른 시간에 업그레이드 하는 것이 좋겠습니다.

이번 스프링 부트 3.0 에는 이런 특징들 외에도 Jakarta EE 를 지원한다는 큰 변곡점이 생겼습니다.
기존에 우리는 JavaEE 를 사용하고 있었습니다.
사용자의 요청을 처리하고 적절한 응답을 전달하는 HttpServletRequest 와 HttpServletResponse 가 대표적인 클래스입니다. 이들 클래스들은 javax.servlet.http 패키지에 포함되어 있습니다.
하지만 Spring Boot 3.0 에서는 JavaEE 가 아닌 Jakarta EE 를 사용해야 하므로 jakarta.servlet.http.HttpServletRequest, jakarta.servlet.http.HttpServletResponse 를 소스 코드에 import 해야 합니다.
그러므로 기존에 Spring Boot 2.X 를 3.0 으로 업그레이드 하려면 수많은 코드를 수정해야 합니다.
Jakarta EE
2017년 하반기 오라클은 JavaEE 프로젝트를 비영리 기관인 이클립스 재단으로 이관하는 내용을 발표합니다.
이는 사실상 JavaEE 의 기술 주도권이 오라클에서 이클립스 재단으로 넘어간 사건입니다.
하지만 JavaEE 상표권은 오라클이 갖고 있어서 이클립스 재단에서도 JavaEE 패키지 이름인 javax.* 를 사용할 수 없습니다.
이클립스 재단은 Jakarta EE 라고 하고 패키지는 jakarta.* 로 명명합니다.
다음과 같은 JavaEE 이름들이 JakartaEE 이름으로 새롭게 변경되었고, 패키지 이름로 변경되었습니다.
- Java Servlet(javax.servlet) -> Jakarta Servlet(jakarta.servlet)
- Java Message Servie (javax.jms) -> Jakarta Messaging (jakart.jms)
- JPA:Java Persistence API (javax.persistence) -> Jakarta Persistence(jakarta.persistence)
- JTA:Java Transaction API (javax.transaction) -> Jakarta Transaction(jakarta.transaction)
- Java Mail (javax.mail) -> Jakarta Mail (jakarta.mail)
Spring Boot 2.7.X 에서 3.0.1 으로 업그레이드
가장 먼저 할일은 프로젝트의 JDK 버전을 올리는 일입니다. 기존에 Java17 이상을 사용하고 있었다면, 수정할 일이 없습니다.
두번째는 pom.xml 을 열어서 Spring Boot 의 버전을 올리는 일입니다
<parent/> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.0.1</version> </parent> ... <properties> <java.version>17</java.version> </properties>
스프링 프레임워크 버전을 올리면 JavaEE 가 Jakarta EE 로 변경되어 패키지와 클래스 사용법에 대 변화가 발생합니다.
스프링 프레임워크의 버전이 6.0 으로 올라가면서, 프레임 워크에서 제공하는 클래스의 사용 방법 또한 변경이 있습니다. 만약 한번에 패키지 경로를 바꾸려고 IDE 가 제공하는 replace 기능을 사용하면 곤란합니다.
‘import javax.’ 문자열을 찾아서 ‘import jakarta.’ 로 변경하지 마세요.
“javax.sql.DataSource”나 “javax.crypto.SecretKey” 같은 클래스는 JDK 에 포함되어있는 것들이며 이들은 바뀌지 않았으니까요.
대표적인 몇가지 클래스들을 나열하면 다음과 같습니다.
import javax.annotation.PostConstruct; -> import jakarta.annotation.PostConstruct; import javax.persistence.EntityManager; -> import jakarta.persistence.EntityManager; import javax.persistence.Column; -> import jakarta.persistence.Column; import javax.persistence.Convert; -> import jakarta.persistence.Convert; import javax.persistence.Entity; -> import jakarta.persistence.Entity; import javax.servlet.http.HttpServletRequest; -> import jakarta.servlet.http.HttpServletRequest;
아마도 수많은 클래스들을 다시 import 해야 합니다.
만약 다음과 같은 클래스를 사용하고 있다면, 스프링 프레임워크 6.0 에서 제공하는 클래스들로 변경해야 합니다.
다음 클래스들은 단순히 패키지 이름을 spring5 -> spring6 로 변경하면 기능을 그대로 사용할 수 있습니다.
import org.thymeleaf.spring5.SpringTemplateEngine; import org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver; import org.thymeleaf.spring5.view.ThymeleafViewResolver;
외부 라이브러리에 의존하는 경우에는 JakartaEE 에 맞게 변경된 라이브러리를 사용해야 합니다.
XML 을 처리하는 Jaxb 나 JavaMail 이 대표적입니다.
다음은 pom.xml 예제이며, 변경 전/후을 비교했습니다.
// Before Spring Boot 3.0 <dependency> <groupId>javax.xml.bind</groupId> <artifactId>jaxb-api</artifactId> <version>2.3.1</version> </dependency> <dependency> <groupId>javax.mail</groupId> <artifactId>mail</artifactId> <version>1.4.7</version> </dependency>
// After Spring Boot 3.0 <dependency> <groupId>jakarta.xml.bind</groupId> <artifactId>jakarta.xml.bind-api</artifactId> <version>4.0.0</version> </dependency> <dependency> <groupId>jakarta.mail</groupId> <artifactId>jakarta.mail-api</artifactId> <version>2.1.0</version> </dependency>
만약 프로젝트가 사내에서 만든 라이브러리(In-House Library)에 의존하는지 확인할 필요가 있습니다.
만약 이 라이브러리가 Spring Framework 6.0 에서 지원하지 않는 클래스 혹은 JavaEE 클래스를 사용하는 경우, 적절한 클래스를 찾을 수 없어서 실행되지 않습니다.
Query DSL 설정
애플리케이션이 Query DSL 을 사용하는 경우, 더 많은 주의가 필요합니다.
이 글을 쓰는 시점에서(2023/01/07) QueryDSL 최신 버전은 5.0.0 입니다.
기존 애플리케이션이 QueryDSL 5.0.0 을 사용하고 있더라도, JakartaEE 를 지원하도록 설정을 변경해줘야 합니다.
다음은 pom.xml 을 확인해봅시다. classifier 속성을 사용하여 jakarta 를 설정한것을 확인할 수 있습니다.
// FROM <dependency> <groupId>com.querydsl</groupId> <artifactId>querydsl-apt</artifactId> <version>5.0.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>com.querydsl</groupId> <artifactId>querydsl-jpa</artifactId> <version>5.0.0</version> </dependency> ------------------------------------------------------------- // TO <dependency> <groupId>com.querydsl</groupId> <artifactId>querydsl-apt</artifactId> <version>5.0.0</version> <classifier>jakarta</classifier> <scope>provided</scope> </dependency> <dependency> <groupId>com.querydsl</groupId> <artifactId>querydsl-jpa</artifactId> <version>5.0.0</version> <classifier>jakarta</classifier> </dependency>
QueryDSL 은 apt-maven-plugin 을 사용하여 컴파일 과정에서 Q-Type 클래스를 생성합니다.
하지만 querydsl-apt jakarta 버전에서는 apt-maven-plugin 이 필요 없습니다.
만약 pom.xml 에 apt-maven-plugin 설정이 있다면 다음과 같이 에러가 발생할 수 있습니다.
[INFO] --- maven-compiler-plugin:3.10.1:compile (default-compile) @
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 216 source files to ...
Attempt to recreate a file for type com.dooray.idp.domain.QMember
Attempt to recreate a file for type com.dooray.idp.domain.QTenant
...
[INFO] -------------------------------------------------------------
[ERROR] COMPILATION ERROR :
[INFO] -------------------------------------------------------------
[ERROR] Attempt to recreate a file for type com.dooray.idp.domain.QMember
[ERROR] Attempt to recreate a file for type com.dooray.idp.domain.QTenant
...
[INFO] 2 errors
이때 여러분이 설정한 apt-maven-plugin 을 제거하면 됩니다.
querydsl-apt jakarta 버전에서는 javax.annotation.processing.Processor 를 서비스로 제공하고 있고, 자바 컴파일러가 이를 사용하여 Q-class 를 target/generated-sources/annotation 에 생성합니다.
그러므로 여러분들의 apt-maven-plugin 이 이중 동작해서 에러가 발생하는 것입니다.

이와 관련된 자세한 내용은 다음 링크로 참고하세요