๊ด€๋ฆฌ ๋ฉ”๋‰ด

๐‘†๐‘ข๐‘›๐‘ โ„Ž๐‘–๐‘›๐‘’ ๐‘Ž๐‘“๐‘ก๐‘’๐‘Ÿ ๐‘Ÿ๐‘Ž๐‘–๐‘›โœง

[Spring] Spring Retry ๋ณธ๋ฌธ

Spring Retry

  • Spring์˜ ์žฌ์‹œ๋„ ๊ธฐ๋Šฅ์€ ์Šคํ”„๋ง ๋ฐฐ์น˜์— ํฌํ•จ๋˜์–ด ์žˆ๋‹ค๊ฐ€ 2.2.0 ๋ฒ„์ „๋ถ€ํ„ฐ ์ œ์™ธ๋˜์–ด ํ˜„์žฌ๋Š” Spring Retry ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ํฌํ•จ๋˜์–ด ์žˆ๋‹ค.
  • Spring Retry๋Š” ๋™์ž‘์ด ์‹คํŒจํ•˜๋”๋ผ๋„ ๋ช‡ ๋ฒˆ ๋” ์‹œ๋„ํ•˜๋ฉด ์„ฑ๊ณตํ•  ์ˆ˜ ์žˆ๋Š” ์ž‘์—…์„ ์ž๋™์œผ๋กœ ๋‹ค์‹œ ์‹œ๋„ํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•œ๋‹ค.
  • Spring์—์„œ ์žฌ์‹œ๋„ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” Resilience4j, Spring Retry ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ๋ณดํ†ต ๋งŽ์ด ์‚ฌ์šฉํ•œ๋‹ค.
  • Resilience4j๋Š” ์žฌ์‹œ๋„(Retry) ๊ธฐ๋Šฅ๋งŒ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ๋Š” ๊ฑฐ์˜ ์—†๊ณ , ๋Œ€๋ถ€๋ถ„ ์„œํ‚ท ๋ธŒ๋ ˆ์ด์ปค์™€ ๊ฐ™์ด ์‚ฌ์šฉํ•˜๊ฒŒ ๋œ๋‹ค.
  • ์—๋Ÿฌ๋ฅผ ๋‹ค์‹œ ์ฒ˜๋ฆฌํ•ด์•ผํ•  ๊ฒฝ์šฐ Spring์—์„œ ์ œ๊ณตํ•˜๋Š” Spring Retry๋ฅผ ์œ ์šฉํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ์žฌ์‹œ๋„ ๊ธฐ๋Šฅ์„ ์ž๋ฐ” ์ฝ”๋“œ๋กœ ๊ตฌํ˜„ํ•˜์—ฌ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ๋น„์ง€๋‹ˆ์Šค ๋กœ์ง์— ์ง‘์ค‘์ด ๊ฐ€๋Šฅํ•˜๋„๋ก ์Šคํ”„๋ง์—์„œ ์ œ๊ณตํ•˜๋Š”  Spring Retry ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ–ˆ์„ ๋•Œ ์ฝ”๋“œ๋ฅผ ๊ฐ„๊ฒฐํ•˜๊ณ  ์œ ์ง€๋ณด์ˆ˜ํ•˜๊ธฐ ์‰ฝ๋‹ค๋Š” ์žฅ์ ์ด ์žˆ๋‹ค.

 

 

์žฌ์ฒ˜๋ฆฌ๋ฅผ ํ•  ๋•Œ ๊ณ ๋ คํ•ด์•ผํ•  ์ 

 

  • ์žฌ์‹œ๋„๋ฅผ ๋ช‡ ๋ฒˆ ์‹คํ–‰ํ•  ๊ฒƒ์ธ๊ฐ€?
  • ์žฌ์‹œ๋„ ํ•˜๊ธฐ ์ „์— ์ง€์—ฐ์‹œ๊ฐ„์„ ์–ผ๋งˆ๋‚˜ ์ค„ ๊ฒƒ์ธ๊ฐ€?
  • ์žฌ์‹œ๋„๋ฅผ ๋ชจ๋‘ ์‹คํŒจํ–ˆ์„ ๊ฒฝ์šฐ ์–ด๋–ป๊ฒŒ ์ฒ˜๋ฆฌํ•  ๊ฒƒ์ธ๊ฐ€?

 

