Contents
see List2025년 11월, Spring Framework 7.0과 Spring Boot 4.0이 정식 출시되었다. Jakarta EE 11 전면 채택, 내장 레질리언스 패턴, GraalVM 24 네이티브 이미지 완전 지원, Micrometer 2 기반 통합 관찰성 등 대규모 변경이 이루어졌다. 기존 Spring Boot 3.x 프로젝트를 4.0으로 마이그레이션하는 방법을 실전 코드와 함께 살펴보자.
주요 변경사항 요약
- Jakarta EE 11 - Servlet 6.1, JPA 3.2, Bean Validation 3.1로 업그레이드
- Java 17 최소 요구 - Java 21 권장, Virtual Threads 기본 지원 강화
- 내장 레질리언스 - @Retryable, @ConcurrencyLimit 등 외부 라이브러리 없이 사용
- GraalVM 24 네이티브 - AOT 처리 개선, 빌드 시간 및 메모리 사용량 대폭 감소
- Micrometer 2 + OpenTelemetry - 트레이싱, 로그, 메트릭 통합 스타터
- 구조화 로깅 기본 지원 - ECS, GELF, Logstash 형식 내장
build.gradle 마이그레이션
// Spring Boot 3.x (이전)
plugins {
id 'org.springframework.boot' version '3.3.5'
id 'io.spring.dependency-management' version '1.1.4'
id 'java'
}
java {
sourceCompatibility = '17'
}
// Spring Boot 4.0 (이후)
plugins {
id 'org.springframework.boot' version '4.0.2'
id 'io.spring.dependency-management' version '1.2.0'
id 'java'
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
}
}
dependencies {
// Jakarta EE 11 의존성 자동 관리
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
// 새로운 관찰성 스타터
implementation 'org.springframework.boot:spring-boot-starter-opentelemetry'
// 새로운 레질리언스 스타터
implementation 'org.springframework.boot:spring-boot-starter-resilience'
}
내장 레질리언스 패턴
기존에는 Resilience4j 같은 외부 라이브러리가 필요했지만, Spring Framework 7부터는 프레임워크 자체에 레질리언스 어노테이션이 포함되었다.
@Retryable - 자동 재시도
import org.springframework.resilience.annotation.Retryable;
import org.springframework.resilience.annotation.EnableResilientMethods;
@Configuration
@EnableResilientMethods
public class ResilienceConfig {
}
@Service
public class ExternalApiService {
// 최대 3회 재시도, 지수 백오프
@Retryable(
maxAttempts = 3,
backoff = @Backoff(delay = 1000, multiplier = 2.0),
retryFor = {IOException.class, TimeoutException.class},
noRetryFor = {IllegalArgumentException.class}
)
public ApiResponse callExternalApi(String endpoint) {
return restClient.get()
.uri(endpoint)
.retrieve()
.body(ApiResponse.class);
}
// 재시도 실패 시 폴백
@Retryable(maxAttempts = 3, fallbackMethod = "getCachedData")
public Data fetchData(String key) {
return externalService.getData(key);
}
public Data getCachedData(String key, Exception ex) {
log.warn("폴백 실행: {}", ex.getMessage());
return cacheService.get(key);
}
}
@ConcurrencyLimit - 동시성 제어
@Service
public class ResourceIntensiveService {
// 동시 실행 최대 10개로 제한
@ConcurrencyLimit(permits = 10)
public ProcessResult heavyProcessing(InputData data) {
// CPU 집약적 작업
return process(data);
}
// 세마포어 + 타임아웃
@ConcurrencyLimit(
permits = 5,
timeout = 5000, // 5초 대기 후 거부
rejectedHandler = "handleRejection"
)
public Report generateReport(ReportRequest request) {
return reportGenerator.generate(request);
}
public Report handleRejection(ReportRequest request, ConcurrencyLimitException ex) {
return Report.queued(request.getId());
}
}
구조화 로깅 설정
Spring Boot 4.0에서는 구조화 로깅이 내장되어 있어 설정만으로 바로 사용할 수 있다.
# application.yml
logging:
structured:
format:
console: ecs # Elastic Common Schema
file: logstash # Logstash JSON 형식
# 파일 출력 설정
file:
name: /var/log/app/application.log
max-size: 100MB
max-history: 30
# 또는 GELF 형식
logging:
structured:
format:
console: gelf
gelf:
host: my-app-server
service: user-service
구조화 로깅 출력 예시
// ECS 형식 출력
{
"@timestamp": "2026-04-05T14:30:00.123Z",
"log.level": "INFO",
"message": "사용자 로그인 성공",
"service.name": "user-service",
"trace.id": "abc123def456",
"span.id": "789ghi",
"user.id": "12345",
"ecs.version": "1.2.0"
}
OpenTelemetry 통합
# application.yml - OpenTelemetry 설정
management:
opentelemetry:
enabled: true
exporter:
otlp:
endpoint: http://otel-collector:4317
protocol: grpc
resource:
attributes:
service.name: my-service
service.version: 1.0.0
deployment.environment: production
tracing:
sampling:
probability: 0.1 # 10% 샘플링
metrics:
export:
otlp:
enabled: true
// 커스텀 스팬 생성
import io.micrometer.tracing.Tracer;
import io.micrometer.tracing.ScopedSpan;
@Service
public class OrderService {
private final Tracer tracer;
public Order createOrder(OrderRequest request) {
ScopedSpan span = tracer.startScopedSpan("order.create");
try {
span.tag("order.type", request.getType());
span.tag("order.items.count",
String.valueOf(request.getItems().size()));
Order order = processOrder(request);
span.tag("order.id", order.getId());
return order;
} catch (Exception ex) {
span.error(ex);
throw ex;
} finally {
span.end();
}
}
}
GraalVM 네이티브 이미지 빌드
# Gradle 네이티브 빌드
./gradlew nativeCompile
# 또는 Buildpacks 사용
./gradlew bootBuildImage \
--imageName=myapp:native \
-Pnative
# 네이티브 실행
./build/native/nativeCompile/myapp
# 시작 시간: ~50ms (JVM: ~2000ms)
# 메모리 사용량: ~50MB (JVM: ~250MB)
마이그레이션 체크리스트
- Java 버전을 21 이상으로 업그레이드
- build.gradle 또는 pom.xml의 Spring Boot 버전을 4.0.x로 변경
- javax.* 패키지가 남아있다면 jakarta.*로 변경 (3.x에서 이미 완료했어야 함)
- Resilience4j를 사용 중이라면 내장 @Retryable로 전환 검토
- Micrometer 1.x 커스텀 메트릭을 Micrometer 2.x API로 업데이트
- Spring Security 설정에서 deprecated 메서드 제거
- application.properties/yml의 변경된 속성명 확인 (spring-boot-properties-migrator 활용)
- 테스트 실행 및 통합 테스트 검증