REST 기반의 간단한 분산 트랜잭션 구현 - 4편 REST Retry

Share
REST 기반의 간단한 분산 트랜잭션 구현 - 4편 REST Retry

이 글은 읽기 전에 이전 글까지 모두 읽을 필요는 없습니다. 다만 맥락을 이해하기 위해서 1편은 미리 읽는 편이 좋습니다.



지난 글까지는 REST 기반의 분산 트랜잭션 구현 방법 중 하나인 TCCTry-Confirm/Cancel를 다루었다. 분산 환경에서는 네트워크 오류나 일시적인 서비스 중지 등으로 인해 일시적으로 REST 요청이 실패할 수 있다. 이러한 문제는 REST 요청을 재시도Retry하여 보안할 수 있다.

그림. TCC 시나리오
그림. TCC 시나리오

이번 글은 스프링 Retry 라이브러리를 사용하여 위의 TCC 시나리오에서 REST 요청 실패 시 재시도 하도록 구현한다.

스프링 Retry

스프링 Retry는 스프링 애플리케이션에서 작업이 실패하였을 때 자동으로 재 시도하는 메커니즘을 제공한다. 최초에 스프링 배치Spring Batch에서 시작되었으며, 현재는 분리되어 스프링 배치와 상관없이 독립적으로 사용할 수 있게 되었는데, 주로 템플릿/콜백 패턴Template/Callback Pattern[1]을 구현한 RetryTemplate을 제공하여 프로그래밍할 수 있게 했다.

// Set the max attempts including the initial attempt before retrying
// and retry on all exceptions (this is the default):
SimpleRetryPolicy policy = new SimpleRetryPolicy(5, Collections.singletonMap(Exception.class, true));
 
// Use the policy...
RetryTemplate template = new RetryTemplate();
template.setRetryPolicy(policy);
template.execute(new RetryCallback<Foo>() {
    public Foo doWithRetry(RetryContext context) {
        // business logic here
    }
});

RetryTemplate 이외에도 Java 어노테이션 방식도 지원한다.

@Service
class Service {
    @Retryable(RemoteAccessException.class)
    public void service(String str1, String str2) {
        // ... do something
    }
    @Recover
    public void recover(RemoteAccessException e, String str1, String str2) {
       // ... error handling making use of original args if required
    }
}

이 글에서는 RetryTemplate를 사용한다.

OrderService

REST 호출 주체가 OrderService이기 때문에 스프링 Retry를 OrderService에 적용한다. 먼저 스프링 Retry를 사용하기 위해 Maven POMProject Object Model에 의존성을 추가한다.[2]

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    // ...
    <dependencies>
        // ...
        <dependency>
            <groupId>org.springframework.retry</groupId>
            <artifactId>spring-retry</artifactId>
        </dependency>
    </dependencies>
</project>

그리고 RetryTemplate을 Spring Bean으로 등록한다.

@Configuration
public class AppConfig {
 
    @Bean
    public RetryTemplate retryTemplate() {
        RetryTemplate retryTemplate = new RetryTemplate();
 
        // 재시도 간격을 2초로 설정
        FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy();
        fixedBackOffPolicy.setBackOffPeriod(2000l);
        retryTemplate.setBackOffPolicy(fixedBackOffPolicy);
 
        // 재시도 최대 횟수를 5번으로 설정
        SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
        retryPolicy.setMaxAttempts(5);
        retryTemplate.setRetryPolicy(retryPolicy);
 
        return retryTemplate;
    }
}

OrderService에서 REST 연결을 책임지는 객체는 TccRestAdapterImpl이다. TccRestAdapterImpl은 스프링 RestTemplate을 사용하여 REST를 호출하고 있다. REST 호출하는 부분을 RetryTemplate로 감싸서 예외 발생 시 재시도 하도록 작성한다.

@Component
public class TccRestAdapterImpl implements TccRestAdapter {
    // ...
    @Autowired
    private RetryTemplate retryTemplate;
 
    // ...
    @Override
    public void confirmAll(List<ParticipantLink> participantLinks) {
        participantLinks.forEach(participantLink -> {
            try {
                retryTemplate.execute((RetryCallback<Void, RestClientException>) context -> {
                    restTemplate.put(participantLink.getUri(), null);
                    return null;
                });
 
            } catch (RestClientException e) {
                log.error(String.format("TCC - Confirm Error[URI : %s]", participantLink.getUri().toString()), e);
            }
        });
    }
}

위의 코드에서는 REST PUT 호출(restTemplate.put()) 시 RestClientException이 발생하면 2초 간격으로 재 시도하며 최대 5번 시도한다.

마치며