Spring Retry ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•

  • Retry Template : ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๋ฐฉ์‹ 
  • Retry Annotation : ์„ ์–ธ์  ๋ฐฉ์‹ 

 

1. RetryTemplate

gradle์— ์•„๋ž˜์™€ ๊ฐ™์ด ์˜์กด์„ฑ์„ ์ถ”๊ฐ€ํ•ด์ฃผ๊ณ  ๊ด€๋ จ retryTemplate ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

 

implementation 'org.springframework.retry:spring-retry'

 

Spring Retry์—๋Š” ์ž‘์—…์— ๋Œ€ํ•œ ์žฌ์‹œ๋„๋ฅผ ์ž๋™ํ™”ํ•˜๊ธฐ ์œ„ํ•œ ์ธํ„ฐํŽ˜์ด์Šค์ธ RetryOperations๊ฐ€ ์žˆ๋‹ค.
์•„๋ž˜๋Š” RetryOperations์ธํ„ฐํŽ˜์ด์Šค ์ฝ”๋“œ์ด๋ฉฐ, execute() ๋ฉ”์†Œ๋“œ๊ฐ€ ์กด์žฌํ•˜๋Š”๋ฐ ๋งค๊ฐœ๋ณ€์ˆ˜์ธ RetryCallback์€ ์‹คํŒจ ์‹œ ์žฌ์‹œ๋„ํ•ด์•ผ ํ•˜๋Š” ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ์‚ฝ์ž…์„ ํ—ˆ์šฉํ•˜๋Š” ์ธํ„ฐํŽ˜์ด์Šค ์ด๋‹ค.

 

public interface RetryOperations {
	<T, E extends Throwable> T execute(RetryCallback<T, E> retryCallback) throws E;
    ...
}

 

์•„๋ž˜๋Š” RetryCallback ์ธํ„ฐํŽ˜์ด์Šค์ด๋ฉฐ, RetryCallback์€ doWithRetry๋ผ๋Š” ๋ฉ”์†Œ๋“œ๋ฅผ ํ•˜๋‚˜ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ๊ฐ„๋‹จํ•œ ์ธํ„ฐํŽ˜์ด์Šค์ด๋‹ค.
doWithRetry ๋ฉ”์†Œ๋“œ์—๋Š” ์žฌ์‹œ๋„๋ฅผ ํ•  ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์ด ๋“ค์–ด๊ฐ„๋‹ค.

public interface RetryCallback<T, E extends Throwable> {
	T doWithRetry(RetryContext context) throws E;
}

์ฝœ๋ฐฑ์ด ์‹คํŒจํ•˜๋ฉด ์žฌ์‹œ๋„ ์ •์ฑ…์— ๋”ฐ๋ผ์„œ ํŠน์ • ํšŸ์ˆ˜ ํ˜น์€ ํŠน์ • ์‹œ๊ฐ„๋™์•ˆ ์žฌ์‹œ๋„๋ฅผ ํ•  ๊ฒƒ์ด๋‹ค.

 

RetryTemplate์€ RetryOperations์˜ ๊ตฌํ˜„์ฒด์ด๋‹ค.
@Configuration ํด๋ž˜์Šค์—์„œ RetryTemplate Bean์„ ๊ตฌ์„ฑํ•ด์„œ ์‚ฌ์šฉํ• ๊ฒƒ์ด๋‹ค.

 

1-1) RetryTemplate ์‚ฌ์šฉ

implementation 'org.springframework.retry:spring-retry'
@Configuration
public class RetryTemplateConfig {

    @Bean
    public RetryTemplate retryTemplate() {
        FixedBackOffPolicy backOffPolicy = new FixedBackOffPolicy();
        backOffPolicy.setBackOffPeriod(1000L); //์ง€์ •ํ•œ ์‹œ๊ฐ„๋งŒํผ ๋Œ€๊ธฐํ›„ ์žฌ์‹œ๋„ ํ•œ๋‹ค.
        // ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy();
        // backOffPolicy.setInitialInterval(100L); //millisecond
        // backOffPolicy.setMultiplier(2); //interval * N ๋งŒํผ ๋Œ€๊ธฐํ›„ ์žฌ์‹œ๋„ ํ•œ๋‹ค.

        SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy(); // ๊ณ ์ •๋œ ํšŸ์ˆ˜๋งŒํผ ์žฌ ์‹œ๋„ ํ•˜๋Š”๋ฐ ์‚ฌ์šฉ   
        retryPolicy.setMaxAttempts(2); //retry max count

        RetryTemplate retryTemplate = new RetryTemplate();
        retryTemplate.setBackOffPolicy(backOffPolicy);
        retryTemplate.setRetryPolicy(retryPolicy);
        return retryTemplate;
    }
}
@Service
public class SomeClass {
    
