본문 바로가기
Spring

[Spring] @Retryable를 활용하여 깔끔하게 API 및 메소드 재호출하기

by 임채훈 2022. 2. 15.

개발을 하다보면 어떠한 로직이 성공적으로 수행되지 않았을 경우에 일종의 재시도 혹은 재호출을 하는 경우가 존재합니다.

일반적으로 클라이언트가 서버측에 특정 요청을 했을때 오류가 발생하거나 올바르지 않은 응답이 이루어질때에는 단순히 한번 더 호출 혹은 정해진 제한 횟수만큼의 재호출을 하도록 하는 경우가 있을겁니다.

아래의 코드를 한번 봅시다.

private SearchAPI searchApi;

public List<SearchTable> fetch() {
    try {
        return searchApi.search();
    } catch (Exception e) {
        throw new RuntimeException("오류 발생");
    }
}

앞선 코드에서 작성된 fetch() 메소드는 단순히 검색 API 요청을 수행한 후 정상적으로 응답이 이루어질 경우 결과를 반환하고 그렇지 않고 예외가 발생한 경우에 RumtimeException을 집어던져버리는 로직을 수행합니다.

그런데 해당 메소드에서 오류가 발생할 시 최대 5회까지 요청을 재시도하도록 코드를 변경하고자 한다면 다음과 같은 코드 형태로 처리할 수 있겠습니다.

private SearchAPI searchApi;

public List<SearchTable> fetch() {
    final int RETRY_LIMIT_COUNT = 5;
    int retryCount = 0;
    do {
        try {
            return searchApi.search();
        } catch (Exception e) {
            e.printStackTrace();
        }
    } while (++retryCount < RETRY_LIMIT_COUNT);
    throw new RuntimeException("오류 발생");
}

이번에 수정한 fetch() 메소드 또한 마찬가지로 어떤 처리가 이루어지는지 파악하기에 크게 어렵지는 않습니다.

여기서의 문제는 해당 fetch() 함수의 핵심 관심사는 단순히 검색 API를 요청하여 응답 결과를 반환하는 일인데 재시도를 하기 위해 작성한 변수, 반복문 등의 로직이 주를 이루어 코드가 지저분해지고 핵심 역할을 파악하는데 방해의 요소로 작용할 수 있어 가독성이 저하됩니다.

Spring Framework에서는 AOP를 활용한 재호출을 쉽게 처리할 수 있도록 @Retryable Annotation이 존재합니다.

해당 @Retryable 사용하게 되면 위의 장황한 코드를 다음과 같이 작성할 수 있습니다.

@Retryable(value = Exception.class, maxAttempts = 5, backoff = @Backoff(0))
public List<Movie> fetch() {
    try {
        return searchApi.search();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

한눈에 봐도 굉장히 깔끔해졌고 가독성도 무진장 좋아진것을 알 수 있습니다.

Spring 프로젝트에서 Spring Retry를 사용하기 위해서는 다음과 같은 설정을 필수로 합니다.

의존성 설정

  • Maven
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>${SPRING_VERSION}</version>
</dependency>
  • Gradle
implementation "org.springframework:spring-aspects:${SPRING_VERSION}"

Spring Legacy 환경일 경우에는 spring-aspects dependency를 추가적으로 필요로 하지만 Spring Boot프로젝트의 경우에는 spring-boot-starter-aop 의존성이 걸려있다면 별도로 spring-aspects 의존성을 걸어주지 않아도 무관합니다.

Configuration

  • Spring Boot EntryPoint 클래스에 작성
@SpringBootApplication
@EnableRetry
public class SpringBootSampleApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringBootSampleApplication.class, args);
    }

}
  • 별도의 클래스 파일에서 작성
@EnableRetry
@Configuration
public class SpringRetryConfiguration {

}

@EnableRetry 어노테이션을 통해 별도로 활성화 해주도록 합니다.

여러가지 방식이 있겠고 앞서 대표적으로 별도의 Configuration 클래스를 생성하여 어노테이션을 달아주도록 하거나 Main 클래스에 작성해줄 수 있습니다.

댓글