[TIPS] Spring Boot 2.x ➔ 3.x 완벽 마이그레이션 가이드: Java 17+·javax에서 jakarta 네임스페이스 전환·Spring Security 6 핵심 변경점 및 치명적 에러 해결
💡 핵심 요약: 2023년 11월 24일부로 Spring Boot 2.7.x의 공식 오픈소스 지원(OSS EOL)이 공식 종료됨에 따라, 엔터프라이즈 환경에서 Spring Boot 3.x(최신 3.3/3.4 LTS 계열)로의 전환은 선택이 아닌 보안과 컴플라이언스를 위한 필수 과제가 되었습니다. 하지만 단순히 build.gradle이나 pom.xml의 버전 숫자만 올렸다가는 Java 17+ baseline 강제, Oracle 상표권 분쟁으로 촉발된 javax.*에서 jakarta.*로의 패키지 네임스페이스 전면 교체, Spring Security 6의 WebSecurityConfigurerAdapter 삭제 및 람다 DSL 강제, Hibernate 6 및 QueryDSL 5.x의 Q클래스 충돌 등 수많은 빌드·런타임 에러를 직면하게 됩니다. 본 가이드에서는 2.x와 3.x의 생태계 변화와 최신 표준 아키텍처, Gradle과 Maven을 아우르는 실무 프로덕션 설정 코드, 그리고 4대 치명적 마이그레이션 에러에 대한 종결형 해결책을 총정리합니다.
1. 왜 과거 Spring Boot 2.x 방식은 3.x에서 깨지는가?
수많은 엔터프라이즈 레거시 시스템이 여전히 Spring Boot 2.7 환경에 머물러 있는 이유는 "단순 버전 업그레이드가 아니라 내부 플랫폼의 기반이 통째로 뒤바뀌었기 때문"입니다. Spring Boot 2.x 코드를 3.x 환경으로 그대로 가져왔을 때 파괴적인 컴파일 및 런타임 장애가 발생하는 근본 원인은 다음과 같습니다.
1) Spring Boot 2.x의 공식 수명 주기 종료(EOL)와 Java 17 최소 요구
Spring Boot 2.7은 Spring Boot 2.x 세대의 마지막 릴리스였습니다. Spring 공식 팀은 2023년 11월을 기점으로 Spring Boot 2.7.x의 무료 오픈소스 지원(OSS EOL)을 공식 종료했습니다. 이후 발견되는 치명적인 CVE 보안 취약점(예: Spring Framework 원격 코드 실행 취약점, 내장 Tomcat 취약점 등)은 유료 상용 라이선스를 구매하지 않는 한 패치가 제공되지 않습니다.
더불어 Spring Boot 3.0부터는 최소 요구 JDK 버전이 Java 17 Baseline으로 상향되었습니다. 과거 시스템에서 널리 쓰이던 Java 8 및 Java 11은 런타임 단계에서 바이트코드 버전 불일치(UnsupportedClassVersionError: 61.0)로 실행 자체가 거부됩니다. Java 17은 최소한의 진입 조건일 뿐이며, 현재 프로덕션 환경에서는 성능 최적화와 가상 스레드(Virtual Threads) 지원을 위해 Java 21 LTS 사용이 적극 권장됩니다.
2) Oracle과의 상표권 분쟁과 javax.* ➔ jakarta.* 네임스페이스 대전환
Java 생태계 역사상 가장 파괴적인 변화 중 하나는 Java EE의 Eclipse 재단 이전과 그에 따른 상표권 분쟁입니다. Oracle이 Java라는 단어 및 javax.* 패키지 이름에 대한 상표권을 Eclipse 재단에 양도하지 않기로 결정함에 따라, Jakarta EE 9부터 모든 표준 패키지명이 javax.*에서 jakarta.*로 강제 변경되었습니다.
[Java EE / Jakarta EE 패키지 네임스페이스 변천사]
Java EE 8 (과거 레거시) ────────► javax.servlet.*, javax.persistence.*, javax.validation.*
│ (Oracle 상표권 분쟁으로 javax 명칭 사용 불허)
▼
Jakarta EE 9 / 10 (현대 표준) ───► jakarta.servlet.*, jakarta.persistence.*, jakarta.validation.*
Spring Boot 2.7 ────────────────► javax.* 네임스페이스 사용 (Tomcat 9, Servlet 4.0)
Spring Boot 3.x ────────────────► jakarta.* 네임스페이스 강제 (Tomcat 10+, Servlet 6.0)
Spring Boot 3.x는 Jakarta EE 10 스펙을 기반으로 동작하므로, 서블릿 필터(Filter), 인터셉터(HandlerInterceptor), JPA 엔티티(@Entity, @Table), 유효성 검증(@NotNull, @Valid) 등 애플리케이션 전반에 걸친 모든 javax.* 패키지가 컴파일 시점에 즉각적인 미인식(Package javax does not exist) 오류를 유발합니다.
3) Spring Security 6의 WebSecurityConfigurerAdapter 완전 삭제와 람다 DSL 강제
Spring Boot 2.x 시절 수년간 관행처럼 사용되던 상속 기반의 보안 설정 클래스인 WebSecurityConfigurerAdapter는 Spring Security 5.7에서 Deprecated 처리된 후, Spring Security 6.0(Spring Boot 3.0)에서 클래스 자체가 물리적으로 완전히 삭제되었습니다.
// [과거 레거시 방식 - Spring Boot 3.x에서 클래스 부재로 컴파일 에러 발생]
@Configuration
@EnableWebSecurity
public class LegacySecurityConfig extends WebSecurityConfigurerAdapter { // ❌ 컴파일 에러! 클래스 삭제됨
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable() // ❌ 메서드 체이닝 and() 제거됨
.authorizeRequests() // ❌ authorizeRequests() 제거됨
.antMatchers("/api/**").permitAll() // ❌ antMatchers() 제거됨
.anyRequest().authenticated();
}
}
또한 기존의 유창한 체이닝 방식(.and())과 authorizeRequests(), antMatchers() 메서드가 완전히 제거되었으며, 함수형 람다 DSL(Customizer.withDefaults())과 컴포넌트 빈 등록 기반 아키텍처가 의무화되었습니다.
2. 최신 공식 표준 해법: Spring Boot 3.3/3.4 & Jakarta EE 10
Spring Boot 3.x 생태계에서 프로덕션 애플리케이션을 안정적으로 구축하기 위한 공식 표준 아키텍처 원칙은 다음과 같습니다.
1) Java 17 / 21 LTS 런타임 표준
Spring Boot 3.x는 현대적인 Java LTS(Java 17 및 Java 21) 환경에 최적화되어 있습니다.
- Java 17 LTS:
record클래스를 활용한 불변 DTO 생성, 패턴 매칭(Pattern Matching for switch / instanceof), 텍스트 블록(Text Blocks)을 통한 직관적인 멀티라인 쿼리 작성이 기본 지원됩니다. - Java 21 LTS: 가상 스레드(Virtual Threads / Project Loom)를 공식 지원하여
spring.threads.virtual.enabled=true옵션 하나만으로 톰캣 서블릿 요청을 논블로킹에 준하는 높은 동시성으로 처리할 수 있습니다. I/O 블로킹 작업이 빈번한 대규모 트래픽 서비스에서 CPU 및 메모리 점유율을 획기적으로 낮출 수 있습니다.
2) Jakarta EE 10 규격의 완전한 준수
모든 웹 및 데이터 레이어 의존성은 Jakarta EE 10 기반의 구현체를 참조해야 합니다.
- 서블릿 컨테이너: 내장 Tomcat 10.1+ 또는 Jetty 12+ 엔진이 탑재되어 Servlet 6.0 스펙을 지원합니다.
- 영속성 계층: JPA 3.1 스펙을 충족하는 Hibernate ORM 6.x를 기본 탑재합니다.
- 유효성 검증: Bean Validation 3.0 스펙의
jakarta.validation-api및 Hibernate Validator 8.x가 적용됩니다.
3) Spring Security 6 컴포넌트 기반 아키텍처 (SecurityFilterChain)
상속 구조를 탈피하고, SecurityFilterChain을 스프링 빈(@Bean)으로 노출하는 함수형 선언 방식을 취합니다. HttpSecurity 설정 시 모든 하위 구성은 람다 표현식(Lambda DSL)을 사용하므로 괄호의 중첩 구조를 통해 보안 정책의 스코프를 명확하게 시각화할 수 있습니다. 또한 인증 매니저(AuthenticationManager) 역시 커스텀 빈으로 노출하여 유연하게 주입합니다.
3. 버전 및 라이브러리 호환성 매트릭스
마이그레이션 작업 착수 전, 빌드 도구와 하위 주요 라이브러리의 버전 매트릭스를 반드시 대조해야 합니다.
| 구성 요소 (Component) | Spring Boot 2.7.x (레거시) | Spring Boot 3.3 / 3.4 (최신 표준) | 주요 변경 및 호환성 주의 사항 |
|---|---|---|---|
| Java JDK Baseline | Java 8, 11, 17 | Java 17, 21 LTS 권장 | Java 8/11 지원 완전 중단, 바이트코드 61+ 요구 |
| Java EE / Jakarta | Java EE 8 (javax.*) |
Jakarta EE 10 (jakarta.*) |
네임스페이스 완전 변경 (서블릿, JPA, 검증 등) |
| Servlet Specification | Servlet 4.0 (Tomcat 9.0) | Servlet 6.0 (Tomcat 10.1+) | 쿠키 파싱, 멀티파트 핸들링 로직 내부 변경 |
| Spring Framework | Spring Framework 5.3.x | Spring Framework 6.1 / 6.2 | Ahead-Of-Time (AOT), GraalVM 네이티브 이미지 지원 |
| Spring Security | Spring Security 5.7.x | Spring Security 6.3 / 6.4 | WebSecurityConfigurerAdapter 삭제, 람다 DSL 강제 |
| ORM / JPA | Hibernate 5.6.x | Hibernate ORM 6.5+ | SQM 엔진 도입, Legacy Dialect 삭제, 반환 타입 변경 |
| QueryDSL | querydsl-jpa:5.0.0 |
querydsl-jpa:5.0.0:jakarta |
APT 플러그인 설정 및 classifier 변경 필수 |
| API 문서화 | Springfox 3.x (Swagger) | Springdoc OpenAPI v2 (2.6+) | Springfox 3.x는 Boot 3.x 미지원으로 전면 교체 필수 |
| JSON 직렬화 | Jackson 2.13.x | Jackson 2.17+ / 2.18+ | Java 8 날짜/시간 모듈 기본 포함, 레코드 타입 기본 역직렬화 |
| 모니터링 / 메트릭 | Micrometer 1.9.x | Micrometer 1.13+ (Tracing) | Spring Cloud Sleuth ➔ Micrometer Tracing으로 전면 이관 |
4. 실무 프로덕션 설정 코드
Spring Boot 3.3/3.4 기반 프로덕션 환경에 즉시 적용 가능한 표준 빌드 스크립트와 보안 설정 예시입니다.
1) build.gradle (최신 표준 의존성 및 QueryDSL 5 Jakarta 설정)
plugins {
id 'java'
id 'org.springframework.boot' version '3.3.5'
id 'io.spring.dependency-management' version '1.1.6'
}
group = 'com.example'
version = '1.0.0-SNAPSHOT'
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21) // 또는 JavaLanguageVersion.of(17)
}
}
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
dependencies {
// 1. Spring Boot Web & Validation (Jakarta EE 10 기반)
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-validation'
// 2. Spring Security 6
implementation 'org.springframework.boot:spring-boot-starter-security'
// 3. Spring Data JPA & Database Driver
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
runtimeOnly 'com.mysql:mysql-connector-j'
// 4. QueryDSL 5.x (Boot 3.x / Jakarta 호환 핵심 설정)
implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta'
annotationProcessor 'com.querydsl:querydsl-apt:5.0.0:jakarta'
annotationProcessor 'jakarta.annotation:jakarta.annotation-api'
annotationProcessor 'jakarta.persistence:jakarta.persistence-api'
// 5. OpenAPI v2 (Swagger 교체 표준)
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.6.0'
// 6. Lombok & Developer Tools
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
// 7. Test Suite
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.security:spring-security-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}
tasks.named('test') {
useJUnitPlatform()
}
2) pom.xml (Maven 프로젝트 전환 시 대응 설정)
Maven을 사용하는 프로젝트의 경우 pom.xml의 QueryDSL APT 플러그인과 컴파일러 플러그인을 다음과 같이 Jakarta 스펙에 맞춰 명시해야 합니다:
<dependencies>
<!-- QueryDSL JPA Jakarta Classifier -->
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-jpa</artifactId>
<version>5.0.0</version>
<classifier>jakarta</classifier>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.13.0</version>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-apt</artifactId>
<version>5.0.0</version>
<classifier>jakarta</classifier>
</path>
<path>
<groupId>jakarta.persistence</groupId>
<artifactId>jakarta.persistence-api</artifactId>
<version>3.1.0</version>
</path>
<path>
<groupId>jakarta.annotation</groupId>
<artifactId>jakarta.annotation-api</artifactId>
<version>2.1.1</version>
</path>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
3) SecurityConfig.java (Spring Security 6 표준 람다 DSL)
package com.example.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
@EnableWebSecurity
@EnableMethodSecurity // @Secured, @PreAuthorize 활성화
public class SecurityConfig {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authConfig) throws Exception {
return authConfig.getAuthenticationManager();
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
// 1. CSRF & CORS 설정 (람다 DSL)
.csrf(AbstractHttpConfigurer::disable)
.cors(Customizer.withDefaults())
// 2. 세션 정책 설정 (REST API 무상태 세션)
.sessionManagement(session ->
session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
)
// 3. 인가(Authorization) 정책 설정: requestMatchers 표준 채택
.authorizeHttpRequests(auth -> auth
.requestMatchers("/", "/health", "/css/**", "/js/**", "/images/**").permitAll()
.requestMatchers("/swagger-ui/**", "/v3/api-docs/**").permitAll()
.requestMatchers("/api/v1/auth/**").permitAll()
.requestMatchers("/api/v1/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
// 4. 기본 HTTP Basic 및 Form Login 비활성화 (필요 시 커스텀 엔드포인트 연결)
.httpBasic(AbstractHttpConfigurer::disable)
.formLogin(AbstractHttpConfigurer::disable);
return http.build();
}
}
4) application.yml 변경 속성 (Spring Boot 3.x 표준)
spring:
application:
name: migration-demo-service
# Java 21 가상 스레드 공식 활성화 (Boot 3.2+)
threads:
virtual:
enabled: true
datasource:
url: jdbc:mysql://localhost:3306/service_db?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC&characterEncoding=UTF-8
username: app_user
password: app_password
# MySQL 최신 드라이버 클래스명 (com.mysql.cj.jdbc.Driver 표준)
driver-class-name: com.mysql.cj.jdbc.Driver
hikari:
maximum-pool-size: 20
minimum-idle: 10
idle-timeout: 30000
connection-timeout: 30000
jpa:
hibernate:
ddl-auto: validate
# ❌ [제거 대상]: spring.jpa.database-platform 설정은 Hibernate 6에서 자동 감지하므로 명시하지 않음
open-in-view: false
properties:
hibernate:
format_sql: true
show_sql: false
default_batch_fetch_size: 100
server:
port: 8080
# Tomcat 10.1 리소스 인코딩 표준
servlet:
encoding:
charset: UTF-8
force: true
5. 자주 겪는 4대 치명적 에러 트러블슈팅
실무 마이그레이션 현장에서 발생하는 가장 빈도 높고 파괴적인 4대 에러의 원인과 해결책입니다.
1) ClassNotFoundException: javax.servlet.http.HttpServletRequest
- 발생 증상: 컴파일 또는 런타임 시작 시점에
java.lang.ClassNotFoundException: javax.servlet.Filter또는java.lang.NoClassDefFoundError: javax/servlet/http/HttpServletRequest가 발생하며 WAS 구동 실패. - 근본 원인: Spring Boot 3.x의 톰캣 10+은 오직
jakarta.servlet패키지만 로드합니다. 소스 코드 내부 또는 과거 버전의 서드파티 라이브러리(JWT 필터, 로깅 유틸 등)가 여전히javax.servlet을 참조하고 있기 때문입니다. - 해결 절차:
- 프로젝트 전체 검색/바꾸기(Replace in Path)를 통해 모든 자바 소스 파일의 패키지 임포트를 일괄 변경합니다:
// ❌ AS-IS (제거 대상 레거시 임포트)
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.validation.constraints.NotNull;
import javax.annotation.PostConstruct;
// ✅ TO-BE (수정 대상 Jakarta 10 표준 임포트)
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.validation.constraints.NotNull;
import jakarta.annotation.PostConstruct;
- 외부 라이브러리 내부에서
javax.servlet을 호출하고 있다면 해당 라이브러리를 Jakarta EE 10 대응 버전(예:jjwt-api:0.12.x,springdoc-openapi-starter-webmvc-ui:2.x)으로 즉시 업그레이드해야 합니다.
2) The method antMatchers() is undefined for the type ...
- 발생 증상: Security 설정 작성 시
antMatchers(),mvcMatchers(),regexMatchers()메서드에서 컴파일 에러 발생. - 근본 원인: Spring Security 6에서는 보안 취약점 유발 소지가 있었던 패턴 매처들을 단일화된
requestMatchers()로 완전히 통폐합했습니다. 기존에 MVC 서블릿 컨텍스트 경로와 일반 서블릿 경로 간 매핑 불일치로 발생하던 인가 우회 취약점을 근본적으로 차단하기 위한 설계 개선입니다. - 해결 절차:
// ❌ AS-IS: Spring Boot 2.x (Security 5.x)
http.authorizeRequests()
.antMatchers("/public/**").permitAll()
.antMatchers(HttpMethod.POST, "/api/orders").hasRole("USER")
.antMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated();
// ✅ TO-BE: Spring Boot 3.x (Security 6.x 람다 DSL + requestMatchers)
http.authorizeHttpRequests(auth -> auth
.requestMatchers("/public/**").permitAll()
.requestMatchers(HttpMethod.POST, "/api/orders").hasRole("USER")
.requestMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
);
3) QueryDSL 5.x의 javax.annotation.Generated Q클래스 충돌
- 발생 증상: Gradle 빌드(
gradle build또는compileJava) 시java: package javax.annotation does not exist에러가 발생하며 Q클래스 생성이 중단됨. - 근본 원인: 기본
querydsl-apt라이브러리는 빌드 시 Q클래스 상단에javax.annotation.processing.Generated애너테이션을 생성하도록 컴파일러에 지시합니다. JDK 17+ 및 Jakarta 환경에는 해당 패키지가 존재하지 않아 빌드가 깨집니다. - 해결 절차:
QueryDSL 5.x 의존성에 :jakarta 분류자(classifier)를 지정하고, 어노테이션 프로세서 의존성을 Jakarta 네임스페이스로 교체합니다:
// ❌ AS-IS (레거시 QueryDSL 설정) implementation 'com.querydsl:querydsl-jpa:5.0.0' annotationProcessor 'com.querydsl:querydsl-apt:5.0.0:jpa' // ✅ TO-BE (Spring Boot 3.x Jakarta 분류자 필수 적용) implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta' annotationProcessor 'com.querydsl:querydsl-apt:5.0.0:jakarta' annotationProcessor 'jakarta.annotation:jakarta.annotation-api' annotationProcessor 'jakarta.persistence:jakarta.persistence-api'
4) Hibernate 6 전환에 따른 Legacy SQL Dialect 미지원 및 일괄 업데이트 쿼리 변경
- 발생 증상 1: 애플리케이션 시작 시
org.hibernate.boot.registry.classloading.spi.ClassLoadingException: Unable to load class [org.hibernate.dialect.MySQL57Dialect]또는MySQL8Dialect로딩 에러 발생. - 발생 증상 2:
@Query나 JPQL 실행 시ClassCastException: class java.lang.Long cannot be cast to class java.lang.Integer예외 발생. - 근본 원인: Hibernate 6은 새로운 SQM(Semantic Query Model) 엔진을 도입하면서 과거 수십 개로 쪼개져 있던 버전별 방언(Dialect) 클래스들을 대거 폐기하고 최신 단일 방언(
MySQLDialect,PostgreSQLDialect등)으로 통합했습니다. 또한COUNT()함수의 반환 타입이 JPA 표준 스펙에 따라 무조건Long타입으로 고정되었습니다. - 해결 절차:
application.yml에서 수동으로 지정하던spring.jpa.database-platform및hibernate.dialect설정을 완전히 삭제합니다. Hibernate 6이 JDBC 연결 메타데이터를 분석하여 최적의 방언을 100% 자동 결정합니다.- 리포지토리 레이어에서 집계 쿼리의 리턴 타입을
Long으로 일괄 수정합니다:
// ❌ AS-IS: 레거시 Hibernate 5 시절 Integer 캐스팅 허용 코드
@Query("SELECT COUNT(u) FROM User u WHERE u.status = :status")
Integer countByStatus(@Param("status") UserStatus status); // 런타임 캐스팅 예외 위험!
// ✅ TO-BE: Hibernate 6 표준 Long 반환 규격 준수
@Query("SELECT COUNT(u) FROM User u WHERE u.status = :status")
Long countByStatus(@Param("status") UserStatus status);
6. 빌드 검증 및 5단계 최종 마이그레이션 체크리스트
실제 배포 전, 아래 5단계 체크리스트를 순차적으로 통과해야만 무중단 릴리스가 보장됩니다.
[Spring Boot 3.x 마이그레이션 5단계 파이프라인]
1. JDK 17/21 환경 확인 ──► 2. 의존성 3.x 교체 ──► 3. javax ➔ jakarta 전면 치환
│
5. 통합 테스트 & 모니터링 검증 ◄── 4. Spring Security 6 람다 DSL 리팩토링
-
Step 1: 런타임 JDK 환경 일치 확인
- 개발 로컬 장비, Jenkins/GitHub Actions CI 러너, Dockerfile 베이스 이미지의 JDK 버전이 Java 17 또는 Java 21 이상인지 확인합니다 (
java -version). build.gradle의sourceCompatibility및toolchain설정이 올바르게 구성되었는지 검증합니다.
- 개발 로컬 장비, Jenkins/GitHub Actions CI 러너, Dockerfile 베이스 이미지의 JDK 버전이 Java 17 또는 Java 21 이상인지 확인합니다 (
-
Step 2: 네임스페이스 및 서드파티 호환성 일괄 교체
- 소스코드 내
javax.servlet,javax.persistence,javax.validation,javax.annotation참조를 모두jakarta.*로 치환합니다. - Boot 3.x와 호환되지 않는 Springfox Swagger를
springdoc-openapi-starter-webmvc-ui로 교체합니다. - 오래된 서드파티 라이브러리가 Jakarta EE 10 스펙을 지원하는 최신 버전인지 확인합니다.
- 소스코드 내
-
Step 3: QueryDSL Jakarta APT 설정 적용
build.gradle에:jakarta분류자가 적용되었는지 확인하고./gradlew clean compileJava명령을 통해build/generated경로에 Q클래스가 정상 컴파일되는지 검증합니다.- IDE(IntelliJ IDEA) 설정에서 Annotation Processors 옵션이 활성화되어 있는지 확인합니다.
-
Step 4: Spring Security 6 체계 전환
WebSecurityConfigurerAdapter상속을 제거하고SecurityFilterChain빈 구성으로 전면 개편합니다.antMatchers()를requestMatchers()로 변경하고, 모든 HTTP 보안 구성을 람다 DSL 방식으로 리팩토링합니다.
-
Step 5: 클린 빌드 및 엔드투엔드(E2E) 테스트 통과
./gradlew test명령을 실행하여 모든 단위 테스트 및@SpringBootTest통합 테스트가 100% 통과하는지 확인합니다.- Actuator 메트릭(
/actuator/health,/actuator/metrics) 및 DB 커넥션 풀(HikariCP)이 정상 가동되는지 로컬 프로파일에서 검증 후 프로덕션 배포 파이프라인으로 승격합니다. - Java 21 가상 스레드 적용 시 부하 테스트(JMeter, k6 등)를 수행하여 스레드 핀닝(Thread Pinning) 현상이 발생하는 서드파티 라이브러리가 없는지 점검합니다.