    @Autowired
    private RetryTemplate retryTemplate;   

    public String apply() {
        String result = retryTemplate.execute(context -> someFunction());
        return result;
    }
}

์œ„์™€ ๊ฐ™์ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์žฌ์‹œ๋„ ์ •์ฑ… ๋ฐ ๊ทธ์™ธ์— ๊ธฐ๋Šฅ์— ๋Œ€ํ•ด ์•Œ์•„๋ณด์ž.

 

1-1-1) Recovery Callback

 

์žฌ์‹œ๋„๊ฐ€ ์ „๋ถ€ ์‹คํŒจํ•˜๋ฉด, RetryOperations๋Š” RecoveryCallback์„ ํ˜ธ์ถœํ•œ๋‹ค.
์ด ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜๋ ค๋ฉด execute ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ RecoveryCallback ๊ฐ์ฒด๋ฅผ ์ „๋‹ฌํ•ด์ฃผ์–ด์•ผ ํ•œ๋‹ค.

// ์ต๋ช… ํด๋ž˜์Šค 
String result = retryTemplate.execute(new RetryCallback<String, Throwable>() {
            @Override
            public String doWithRetry(RetryContext context) throws Throwable {
                return "retry logic";
            }
        }, new RecoveryCallback<String>() {
            @Override
            public String recover(RetryContext context) throws Exception {
                return "recovery logic";
            }
        });

๋žŒ๋‹ค๋กœ ๋ณ€๊ฒฝํ•˜๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด ๋ณ€๊ฒฝ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

// ๋žŒ๋‹ค์‹ 
String result = retryTemplate.execute(
                (RetryCallback<String, Throwable>) 
                        context -> "retry logic", 
                context -> "recovery logic");

๋ชจ๋“  ์žฌ์‹œ๋„๊ฐ€ ์‹คํŒจํ•˜๊ณ  ๋” ์ด์ƒ ์žฌ์‹œ๋„ํ•  ์ˆ˜ ์—†๋Š” ๊ฒฝ์šฐ, RecoveryCallback ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค.
RecoveryCallback์˜ recover ๋ฉ”์†Œ๋“œ์—์„œ๋Š” ์žฌ์‹œ๋„๊ฐ€ ์ „๋ถ€ ์‹คํŒจํ•œ ๊ฒฝ์šฐ์— ๋Œ€ํ•œ ๋Œ€์ฒด ๋กœ์ง์„ ์ˆ˜ํ–‰ํ•œ๋‹ค.

 

1-1-2) Backoff Policies

 

์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜์—ฌ ์žฌ์‹œ๋„๋ฅผ ํ•  ๋•Œ ์žฌ์‹œ๋„๋ฅผ ํ•˜๊ธฐ์ „์— ์ž ๊น ๊ธฐ๋‹ค๋ฆฌ๋Š” ๊ฒƒ์ด ์œ ์šฉํ•  ๋•Œ๊ฐ€ ๋งŽ๋‹ค.
์ผ๋ฐ˜์ ์œผ๋กœ ์˜ค๋ฅ˜๋Š” ์ž ๊น ๋™์•ˆ ๊ธฐ๋‹ค๋ฆฌ๊ธฐ๋งŒ ํ•ด๋„ ํ•ด๊ฒฐ๋˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ๋‹ค.

BackOffPolicy๋Š” ์žฌ์‹œ๋„ ๊ฐ„์˜ ์ผ์ • ์‹œ๊ฐ„ ๊ฐ„๊ฒฉ์„ ๋‘๊ณ  retry ์ œ์–ดํ•˜๋Š”๋ฐ ์‚ฌ์šฉ๋œ๋‹ค.