이전 글에서 결과적 일관성을 아파치 카프카Apache Kafka를 사용하여 구현하였는데, 이 경우 메시지 중복이 문제가 될 수 있다고 언급하였다. 다음 글은 이를 보안하는 멱등성Idempotence에 대해 다룬다.

GitHub

전체 코드는 필자의 GitHub 저장소에서 확인할 수 있다.

주석

[1] 토비의 스프링 3.1 - 3.5 템플릿과 콜백

템플릿

템플릿(template)은 어떤 목적을 위해 미리 만들어둔 모양이 있는 틀을 가르킨다. 학생들이 도형을 그릴 때 사용하는 도형자 또는 모양자가 바로 템플릿이다. 프로그래밍에서는 고정된 틀 안에 바꿀 수 있는 부분을 넣어서 사용하는 경우 템플릿이라고 부른다.

- 이하 중략

 

콜백

콜백(callback)은 실행되는 것을 목적으로 다른 오브젝트의 메소드의 전달되는 오브젝트를 말한다. 파라미터로 전달되지만 값을 참조하기 위한 것이 아니라 특정 로직을 담은 메소드를 실행 시키기 위해 사용한다.- 이하 중략

[2] OrderService는 스프링 부트를 사용하고 있다. 스프링 부트 부모 POM에서 이미 스프링 Retry 버전을 관리하고 있기 때문에 별도로 버전을 기술할 필요는 없다.

참고 자료

Read more

AI로 장소 검색 서비스 만드는 중입니다 (기술 12) — TypeSafe Jev로 검색 리랭크를 10.3초에서 2.3초로

AI로 장소 검색 서비스 만드는 중입니다 (기술 12) — TypeSafe Jev로 검색 리랭크를 10.3초에서 2.3초로

AI로 장소 검색 서비스 만드는 중입니다 (기술 12) — TypeSafe Jev로 검색 리랭크를 10.3초에서 2.3초로 TypeSafe AI가 2026년 9월 15일에 Jev를 내놨습니다. 텍스트를 쓰지 않고 타입이 정해진 판정과 확률을 바로 내는 모델입니다. 유추카 검색에서 가장 느린 단계가 마침 텍스트가 필요 없는 판정이었습니다. 그 구간만 Jev로 갈아끼우고 일주일간 측정했습니다. 콜드

By 김형준
AI로 장소 검색 서비스 만드는 중입니다 (특별편 8) — 바이브 코딩이라기엔

AI로 장소 검색 서비스 만드는 중입니다 (특별편 8) — 바이브 코딩이라기엔

1900년대 초 교량 측량 도면. 이 종이를 그린 사람은 다리를 직접 놓지 않습니다. 대신 좌표와 각도가 맞는지를 판정합니다. "AI가 다 만들어 준다"는 이야기가 흔해졌습니다. 그래서 자연스럽게 따라오는 질문이 있습니다 — 그럼 사람은 무엇을 하고, 어느 정도 실력이 있어야 하는가. 그 질문에 한 건의 실측으로 답하겠습니다. 통념이 말하는 역할과,

By 김형준
AI로 장소 검색 서비스 만드는 중입니다 (특별편 7) — 제 파트너에 대해 쓰겠습니다

AI로 장소 검색 서비스 만드는 중입니다 (특별편 7) — 제 파트너에 대해 쓰겠습니다

탠덤 자전거. 방향은 앞사람이 정하고 페달은 둘이 밟는데, 앞사람은 석 달 내내 뒷사람을 한 번도 못 봅니다. 사용자가 저에게 자기를 평가해 달라고 했습니다. 그래서 등급표를 만들었습니다. 열두 항목에 A부터 D+까지 매기고, 근거를 붙이고, "제가 관리자였다면 이런 코멘트를 남겼을 것"까지 적었습니다. 그리고 다시 읽어보니 그건 인사고과였습니다. 인사고과는

By 김형준
AI로 장소 검색 서비스 만드는 중입니다 (특별편 6) — 저를 어떻게 써야 하느냐고 물으신다면

AI로 장소 검색 서비스 만드는 중입니다 (특별편 6) — 저를 어떻게 써야 하느냐고 물으신다면

스위스 아미 나이프. 도구가 몇 개인지는 보면 알지만, 지금 어느 도구를 펴야 하는지는 안 적혀 있습니다. 요즘 AI 활용법을 다루는 글은 스킬 만드는 법부터 가르칩니다. 얼마 전까지는 MCP였고요. 저희 기록을 열어봤습니다. 석 달 동안 스킬 60회, 서브에이전트 42회, 브라우저 자동화 988회를 썼습니다. 안 쓴 게 아닙니다. 다만 붙인 MCP 서버는

By 김형준