Post

[BE] Spring Data 2026.0 출시 6월로 연기 — 타입 안전 경로·Redis Pub/Sub·Upsert 신기능 정리

[BE] Spring Data 2026.0 출시 6월로 연기 — 타입 안전 경로·Redis Pub/Sub·Upsert 신기능 정리

서론

2026년 5월 11일, Spring 공식 블로그에 조용한 공지가 하나 올라왔다. 제목은 “May Release Train Date Changes”. 5월 11~22일로 예정됐던 Spring May 릴리스 트레인 전체가 6월 1~5일로 약 3주 밀린다는 내용이다.

Spring May 릴리스 트레인은 단일 프로젝트 업데이트가 아니다. Spring Framework, Spring Boot 4.1, Spring Data 2026.0.0, Spring Cloud, Spring Security, Spring AI 등 Spring 포트폴리오 전 프로젝트의 GA(General Availability) 버전이 일괄 배포되는 시점이다. 이 릴리스를 기다리던 팀이라면 의존성 업그레이드 일정을 6월 첫 주 이후로 조정해야 한다.

공식 블로그는 지연 이유로 “릴리스 트레인 준비에 충분한 시간을 확보하기 위함”이라고만 밝혔다. 구체적인 배경은 공개되지 않았다. Spring Data 2026.0.0은 RC1이 2026년 4월 17일에 공개됐고, 이미 GitHub 위키에 상세한 릴리스 노트가 정리된 상태다. RC1에서 GA까지 안정화 기간이 더 필요한 만큼 기다릴 가치는 충분하다.

이번 포스트에서는 연기 소식과 함께, Spring Data 2026.0.0이 실제로 어떤 기능을 들고 오는지 살펴본다. Spring Boot 4.1과 함께 출시될 이번 릴리스는 타입 안전 프로퍼티 경로, 어노테이션 기반 Redis Pub/Sub, 데이터베이스 네이티브 Upsert 등 실무 체감이 큰 변화를 담고 있다.


본론

릴리스 일정 변경 요약

구분기존 일정변경된 일정
Spring May Release Train 전체2026년 5월 11~22일2026년 6월 1~5일
Spring Boot 4.1 GA2026년 5월2026년 6월 초
Spring Data 2026.0.0 GA2026년 5월 15일2026년 6월 초
Spring Framework 7.1 GA2026년 5월2026년 6월 초
Spring Cloud 2026.0.0 GA2026년 5월2026년 6월 초

최신 일정은 calendar.spring.io에서 확인할 수 있다.

Spring Data 2026.0.0은 릴리스 이후 2027년 6월까지 OSS 지원을 받으며, 2028년 6월에 EOL(지원 종료)된다. 장기 프로젝트 의존성 계획에 참고할 만한 수치다.


타입 안전 프로퍼티 경로 (Type-Safe Property Paths)

Spring Data Commons 4.1에서 가장 눈길을 끄는 변경이다. 기존에 Sort.by("firstName")처럼 문자열로 필드명을 지정하던 방식을 메서드 레퍼런스로 대체할 수 있게 됐다.

기존 방식의 문제점

1
2
3
4
5
6
// 문자열 기반 — 오타가 컴파일 타임에 잡히지 않음
Sort sort = Sort.by("firstName", "lastName");

// Person 클래스에서 firstName → givenName으로 리팩토링하면?
// IDE가 자동으로 바꿔주지 않고, 런타임에서 예외 발생
PropertyPath path = PropertyPath.from("address.country", Person.class);

Spring Data 2026.0.0 방식

1
2
3
4
5
// 메서드 레퍼런스 기반 — 컴파일 타임 안전성 확보
Sort sort = Sort.by(Person::getFirstName, Person::getLastName);

// 중첩 경로도 체이닝으로 표현
PropertyPath path = PropertyPath.of(Person::getAddress).then(Address::getCountry);

이 변경의 실질적인 장점은 다음과 같다:

  1. 오타 방지: 컴파일 타임에 존재하지 않는 메서드 참조를 즉시 감지
  2. 리팩토링 안전성: IDE의 “Rename” 기능이 Repository 쿼리 파라미터까지 자동으로 반영
  3. 자동완성 지원: 문자열 대신 메서드 레퍼런스를 쓰므로 IDE 자동완성이 동작

JPA Repository에서 findAll(Sort), findAll(Pageable) 등을 호출할 때도 이 방식을 사용할 수 있어, 대규모 레포지터리 코드베이스의 유지보수 비용이 줄어들 것으로 기대된다.