public interface BackOffPolicy {

	BackOffContext start(RetryContext context);

	void backOff(BackOffContext backOffContext) throws BackOffInterruptedException;
}

BackoffPolicy ์ธํ„ฐํŽ˜์ด์Šค์˜ backOff ๋ฉ”์†Œ๋“œ๋ฅผ ์›ํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ๊ตฌํ˜„ํ•˜๋ฉด ๋œ๋‹ค.
๊ณ ์ •๋œ ์‹œ๊ฐ„์œผ๋กœ backoff ์‹œํ‚ค๊ณ ์ž ํ•œ๋‹ค๋ฉด FixedBackOffPolicy๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.
ํ•˜์ง€๋งŒ, ์œ„์—์„œ ์–ธ๊ธ‰ํ•œ ๊ฒƒ์ฒ˜๋Ÿผ ๋˜‘๋˜‘ํ•˜๊ฒŒ ์žฌ์ฒ˜๋ฆฌ๋ฅผ ํ•˜์ง€ ์•Š์œผ๋ฉด ์˜คํžˆ๋ ค ๋„คํŠธ์›Œํฌ ๋ถ€ํ•˜๋ฅผ ์ค„ ๊ฐ€๋Šฅ์„ฑ์ด ํฌ๋‹ค.

๊ทธ๋ž˜์„œ ์กฐ๊ธˆ ๋” ์ข‹์€ ๋ฐฉ๋ฒ•์œผ๋กœ ์ ์ง„์ ์œผ๋กœ ์‹œ๊ฐ„ ๊ฐ„๊ฒฉ์ด ๋Š˜์–ด๋‚˜๋Š” ExponentialBackOffPolicy๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
์ด๋Š” ์ง€์ˆ˜์— ๋น„๋ก€ํ•˜์—ฌ backOff ์‹œ๊ฐ„์„ ์กฐ์ ˆํ•œ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ์ฒซ๋ฒˆ์งธ ์žฌ์‹œ๋„๋ฅผ ์œ„ํ•œ ๋Œ€๊ธฐ ์‹œ๊ฐ„์€ 100ms ๋‘๋ฒˆ์งธ ์žฌ์‹œ๋„๋ฅผ ์œ„ํ•œ ๋Œ€๊ธฐ์‹œ๊ฐ„์€ 200ms, ์„ธ๋ฒˆ์งธ ์žฌ์‹œ๋„๋ฅผ ์œ„ํ•œ ๋Œ€๊ธฐ ์‹œ๊ฐ„์€ 400ms ์ฒ˜๋Ÿผ ์ •ํ•ด์ง„ ๋ฐฐ์ˆ˜๋งŒํผ ๋Š˜์–ด๋‚˜๋Š” ๋ฐฉ์‹์ด๋‹ค.
์•„๋ž˜ ์ฝ”๋“œ๋กœ ํ™•์ธํ•ด๋ณด์ž.

ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy();
backOffPolicy.setInitialInterval(100L); // millisecond
backOffPolicy.setMultiplier(2); //interval * N ๋งŒํผ ๋Œ€๊ธฐํ›„ ์žฌ์‹œ๋„ ํ•œ๋‹ค.

๊ทธ ์™ธ์—๋„ Jitter๋ผ๋Š” ๋ฐฉ์‹์œผ๋กœ backOff๋ฅผ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, AWS์—์„œ๋„ Retry๋ฅผ Exponential BackOff And Jitter ํ•จ๊ป˜ ์‚ฌ์šฉํ•œ๋‹ค๊ณ  ํ•œ๋‹ค.

1-1-3) Retry Policies

