FireDrago

Spring 7.0 재시도 기능(@Retryable) 변경점 본문

프로그래밍/Spring

Spring 7.0 재시도 기능(@Retryable) 변경점

화이용 2026. 1. 2. 12:09

spirng 7.0 (spirng-boot 4.0.1) 에서

재시도 담당하는 기능이 변경됐다.
재시도 `@Retryable` 에너테이션이 spring-core로 편입되었다.

또한 기존의 `@Recover` 에너테이션이 삭제 되었는데 그 이유가 흥미로웠다.

 

1. Spring.core 패키지로 편입

https://github.com/spring-projects/spring-framework/issues/34529

 

Introduce `@Retryable` AOP support (based on `core.retry` and Reactor retry functionality) · Issue #34529 · spring-projects/sp

There have been several requests for retry functionality at the core Spring Framework level, along the lines of the separate Spring Retry project. The 7.0 release seems like a good time to reconsid...

github.com

Spring Retry 프로젝트에서 제공하는 것과 유사한 재시도(retry) 기능을 Spring Framework 코어 레벨에서 지원해달라는 요청이 그동안 여러 차례 있었습니다. Spring 7.0 릴리스는 Spring Retry의 공통 기능들을 spring-core 및 spring-aop 모듈 내에 직접 재구현하여 포함시키는 방안을 재검토하기에 아주 적절한 시기로 보입니다.


이전 버전까지 @Retryable 에너테이션을 사용하기위해서는
`Retry` 의존성을 import 해주어야 했다.

 

스프링 7.0 부터 재시도 기능이 spring의 core 기능에서 구현하도록 변경되었다.

스프링 프레임워크의 표준 스펙으로 편입되었다.

 

1.1 `@EnableResilentMethods`에너테이션

몇가지 에너테이션 사용에도 변경점이 있다.

Retry 기능을 사용하기 위한 설정 에너테이션이 변경되었다.
`@EnableRetry` -> `@EnableResilientMethods` 에너테이션을 사용한다.

 

1.2 변경된 속성

https://docs.spring.io/spring-framework/docs/7.0.0-M7/javadoc-api/org/springframework/resilience/annotation/Retryable.html

 

Retryable (Spring Framework 7.0.0-M7 API)

A common annotation specifying retry characteristics for an individual method, or for all proxy-invoked methods in a given class hierarchy if annotated at the type level. Aligned with RetryTemplate as well as Reactor's retry support, either re-invoking an

docs.spring.io

`includes` 속성은 재시도를 수행할 예외 타입을 지정한다 .
기존 Spring Retry에서는 `include` (단수형) 또는 `value`를 사용했지만,

Spring 7.0에서는 `includes` (복수형)로 통일되었다.

 

`maxRetries` 속성은 최대 재시도 횟수를 지정하는데, 

기존 Spring Retry의 `maxAttempts`와 의미가 다르다 .
Spring Retry에서 `maxAttempts = 3`은 초기 호출을 포함한 총 3회 실행을 의미했지만,

Spring 7.0의 `maxRetries = 2`는 초기 호출을 제외한 재시도 횟수만 카운트하므로 총 3회 실행된다.

 

`delay` 속성은 재시도 간 기본 지연 시간을 밀리초 단위로 지정하며 기본값은 1000ms다 .
기존에는 `@Backoff(delay = 400)` 형태로 별도 어노테이션 안에 정의했지만,
Spring 7.0에서는 `@Retryable`에 직접 `delay = 400`으로 지정한다.
더 많은 변경점은 위의 공식문서를 참고하자

2. @Recover 삭제

이번 변경에서 가장 흥미로운 부분이다. `@Recover` 삭제에 관련하여 스프링 개발자의 코멘트를 살펴보자

프레임워크의 핵심 수준(core level)에서는,
의미적으로 서로 결합되어 동작하는 별도의 어노테이션 메서드들을 만드는 것을 지양하고 있습니다.
이는 코드의 자명성(self-descriptive)을 떨어뜨리고 기능을 발견하기 어렵게 만들기 때문입니다.
  • 암묵적 결합의 위험성: @Retryable 메서드와 @Recover 메서드 간의 연결이 코드상에 명시되지 않고 Spring의 런타임 시그니처 매칭에 의존한다. 이는 개발자가 코드를 읽을 때 흐름을 한눈에 파악하기 어렵게 만든다.
  • 발견 가능성(Discoverability) 저하: 별도의 어노테이션 메서드들이 의미적으로 결합된 구조는 IDE의 "Go to Definition" 기능 등으로 추적할 수 없다. 즉, 코드가 스스로를 설명하지 못하는(Not self-descriptive) 상태가 된다.
  • 복구(Recovery)와 리스너(Listener)의 혼동: 단순한 로그 기록(Listener)과 예외를 억제하고 대체 값을 반환하는 실제 복구(Recovery)를 구분해야 한다. 위르겐 횔러는 결과에 영향을 주지 않는 단순 로깅 로직이 복구 메서드로 오용되는 상황을 경계했다.

2.1 @Recover의 한계

@Service
public class PaymentService {
    @Retryable(include = PaymentException.class)
    public PaymentResult processPayment(String userId, BigDecimal amount) { ... }

    @Retryable(include = PaymentException.class)
    public PaymentResult processRefund(String userId, BigDecimal amount) { ... }

    @Recover
    public PaymentResult recoverPayment(
        PaymentException e, String userId, BigDecimal amount) { ... }
}

 

위 구조에서 processRefund가 실패했을 때
어떤 복구 메서드가 호출될지 코드만 봐서는 즉각적으로 알 수 없다.
시그니처가 유사한 여러 복구 메서드가 존재할 경우
런타임 매칭 오류가 발생하거나 의도치 않은 로직이 실행될 위험이 있다.

 

public PaymentResult processPayment(String userId, BigDecimal amount) {
    return retryTemplate.execute(
        context -> {
            // 비즈니스 로직 실행
            return callExternalApi(userId, amount);
        },
        context -> {
            // 명시적으로 정의된 복구 로직
            log.error("Payment recovery for user: {}", userId);
            return PaymentResult.failed("Recovery successful");
        }
    );
}

spring 7.0은 암묵적인 어노테이션 매칭 대신 프로그래밍 방식의 명시적 처리를 권장한다.
RetryTemplate을 사용하거나 직접적인 try-catch를 활용하면 복구 로직의 연결 관계가 명확해진다.

 

명시성 vs 편의성

`@Recover` 삭제는 명시성과 편의성에서 명시성의 중요성을 생각하게 해준다.
비단 재시도 기능뿐만 아니라, 결합도를 낮춰주는

이벤트 기반 아키텍처 같은 기술에서도 동일하게 나타나는 고민이다.


자동화는 당장의 개발을 편리하게 만들지만,

무분별하게 사용될 경우 시스템의 전체 흐름을 안개 속에 가두고 코드 이해를 방해하기도 한다.

때로는 직접 명시하는 것이 가장 안전하고 명확한 설계가 될 수 있다