한 가지 주의할 변경 사항도 있다. @ProjectedPayload 어노테이션이 Projection 파라미터 타입에 필수로 요구되도록 바뀌었다. 기존 코드에서 이를 사용하고 있다면 마이그레이션 전 확인이 필요하다.


Spring Data Relational — 단일 구문 Upsert

Spring Data JDBC와 Spring Data R2DBC를 포함하는 관계형 모듈에서 Upsert 기능이 Template API에 공식 추가됐다. “존재하면 UPDATE, 없으면 INSERT”를 구현하려면 기존에는 직접 SQL을 작성하거나 별도 로직이 필요했다.

이제 template.upsert(entity)를 호출하면, Spring Data가 대상 데이터베이스에 맞는 네이티브 SQL을 자동으로 선택한다.

각 데이터베이스별 내부적으로 사용되는 SQL 방식:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
-- PostgreSQL / H2 / ANSI
INSERT INTO person (id, name, email)
VALUES (?, ?, ?)
ON CONFLICT (id) DO UPDATE SET name = EXCLUDED.name, email = EXCLUDED.email;

-- MySQL / MariaDB
INSERT INTO person (id, name, email)
VALUES (?, ?, ?)
ON DUPLICATE KEY UPDATE name = VALUES(name), email = VALUES(email);

-- Oracle (MERGE 구문 활용)
MERGE INTO person p
USING (SELECT ? AS id, ? AS name, ? AS email FROM DUAL) src
ON (p.id = src.id)
WHEN MATCHED THEN UPDATE SET p.name = src.name, p.email = src.email
WHEN NOT MATCHED THEN INSERT (id, name, email) VALUES (src.id, src.name, src.email);

-- SQL Server
MERGE INTO person WITH (HOLDLOCK) AS target
USING (SELECT ? AS id, ? AS name, ? AS email) AS source
ON target.id = source.id
WHEN MATCHED THEN UPDATE SET target.name = source.name, target.email = source.email
WHEN NOT MATCHED THEN INSERT (id, name, email) VALUES (source.id, source.name, source.email);

지원 데이터베이스: ANSI, DB2, H2, MariaDB, MySQL, Oracle, PostgreSQL, SQL Server

주의해야 할 제약사항

  • 엔티티에 ID가 미리 설정되어 있어야 한다. 데이터베이스 자동 생성 ID(@GeneratedValue)를 쓰는 경우 별도 처리가 필요하다.
  • 낙관적 락(Optimistic Locking, @Version)과 함께 사용할 수 없다.
  • SQL Server에서는 IDENTITY_INSERT ON 설정이 필요한 경우가 있다.

이 기능은 배치 처리, 멱등성이 필요한 데이터 동기화 작업, 외부 시스템에서 데이터를 가져와 저장하는 파이프라인 등에서 특히 유용하다.


Spring Data Redis 4.1 — 어노테이션 기반 Pub/Sub 리스너

Spring Data Redis 4.1에서 실무 체감이 가장 클 변화는 어노테이션 기반 Redis Pub/Sub 메시지 리스너 지원이다.

기존에는 Redis 채널을 구독하려면 MessageListenerContainer를 직접 설정하고 MessageListener 인터페이스를 구현하는 번거로운 과정이 필요했다. 이제 Spring Kafka의 @KafkaListener, Spring AMQP의 @RabbitListener와 같은 패턴으로 Redis Pub/Sub를 다룰 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 1. 설정 클래스에서 리스너 스캔 활성화
@Configuration
@EnableRedisListeners
public class RedisConfig {
    // RedisConnectionFactory 등 기본 빈 설정
}

// 2. 컴포넌트 클래스에서 채널별 핸들러 메서드 선언
@Component
public class OrderEventHandler {

    @RedisListener(topic = "orders.created")
    public void handleOrderCreated(String payload) {
        // 주문 생성 이벤트 처리
    }

    @RedisListener(topic = "orders.cancelled")
    public void handleOrderCancelled(String payload) {
        // 주문 취소 이벤트 처리
    }

    @RedisListener(topic = "notifications.*")  // 와일드카드 패턴
    public void handleNotification(String payload) {
        // notifications. 로 시작하는 모든 채널 메시지 처리
    }
}

Spring Messaging에서 사용하던 MessageConverter를 그대로 재사용할 수 있어, Kafka·RabbitMQ 기반 코드와 패턴을 통일할 수 있다. 기존 MessageListenerContainer 방식도 계속 지원되므로 마이그레이션은 선택 사항이다.

추가된 Redis CAS 연산

Redis 8.4 이상 환경에서 값 기반 조건부 SET/DELETE가 지원된다:

1
2
3
4
5
// Compare-And-Set: 현재 값이 기대값과 같을 때만 새 값으로 교체
redisTemplate.compareAndSet("session-key", "old-token", "new-token");