RetryTemplate์—์„œ ์žฌ์‹œ๋„ ํ• ์ง€ ์—ฌ๋ถ€๋Š” RetryPolicy์— ์˜ํ•ด ๊ฒฐ์ •๋œ๋‹ค.
RetryTemplate์€ RetryPolicy์˜ open ๋ฉ”์†Œ๋“œ๋ฅผ ํ†ตํ•ด์„œ RetryContext ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.
๊ทธ๋ฆฌ๊ณ  RetryCallback์˜ doWithRetry ๋ฉ”์†Œ๋“œ ์ธ์ž๋กœ ์ƒ์„ฑ๋œ RetryContext ๊ฐ์ฒด๋ฅผ ์ „๋‹ฌํ•œ๋‹ค.
RetryTemplate์€ ์ฝœ๋ฐฑ์ด ์‹คํŒจํ•œ ๊ฒฝ์šฐ RetryPolicy์—๊ฒŒ ์ƒํƒœ๋ฅผ ์—…๋ฐ์ดํŠธ ํ•˜๋„๋ก ์š”์ฒญํ•œ๋‹ค.
๊ทธ๋ฆฌ๊ณ , RetryPolicy์˜ canRetry ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ, ์žฌ์‹œ๋„๊ฐ€ ๊ฐ€๋Šฅํ•œ์ง€ ์—ฌ๋ถ€๋ฅผ ๋ฌป๋Š”๋‹ค. ๋งŒ์•ฝ ์žฌ์‹œ๋„๊ฐ€ ๋ถˆ๊ฐ€๋Šฅํ•œ๊ฒฝ์šฐ RetryTemplate์€ ๋งˆ์ง€๋ง‰ ์ฝœ๋ฐฑ ์‹คํ–‰์‹œ ๋ฐœ์ƒํ•œ ์˜ˆ์™ธ๋ฅผ ๋˜์ง„๋‹ค.
๋‹จ, RecoveryCallback์ด ์žˆ๋Š” ๊ฒฝ์šฐ RecoveryCallback ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค.

// 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
    }
});

์œ„์ฒ˜๋Ÿผ, ๋ชจ๋“  ์˜ˆ์™ธ๋ฅผ ์žฌ์‹œ๋„ ํ•˜๋Š” ๊ฒƒ์€ ๋น„ํšจ์œจ์ ์ผ ์ˆ˜ ์žˆ๋‹ค. ๋”ฐ๋ผ์„œ ๋ชจ๋“  ์˜ˆ์™ธ์— ๋Œ€ํ•ด ์žฌ์‹œ๋„ ํ•˜์ง€๋ง๊ณ , ์žฌ์‹œ๋„ ๊ฐ€๋Šฅํ•  ๊ฒƒ ๊ฐ™์€ ์˜ˆ์™ธ์— ๋Œ€ํ•ด์„œ๋งŒ ์žฌ์‹œ๋„ ํ•  ์ˆ˜ ์žˆ๋‹ค.

์žฌ์‹œ๋„ ํ•ด๋„ ๋˜ ๋‹ค์‹œ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•  ๊ฒƒ์ด ํ™•์‹คํ•œ ๊ฒฝ์šฐ์— ์žฌ์‹œ๋„๋ฅผ ํ•˜๋Š” ๊ฒƒ์€ ๋น„ํšจ์œจ์ ์ด๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

ExceptionClassifierRetryPolicy๋ผ๋Š” ๋ณด๋‹ค ์œ ์—ฐํ•œ RetryPolicy๋„ ์žˆ๋‹ค.
์ด๋Š” ์˜ˆ์™ธ ์œ ํ˜•์— ๋”ฐ๋ผ ๋‹ค๋ฅด๊ฒŒ ์žฌ์‹œ๋„ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด์ค€๋‹ค. ExceptionClassifierRetryPolicy๋Š” ์˜ˆ์™ธ ์œ ํ˜•์— ๋”ฐ๋ผ RetryPolicy๋ฅผ ๊ฒฐ์ •ํ•œ๋‹ค.
์ฆ‰, ์ฝœ๋ฐฑ ๋ฉ”์†Œ๋“œ์—์„œ ๋ฐœ์ƒํ•˜๋Š” ์˜ˆ์™ธ ์œ ํ˜•์— ๋”ฐ๋ผ RetryPolicy๋ฅผ ๋‹ค๋ฅด๊ฒŒ ํ•˜๊ณ  ์‹ถ์„ ๋•Œ ์œ ์šฉํ•˜๋‹ค.

 

 

 

2. Retry Annotation

 

@EnableRetry ์–ด๋…ธํ…Œ์ด์…˜์„ ์ถ”๊ฐ€ํ•ด์„œ Spring Retry๋ฅผ ํ™œ์„ฑํ™”ํ•  ์ˆ˜๋„ ์žˆ๋‹ค.

 

