Contents
see List작성일 2026. 04. 05.
2025년 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 활용)
- 테스트 실행 및 통합 테스트 검증