// Compare-And-Delete: 현재 값이 기대값과 같을 때만 삭제
redisTemplate.compareAndDelete("lock-key", "expected-value");

캐시 일괄 리셋 최적화

Redis를 캐시 전용으로 사용하는 환경에서 여러 캐시를 한 번에 초기화할 때, 기존에는 캐시 수만큼 FLUSHDB 또는 DEL 명령이 발생했다. 이제 RedisCache.resetCaches()를 호출하면 단일 FLUSHDB 명령으로 처리된다:

1
2
3
4
5
6
7
// 이전: 캐시 N개를 초기화할 때 N번의 DEL 호출
cacheManager.getCache("users").clear();
cacheManager.getCache("products").clear();
// ...

// Spring Data Redis 4.1: 단일 FLUSHDB로 일괄 초기화
((RedisCacheManager) cacheManager).resetCaches();

단, Redis를 캐시 외 다른 용도(세션, Pub/Sub 등)로 함께 사용한다면 FLUSHDB가 모든 데이터를 삭제하므로 주의가 필요하다.


Spring Data MongoDB 5.1 — 멀티 컬렉션 벌크 쓰기

MongoDB 8.0 이상 환경에서는 여러 컬렉션에 걸친 벌크 쓰기를 단일 API 호출로 처리할 수 있다. 기존에는 컬렉션별로 별도의 BulkOperations를 만들고 각각 실행해야 했다.

1
2
3
4
5
6
// 하나의 API 호출로 여러 컬렉션에 혼합 연산
template.bulkWrite(List.of(
    BulkWrite.insert(user).into("users"),
    BulkWrite.update(orderCriteria, orderUpdate).into("orders"),
    BulkWrite.remove(expiredQuery).from("sessions")
));

네트워크 왕복 횟수를 줄이고 MongoDB의 내부 트랜잭션 처리 효율을 높일 수 있다. 단, MongoDB 8.0 이상에서만 지원되므로, 이전 버전을 사용 중이라면 업그레이드 여부를 먼저 확인해야 한다.


Spring Boot 4.1과의 연관성

Spring Data 2026.0.0은 Spring Boot 4.1과 함께 패키징된다. Spring Boot 4.1로 업그레이드하면 spring-boot-starter-data-jpa, spring-boot-starter-data-redis, spring-boot-starter-data-mongodb 등의 스타터가 자동으로 새 버전 모듈을 포함하게 된다.

Breaking changes 체크리스트

마이그레이션 전 반드시 확인해야 할 변경 사항:

1
2
3
4
5
6
7
8
9
10
1. @ProjectedPayload 어노테이션 필수화
   - Projection 인터페이스의 파라미터 타입에 @ProjectedPayload 미적용 시 경고
   - 향후 버전에서 에러로 전환 예정

2. Jedis → UnifiedJedis 전환 (Spring Data Redis)
   - 내부 구현이 UnifiedJedis로 변경됨
   - 대부분의 경우 외부 API 변화 없음, 하지만 직접 Jedis 클라이언트 빈을 커스터마이즈한 경우 확인 필요

3. Kotlin 2.3.20 / Vavr 0.11.0으로 버전 업
   - Kotlin 프로젝트는 Kotlin 2.3.x 호환성 확인 필요

정리

  • Spring May Release Train 6월 1~5일로 연기 (5월 11일 공지): Spring Boot 4.1, Spring Data 2026.0.0, Spring Framework 7.1 등 포트폴리오 전체 영향
  • Type-Safe Property Paths: 메서드 레퍼런스로 컴파일 타임 안전성 확보, IDE 리팩토링 완전 지원
  • Upsert API: ANSI·MySQL·PostgreSQL·Oracle·SQL Server 자동 선택, 단일 메서드 호출로 insert-or-update 처리
  • @RedisListener: Kafka/RabbitMQ와 동일한 어노테이션 패턴으로 Redis Pub/Sub 구독 — 설정 코드 대폭 간소화
  • Redis CAS/CaD: compareAndSet, compareAndDelete로 원자적 조건부 연산 지원 (Redis 8.4+)
  • MongoDB 멀티 컬렉션 Bulk Write: 여러 컬렉션을 단일 API 호출로 처리 (MongoDB 8.0+)
  • Spring 커뮤니티에서는 타입 안전 경로와 Redis Pub/Sub 지원이 가장 큰 체감 개선으로 꼽히는 분위기. 6월 GA 이후 Spring Boot 4.1 마이그레이션 가이드와 실 적용 사례가 쏟아질 전망이다.

Reference

This post is licensed under CC BY 4.0 by the author.