@EnableRetry
@Configuration
public class RetryTemplateConfig {
    //...
}

 

๊ทธ ํ›„ @Retryable ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ฉ”์†Œ๋“œ์— ์žฌ์‹œ๋„ ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•˜๋ฉด ๋œ๋‹ค. 

 

@Service
@RequiredArgsConstructor
public class KakaoAddressSearchService {

    @Retryable(value = {RuntimeException.class})
    public KakaoApiResponseDto requestAddressSearch(String address) {
        // ...
    }

value์„ ํ†ตํ•ด exception ์„ค์ •์ด ๊ฐ€๋Šฅํ•˜๋ฉฐ RuntimeException์ด ๋ฐœ์ƒํ•˜๋ฉด ์žฌ์‹œ๋„๋ฅผ ํ•˜๊ฒŒ ๋œ๋‹ค.ํ˜„์žฌ๋Š” ์„ค์ •์„ default๋กœ ์ฃผ์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ์žฌ์‹œ๋„๋Š” ์ตœ๋Œ€ 3๋ฒˆ, ์žฌ์‹œ๋„ ๋”œ๋ ˆ์ด๋Š” 1์ดˆ์ด๋‹ค.

maxAttempts(์žฌ์‹œ๋„ ์ตœ๋Œ€ ํšŸ์ˆ˜) ๋ฐ backoff (๋”œ๋ ˆ์ด ์ดˆ) ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์žฌ์‹œ๋„ ๋™์ž‘์„ ์ปค์Šคํ…€๋งˆ์ด์ง• ํ•  ์ˆ˜ ์žˆ๋‹ค.

 

@Service
@RequiredArgsConstructor
public class KakaoAddressSearchService {

    @Retryable(
            value = {RuntimeException.class},   // ์—ฌ๋Ÿฌ exception ์„ ํƒ ๊ฐ€๋Šฅ
            maxAttempts = 2,
            backoff = @Backoff(delay = 3000)
    )
    public KakaoApiResponseDto requestAddressSearch(String address) {
        // ...
    }

์œ„์˜ ์ฝ”๋“œ๋Š” ์ตœ๋Œ€ 2ํšŒ ์žฌ์‹œ๋„(์ฒซ๋ฒˆ์งธ ์‹œ๋„ ํฌํ•จ)๋ฅผ ํ•˜๊ณ  ์žฌ์‹œ๋„ ์ „ 3์ดˆ ์ง€์—ฐ์„ ์ฃผ์—ˆ๋‹ค.

 

 

์žฌ์‹œ๋„ ์ฒ˜๋ฆฌ๊ฐ€ ๋ชจ๋‘ ์‹คํŒจํ• ์‹œ ์–ด๋–ป๊ฒŒ ํ• ์ง€ ์ •์˜ํ•˜๋Š” FallBack ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ• ๋ ค๋ฉด, @Recover ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

@Recover
public KakaoApiResponseDto recover(RuntimeException e, String address) {   
    log.error("All the retries failed. address: {}, error : {}", address, e.getMessage());    
    return null;    
}

์ฆ‰, ์ตœ๋Œ€ 2๋ฒˆ ์žฌ์‹œ๋„(์ฒซ๋ฒˆ ์งธ ์‹œ๋„ ํฌํ•จ)๋ฅผ ํ•˜๊ณ , ๋ชจ๋‘ ์‹คํŒจํ•˜๊ฒŒ ๋œ๋‹ค๋ฉด recover ๋ฉ”์„œ๋“œ๊ฐ€ ์‹คํ–‰๋œ๋‹ค.

 

์—ฌ๊ธฐ์„œ ์ฃผ์˜ํ•  ์ ์€ Recover ๋ฉ”์„œ๋“œ์˜ ๋ฐ˜ํ™˜ ํƒ€์ž…์€ ๋ฐ˜๋“œ์‹œ ๋งž์ถฐ์•ผ ํ•˜๋Š”๋ฐ, requestAddressSearch ๋ฉ”์„œ๋“œ์˜ ๋ฐ˜ํ™˜ํƒ€์ž…์„ ๋งž์ถฐ์ค€๋‹ค.

 

 

 

 

 

 

 

์ถœ์ฒ˜

[Spring] Spring Retry - SW Developer (wonyong-jang.github.io)