๐Ÿš€ Spring @Retryable๋กœ ์˜ค๋ฅ˜ ๋ณต๊ตฌ ๋งˆ์Šคํ„ฐํ•˜๊ธฐ! ๐Ÿ› ๏ธ

์ฝ˜ํ…์ธ  ๋Œ€ํ‘œ ์ด๋ฏธ์ง€ - ๐Ÿš€ Spring @Retryable๋กœ ์˜ค๋ฅ˜ ๋ณต๊ตฌ ๋งˆ์Šคํ„ฐํ•˜๊ธฐ! ๐Ÿ› ๏ธ

 

 

์•ˆ๋…•ํ•˜์„ธ์š”, ๊ฐœ๋ฐœ์ž ์—ฌ๋Ÿฌ๋ถ„! ์˜ค๋Š˜์€ ์ •๋ง ๊ฟ€์žผ ์ฃผ์ œ๋กœ ์ฐพ์•„์™”์–ด์š”. ๋ฐ”๋กœ Spring์˜ @Retryable์„ ์ด์šฉํ•œ ์˜ค๋ฅ˜ ๋ณต๊ตฌ ๋ฉ”์ปค๋‹ˆ์ฆ˜ ๊ตฌํ˜„์— ๋Œ€ํ•ด ์•Œ์•„๋ณผ ๊ฑฐ์˜ˆ์š”. ์ด๊ฑฐ ์ง„์งœ ๊ฐœ๋ฐœ์ž๋“ค ์‚ฌ์ด์—์„œ ํ•ซํ•œ ์ฃผ์ œ๋ผ๊ณ ์š”! ๐Ÿ”ฅ ์žฌ๋Šฅ๋„ท์—์„œ๋„ ์ด๋Ÿฐ ์Šคํ‚ฌ ๊ฐ€์ง„ ๊ฐœ๋ฐœ์ž๋“ค ๋ชจ์‹œ๋Ÿฌ ๋‚œ๋ฆฌ ๋‚ฌ๋‹ค๋˜๋ฐ? ใ…‹ใ…‹ใ…‹

์ž ๊น! ํ˜น์‹œ ์•„์ง Spring์ด ๋ญ”์ง€ ๋ชจ๋ฅด๋Š” ๋ถ„๋“ค๋„ ๊ณ„์‹ค ํ…๋ฐ, ๊ฑฑ์ • ๋งˆ์„ธ์š”. ์šฐ๋ฆฌ ํ•จ๊ป˜ ์ฐจ๊ทผ์ฐจ๊ทผ ์•Œ์•„๊ฐ€ ๋ด์š”. Spring์€ ์ž๋ฐ” ๊ฐœ๋ฐœ์„ ์œ„ํ•œ ์˜คํ”ˆ์†Œ์Šค ํ”„๋ ˆ์ž„์›Œํฌ์˜ˆ์š”. ์‰ฝ๊ฒŒ ๋งํ•ด์„œ, ์ž๋ฐ”๋กœ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋งŒ๋“ค ๋•Œ ์—„์ฒญ ํŽธํ•˜๊ฒŒ ํ•ด์ฃผ๋Š” ๋„๊ตฌ๋ผ๊ณ  ์ƒ๊ฐํ•˜์‹œ๋ฉด ๋ผ์š”!

๐Ÿค” @Retryable์ด ๋ญ๊ธธ๋ž˜ ์ด๋ ‡๊ฒŒ ๋‚œ๋ฆฌ์•ผ?

์ž, ์—ฌ๋Ÿฌ๋ถ„. ๊ฐœ๋ฐœํ•˜๋‹ค ๋ณด๋ฉด ์ง„์งœ ์งœ์ฆ๋‚˜๋Š” ๊ฒŒ ๋ญ๊ฒŒ์š”? ๋ฐ”๋กœ ์—๋Ÿฌ์ฃ ! ํŠนํžˆ ๋„คํŠธ์›Œํฌ ๋ฌธ์ œ๋‚˜ ์ผ์‹œ์ ์ธ ์„œ๋ฒ„ ๋‹ค์šด ๊ฐ™์€ ๊ฑฐ ๋•Œ๋ฌธ์— ์ƒ๊ธฐ๋Š” ์—๋Ÿฌ๋Š” ์ •๋ง... ๐Ÿคฆโ€โ™‚๏ธ ๊ทผ๋ฐ @Retryable์„ ์“ฐ๋ฉด ์ด๋Ÿฐ ๋ฌธ์ œ๋ฅผ ์—„์ฒญ ์‰ฝ๊ฒŒ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์–ด์š”!

@Retryable์€ Spring์˜ ์–ด๋…ธํ…Œ์ด์…˜ ์ค‘ ํ•˜๋‚˜์˜ˆ์š”. ์ด๊ฑธ ๋ฉ”์„œ๋“œ ์œ„์— ๋ถ™์ด๋ฉด, ๊ทธ ๋ฉ”์„œ๋“œ๊ฐ€ ์‹คํŒจํ–ˆ์„ ๋•Œ ์ž๋™์œผ๋กœ ์žฌ์‹œ๋„๋ฅผ ํ•ด์ค˜์š”. ์™„์ „ ๊ฐœ๊ฟ€ ๊ธฐ๋Šฅ์ด์ฃ ? ใ…‹ใ…‹ใ…‹

@Retryable ๋™์ž‘ ์›๋ฆฌ ์—๋Ÿฌ ๋ฐœ์ƒ @Retryable ์žฌ์‹œ๋„

์œ„ ๊ทธ๋ฆผ์„ ๋ณด์„ธ์š”. ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด @Retryable์ด "์ž ๊น๋งŒ, ๋‚ด๊ฐ€ ๋‹ค์‹œ ํ•œ ๋ฒˆ ํ•ด๋ณผ๊ฒŒ!"๋ผ๊ณ  ํ•˜๋ฉด์„œ ์žฌ์‹œ๋„๋ฅผ ํ•˜๋Š” ๊ฑฐ์˜ˆ์š”. ์™„์ „ ๋“ ๋“ ํ•œ ์นœ๊ตฌ ๊ฐ™์ฃ ? ๐Ÿ˜Ž

๐Ÿ› ๏ธ @Retryable ์‚ฌ์šฉ๋ฒ•, ์–ด๋ ต์ง€ ์•Š์•„์š”!

์ž, ์ด์ œ ์‹ค์ œ๋กœ ์–ด๋–ป๊ฒŒ ์‚ฌ์šฉํ•˜๋Š”์ง€ ์•Œ์•„๋ณผ๊นŒ์š”? ๋จผ์ €, Spring Boot ํ”„๋กœ์ ํŠธ์— spring-retry ์˜์กด์„ฑ์„ ์ถ”๊ฐ€ํ•ด์•ผ ํ•ด์š”.


implementation 'org.springframework.retry:spring-retry'
implementation 'org.springframework.boot:spring-boot-starter-aop'
  

์ด๋ ‡๊ฒŒ ์˜์กด์„ฑ์„ ์ถ”๊ฐ€ํ•˜๊ณ  ๋‚˜๋ฉด, ์ด์ œ @Retryable์„ ์‚ฌ์šฉํ•  ์ค€๋น„๊ฐ€ ๋œ ๊ฑฐ์˜ˆ์š”! ๐Ÿ‘

๊ทธ ๋‹ค์Œ, ์žฌ์‹œ๋„ํ•˜๊ณ  ์‹ถ์€ ๋ฉ”์„œ๋“œ ์œ„์— @Retryable ์–ด๋…ธํ…Œ์ด์…˜์„ ๋ถ™์—ฌ์ฃผ๋ฉด ๋ผ์š”. ์˜ˆ๋ฅผ ๋“ค์–ด๋ณผ๊นŒ์š”?


import org.springframework.retry.annotation.Retryable;

@Service
public class MyService {

    @Retryable(value = RuntimeException.class, maxAttempts = 3, backoff = @Backoff(delay = 1000))
    public void doSomethingRisky() {
        // ์œ„ํ—˜ํ•œ ์ž‘์—… ์ˆ˜ํ–‰
    }
}
  

์šฐ์™€, ์ด๊ฒŒ ๋ญ”๊ฐ€ ์‹ถ์ฃ ? ํ•˜๋‚˜์”ฉ ์„ค๋ช…ํ•ด ๋“œ๋ฆด๊ฒŒ์š”!

  • value = RuntimeException.class: ์ด๊ฑด ์–ด๋–ค ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ ์žฌ์‹œ๋„ํ•  ๊ฑด์ง€ ์ง€์ •ํ•˜๋Š” ๊ฑฐ์˜ˆ์š”. ์—ฌ๊ธฐ์„œ๋Š” RuntimeException์ด ๋ฐœ์ƒํ•˜๋ฉด ์žฌ์‹œ๋„ํ•œ๋‹ค๋Š” ๋œป์ด์—์š”.
  • maxAttempts = 3: ์ตœ๋Œ€ ๋ช‡ ๋ฒˆ๊นŒ์ง€ ์žฌ์‹œ๋„ํ•  ๊ฑด์ง€ ์ •ํ•˜๋Š” ๊ฑฐ์˜ˆ์š”. ์—ฌ๊ธฐ์„œ๋Š” 3๋ฒˆ์ด๋„ค์š”.
  • backoff = @Backoff(delay = 1000): ์žฌ์‹œ๋„ ์‚ฌ์ด์— ์–ผ๋งˆ๋‚˜ ๊ธฐ๋‹ค๋ฆด์ง€ ์ •ํ•˜๋Š” ๊ฑฐ์˜ˆ์š”. 1000์€ 1์ดˆ๋ฅผ ์˜๋ฏธํ•ด์š”.

์ด๋ ‡๊ฒŒ ์„ค์ •ํ•˜๋ฉด, doSomethingRisky() ๋ฉ”์„œ๋“œ๊ฐ€ RuntimeException์„ ๋˜์กŒ์„ ๋•Œ, Spring์€ ์ตœ๋Œ€ 3๋ฒˆ๊นŒ์ง€ 1์ดˆ ๊ฐ„๊ฒฉ์œผ๋กœ ์žฌ์‹œ๋„๋ฅผ ํ•  ๊ฑฐ์˜ˆ์š”. ์™„์ „ ๋˜‘๋˜‘ํ•˜์ฃ ? ๐Ÿ‘จโ€๐Ÿ”ฌ

์ฃผ์˜! @Retryable์€ ๋งŒ๋Šฅ์ด ์•„๋‹ˆ์—์š”. ์ผ์‹œ์ ์ธ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ๋ฐ๋Š” ์ข‹์ง€๋งŒ, ์˜๊ตฌ์ ์ธ ์˜ค๋ฅ˜(์˜ˆ: ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๊ฒฐ ๋ฌธ์ œ)์—๋Š” ํšจ๊ณผ๊ฐ€ ์—†์„ ์ˆ˜ ์žˆ์–ด์š”. ๊ทธ๋ž˜์„œ ์ ์ ˆํ•œ ์ƒํ™ฉ์— ์‚ฌ์šฉํ•˜๋Š” ๊ฒŒ ์ค‘์š”ํ•ด์š”!

๐ŸŽญ @Recover: @Retryable์˜ ๋“ ๋“ ํ•œ ๋ฐฑ์—…

@Retryable๋งŒ์œผ๋กœ ๋ถ€์กฑํ•˜๋‹ค๊ณ ์š”? ๊ฑฑ์ • ๋งˆ์„ธ์š”! Spring์€ @Recover๋ผ๋Š” ๋˜ ๋‹ค๋ฅธ ๊ฟ€ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ด์š”. ์ด๊ฑด ๋ญ๋ƒ๊ณ ์š”? @Retryable๋กœ ์ง€์ •ํ•œ ์ตœ๋Œ€ ์žฌ์‹œ๋„ ํšŸ์ˆ˜๋ฅผ ๋„˜์–ด์„œ๋„ ์‹คํŒจํ•˜๋ฉด ์‹คํ–‰๋˜๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ์ง€์ •ํ•˜๋Š” ๊ฑฐ์˜ˆ์š”.


@Service
public class MyService {

    @Retryable(value = RuntimeException.class, maxAttempts = 3)
    public void doSomethingRisky() {
        // ์œ„ํ—˜ํ•œ ์ž‘์—… ์ˆ˜ํ–‰
    }

    @Recover
    public void recover(RuntimeException e) {
        // ๋ชจ๋“  ์žฌ์‹œ๋„๊ฐ€ ์‹คํŒจํ–ˆ์„ ๋•Œ ์‹คํ–‰๋  ๋กœ์ง
        System.out.println("๋ชจ๋“  ์žฌ์‹œ๋„ ์‹คํŒจ! ๋Œ€์ฒด ๋กœ์ง ์‹คํ–‰");
    }
}
  

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด, doSomethingRisky() ๋ฉ”์„œ๋“œ๊ฐ€ 3๋ฒˆ ์‹คํŒจํ•˜๋ฉด recover() ๋ฉ”์„œ๋“œ๊ฐ€ ์‹คํ–‰๋ผ์š”. ์™„๋ฒฝํ•œ ๋ฐฑ์—… ํ”Œ๋žœ์ด์ฃ ? ๐Ÿ‘

@Retryable๊ณผ @Recover ๋™์ž‘ ๊ณผ์ • ์ฒซ ๋ฒˆ์งธ ์‹œ๋„ ๋‘ ๋ฒˆ์งธ ์‹œ๋„ ์„ธ ๋ฒˆ์งธ ์‹œ๋„ @Recover ์‹คํ–‰

์ด ๊ทธ๋ฆผ์„ ๋ณด์„ธ์š”. @Retryable์ด ์„ธ ๋ฒˆ ์‹œ๋„ํ•˜๊ณ  ์‹คํŒจํ•˜๋ฉด, ๋งˆ์ง€๋ง‰์— @Recover๊ฐ€ ๋“ฑ์žฅํ•ด์„œ ์ƒํ™ฉ์„ ์ˆ˜์Šตํ•˜๋Š” ๊ฑฐ์˜ˆ์š”. ์™„์ „ ํžˆ์–ด๋กœ ๊ฐ™์ง€ ์•Š๋‚˜์š”? ๐Ÿฆธโ€โ™‚๏ธ

๐Ÿงช ์‹ค์ „ ์˜ˆ์ œ: ์™ธ๋ถ€ API ํ˜ธ์ถœ

์ž, ์ด์ œ ์‹ค์ œ๋กœ ์–ด๋–ป๊ฒŒ ์‚ฌ์šฉํ•˜๋Š”์ง€ ์˜ˆ์ œ๋ฅผ ํ†ตํ•ด ์•Œ์•„๋ณผ๊นŒ์š”? ๊ฐ€์žฅ ํ”ํ•œ ์‚ฌ์šฉ ์‚ฌ๋ก€ ์ค‘ ํ•˜๋‚˜๋Š” ์™ธ๋ถ€ API๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ์˜ˆ์š”. ๋„คํŠธ์›Œํฌ ๋ฌธ์ œ๋กœ API ํ˜ธ์ถœ์ด ์‹คํŒจํ•  ์ˆ˜ ์žˆ์ž–์•„์š”? ์ด๋Ÿด ๋•Œ @Retryable์„ ์‚ฌ์šฉํ•˜๋ฉด ์•„์ฃผ ์ข‹์•„์š”!


import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Recover;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

@Service
public class WeatherService {

    private final RestTemplate restTemplate;

    public WeatherService(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }

    @Retryable(value = RuntimeException.class, maxAttempts = 3, backoff = @Backoff(delay = 1000))
    public String getWeather(String city) {
        String url = "https://api.weather.com/v1/current?city=" + city;
        return restTemplate.getForObject(url, String.class);
    }

    @Recover
    public String recoverGetWeather(RuntimeException e, String city) {
        return "๋‚ ์”จ ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ค๋Š”๋ฐ ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค. ๋„์‹œ: " + city;
    }
}
  

์ด ์˜ˆ์ œ์—์„œ๋Š” ๋‚ ์”จ API๋ฅผ ํ˜ธ์ถœํ•˜๋Š” getWeather() ๋ฉ”์„œ๋“œ์— @Retryable์„ ์ ์šฉํ–ˆ์–ด์š”. ๋งŒ์•ฝ API ํ˜ธ์ถœ์ด ์‹คํŒจํ•˜๋ฉด, Spring์€ ์ตœ๋Œ€ 3๋ฒˆ๊นŒ์ง€ 1์ดˆ ๊ฐ„๊ฒฉ์œผ๋กœ ์žฌ์‹œ๋„๋ฅผ ํ•  ๊ฑฐ์˜ˆ์š”. ๊ทธ๋ž˜๋„ ์‹คํŒจํ•˜๋ฉด? ๊ทธ๋•Œ๋Š” recoverGetWeather() ๋ฉ”์„œ๋“œ๊ฐ€ ์‹คํ–‰๋˜์–ด ๊ธฐ๋ณธ ๋ฉ”์‹œ์ง€๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ฒŒ ๋ผ์š”.

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์ผ์‹œ์ ์ธ ๋„คํŠธ์›Œํฌ ๋ฌธ์ œ ๋•Œ๋ฌธ์— ์‚ฌ์šฉ์ž๊ฐ€ ์—๋Ÿฌ๋ฅผ ๋ณด๋Š” ์ผ์„ ํฌ๊ฒŒ ์ค„์ผ ์ˆ˜ ์žˆ์–ด์š”. ์™„์ „ ๊ฐœ๋ฐœ์ž์˜ ํ’ˆ๊ฒฉ ์•„๋‹ˆ๊ฒ ์–ด์š”? ๐Ÿ˜Ž

ํŒ! ์žฌ๋Šฅ๋„ท์—์„œ ์ด๋Ÿฐ ์Šคํ‚ฌ์„ ๊ฐ€์ง„ ๊ฐœ๋ฐœ์ž๋ฅผ ์ฐพ๊ณ  ๊ณ„์‹ ๋‹ค๋ฉด, 'Spring ์ „๋ฌธ๊ฐ€' ๋˜๋Š” '์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ ๋Šฅ์ˆ™์ž'๋กœ ๊ฒ€์ƒ‰ํ•ด๋ณด์„ธ์š”. ๋ถ„๋ช… ์ข‹์€ ์ธ์žฌ๋ฅผ ๋งŒ๋‚˜์‹ค ์ˆ˜ ์žˆ์„ ๊ฑฐ์˜ˆ์š”!

๐ŸŽ›๏ธ @Retryable ๊ณ ๊ธ‰ ์„ค์ •

์ง€๊ธˆ๊นŒ์ง€ ๊ธฐ๋ณธ์ ์ธ ์‚ฌ์šฉ๋ฒ•์„ ์•Œ์•„๋ดค๋Š”๋ฐ์š”, @Retryable์€ ๋” ๋‹ค์–‘ํ•œ ์˜ต์…˜์„ ์ œ๊ณตํ•ด์š”. ์ด๊ฑธ ์ž˜ ํ™œ์šฉํ•˜๋ฉด ๋”์šฑ ์„ฌ์„ธํ•œ ์žฌ์‹œ๋„ ์ „๋žต์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ต๋‹ˆ๋‹ค!

  • include: ์žฌ์‹œ๋„ํ•  ์˜ˆ์™ธ ํƒ€์ž…์„ ์ง€์ •ํ•ด์š”. ์—ฌ๋Ÿฌ ๊ฐœ๋ฅผ ์ง€์ •ํ•  ์ˆ˜๋„ ์žˆ์–ด์š”.
  • exclude: ์žฌ์‹œ๋„ํ•˜์ง€ ์•Š์„ ์˜ˆ์™ธ ํƒ€์ž…์„ ์ง€์ •ํ•ด์š”.
  • backoff: ์žฌ์‹œ๋„ ๊ฐ„๊ฒฉ์„ ๋” ์„ธ๋ฐ€ํ•˜๊ฒŒ ์กฐ์ •ํ•  ์ˆ˜ ์žˆ์–ด์š”.

์˜ˆ๋ฅผ ๋“ค์–ด๋ณผ๊นŒ์š”?


@Retryable(
    include = {SocketTimeoutException.class, ResourceAccessException.class},
    exclude = {NullPointerException.class},
    maxAttempts = 5,
    backoff = @Backoff(delay = 1000, multiplier = 2)
)
public void complexOperation() {
    // ๋ณต์žกํ•œ ์ž‘์—… ์ˆ˜ํ–‰
}
  

์ด ์„ค์ •์€ ๋ญ˜ ์˜๋ฏธํ•˜๋Š” ๊ฑธ๊นŒ์š”?

  1. SocketTimeoutException์ด๋‚˜ ResourceAccessException์ด ๋ฐœ์ƒํ•˜๋ฉด ์žฌ์‹œ๋„๋ฅผ ํ•ด์š”.
  2. ํ•˜์ง€๋งŒ NullPointerException์ด ๋ฐœ์ƒํ•˜๋ฉด ์žฌ์‹œ๋„ํ•˜์ง€ ์•Š์•„์š”.
  3. ์ตœ๋Œ€ 5๋ฒˆ๊นŒ์ง€ ์žฌ์‹œ๋„ํ•ด์š”.
  4. ์ฒซ ๋ฒˆ์งธ ์žฌ์‹œ๋„๋Š” 1์ดˆ ํ›„์—, ๊ทธ ๋‹ค์Œ๋ถ€ํ„ฐ๋Š” ๊ฐ„๊ฒฉ์ด 2๋ฐฐ์”ฉ ๋Š˜์–ด๋‚˜์š”. (1์ดˆ, 2์ดˆ, 4์ดˆ, 8์ดˆ...)

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋„คํŠธ์›Œํฌ ๊ด€๋ จ ๋ฌธ์ œ๋Š” ์žฌ์‹œ๋„ํ•˜์ง€๋งŒ, ์ฝ”๋“œ์˜ ๋…ผ๋ฆฌ์  ์˜ค๋ฅ˜(NullPointerException ๊ฐ™์€)๋Š” ๋ฐ”๋กœ ์‹คํŒจ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด์š”. ๋˜‘๋˜‘ํ•˜์ฃ ? ๐Ÿง 

@Retryable ๊ณ ๊ธ‰ ์„ค์ • ๋™์ž‘ ๊ณผ์ • 1์ดˆ ํ›„ 2์ดˆ ํ›„ 4์ดˆ ํ›„ 8์ดˆ ํ›„

์ด ๊ทธ๋ฆผ์„ ๋ณด์„ธ์š”. ์žฌ์‹œ๋„ ๊ฐ„๊ฒฉ์ด ์–ด๋–ป๊ฒŒ ๋Š˜์–ด๋‚˜๋Š”์ง€ ํ•œ๋ˆˆ์— ๋ณด์ด์ฃ ? ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์ดˆ๊ธฐ์—๋Š” ๋น ๋ฅด๊ฒŒ ์žฌ์‹œ๋„ํ•˜๋‹ค๊ฐ€, ๋ฌธ์ œ๊ฐ€ ์ง€์†๋˜๋ฉด ์ ์  ๋” ๊ธด ๊ฐ„๊ฒฉ์„ ๋‘๊ณ  ์žฌ์‹œ๋„ํ•˜๊ฒŒ ๋ผ์š”. ์„œ๋ฒ„์— ๊ณผ๋ถ€ํ•˜๋ฅผ ์ฃผ์ง€ ์•Š์œผ๋ฉด์„œ๋„ ํšจ๊ณผ์ ์œผ๋กœ ์žฌ์‹œ๋„ํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์ด์—์š”! ๐Ÿ‘Œ

๐Ÿ—๏ธ @Retryable ํ™œ์šฉ ์•„ํ‚คํ…์ฒ˜

@Retryable์„ ์‚ฌ์šฉํ•  ๋•Œ๋Š” ์ „์ฒด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์•„ํ‚คํ…์ฒ˜๋ฅผ ๊ณ ๋ คํ•ด์•ผ ํ•ด์š”. ์–ด๋–ป๊ฒŒ ๊ตฌ์„ฑํ•˜๋ฉด ์ข‹์„๊นŒ์š”?

  1. ์„œ๋น„์Šค ๊ณ„์ธต์— ์ ์šฉ: ๋ณดํ†ต @Retryable์€ ์„œ๋น„์Šค ๊ณ„์ธต์˜ ๋ฉ”์„œ๋“œ์— ์ ์šฉํ•ด์š”. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง๊ณผ ์žฌ์‹œ๋„ ๋กœ์ง์„ ๊น”๋”ํ•˜๊ฒŒ ๋ถ„๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด์š”.
  2. AOP ํ™œ์šฉ: @Retryable์€ ๋‚ด๋ถ€์ ์œผ๋กœ AOP(Aspect-Oriented Programming)๋ฅผ ์‚ฌ์šฉํ•ด์š”. ์ด๋ฅผ ์ž˜ ํ™œ์šฉํ•˜๋ฉด ์ฝ”๋“œ ์ค‘๋ณต์„ ์ค„์ด๊ณ  ๊ด€์‹ฌ์‚ฌ๋ฅผ ๋ถ„๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด์š”.
  3. ๋กœ๊น… ์ „๋žต: ์žฌ์‹œ๋„๊ฐ€ ๋ฐœ์ƒํ•  ๋•Œ๋งˆ๋‹ค ๋กœ๊ทธ๋ฅผ ๋‚จ๊ธฐ๋Š” ๊ฒƒ์ด ์ข‹์•„์š”. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋‚˜์ค‘์— ๋ฌธ์ œ๋ฅผ ๋ถ„์„ํ•˜๊ธฐ ์‰ฌ์›Œ์ ธ์š”.
  4. ๋ชจ๋‹ˆํ„ฐ๋ง ์—ฐ๋™: ์žฌ์‹œ๋„ ํšŸ์ˆ˜๋‚˜ ์‹คํŒจ์œจ ๋“ฑ์„ ๋ชจ๋‹ˆํ„ฐ๋ง ์‹œ์Šคํ…œ๊ณผ ์—ฐ๋™ํ•˜๋ฉด, ์‹œ์Šคํ…œ์˜ ๊ฑด๊ฐ• ์ƒํƒœ๋ฅผ ์‹ค์‹œ๊ฐ„์œผ๋กœ ํ™•์ธํ•  ์ˆ˜ ์žˆ์–ด์š”.

์ด๋Ÿฐ ๊ตฌ์กฐ๋กœ ์„ค๊ณ„ํ•˜๋ฉด ์–ด๋–ค ์žฅ์ ์ด ์žˆ์„๊นŒ์š”?

  • ์ฝ”๋“œ์˜ ๊ฐ€๋…์„ฑ์ด ๋†’์•„์ ธ์š”.
  • ์œ ์ง€๋ณด์ˆ˜๊ฐ€ ์‰ฌ์›Œ์ ธ์š”.
  • ์‹œ์Šคํ…œ์˜ ์•ˆ์ •์„ฑ์ด ํ–ฅ์ƒ๋ผ์š”.
  • ๋ฌธ์ œ ์ƒํ™ฉ์„ ๋น ๋ฅด๊ฒŒ ํŒŒ์•…ํ•˜๊ณ  ๋Œ€์‘ํ•  ์ˆ˜ ์žˆ์–ด์š”.

์™„์ „ ๊ฐœ๋ฐœ์ž์˜ ๊ฟˆ์ด์ฃ ? ๐Ÿ˜

์ฐธ๊ณ ! ์žฌ๋Šฅ๋„ท์—์„œ๋Š” ์ด๋Ÿฐ ๊ณ ๊ธ‰ ์•„ํ‚คํ…์ฒ˜๋ฅผ ์„ค๊ณ„ํ•  ์ˆ˜ ์žˆ๋Š” ์‹œ๋‹ˆ์–ด ๊ฐœ๋ฐœ์ž๋“ค์˜ ์ˆ˜์š”๊ฐ€ ๋†’์•„์š”. ๋งŒ์•ฝ ์ด๋Ÿฐ ์Šคํ‚ฌ์„ ๊ฐ€์ง€๊ณ  ๊ณ„์‹œ๋‹ค๋ฉด, ์žฌ๋Šฅ๋„ท์—์„œ ์—ฌ๋Ÿฌ๋ถ„์˜ ์žฌ๋Šฅ์„ ๋‚˜๋ˆ ๋ณด๋Š” ๊ฑด ์–ด๋–จ๊นŒ์š”? ๐ŸŒŸ

๐Ÿงฉ @Retryable๊ณผ ๋‹ค๋ฅธ Spring ๊ธฐ๋Šฅ๋“ค์˜ ์กฐํ•ฉ

@Retryable์€ ํ˜ผ์ž์„œ๋„ ๊ฐ•๋ ฅํ•˜์ง€๋งŒ, ๋‹ค๋ฅธ Spring ๊ธฐ๋Šฅ๋“ค๊ณผ ์กฐํ•ฉํ•˜๋ฉด ๋”์šฑ ๋น›์„ ๋ฐœํ•ด์š”. ์–ด๋–ค ์กฐํ•ฉ์ด ๊ฐ€๋Šฅํ• ๊นŒ์š”?

1. @Transactional๊ณผ์˜ ์กฐํ•ฉ

@Retryable์„ @Transactional๊ณผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•  ๋•Œ๋Š” ์ฃผ์˜๊ฐ€ ํ•„์š”ํ•ด์š”. ์™œ๋ƒ๊ณ ์š”? @Transactional์€ ๋ฉ”์„œ๋“œ ์‹คํ–‰์ด ๋๋‚  ๋•Œ ํŠธ๋žœ์žญ์…˜์„ ์ปค๋ฐ‹ํ•˜๊ฑฐ๋‚˜ ๋กค๋ฐฑํ•˜๋Š”๋ฐ, @Retryable๋กœ ์ธํ•ด ๋ฉ”์„œ๋“œ๊ฐ€ ์—ฌ๋Ÿฌ ๋ฒˆ ์‹คํ–‰๋˜๋ฉด ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ๊ฒฐ๊ณผ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๊ฑฐ๋“ ์š”.


@Service
public class PaymentService {

    @Transactional
    @Retryable(maxAttempts = 3)
    public void processPayment(Payment payment) {
        // ๊ฒฐ์ œ ์ฒ˜๋ฆฌ ๋กœ์ง
    }
}
  

์ด๋Ÿฐ ๊ฒฝ์šฐ, ๊ฐ ์žฌ์‹œ๋„๋งˆ๋‹ค ์ƒˆ๋กœ์šด ํŠธ๋žœ์žญ์…˜์ด ์‹œ์ž‘๋ผ์š”. ๊ทธ๋ž˜์„œ ์ด์ „ ์‹œ๋„์˜ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋ณ€๊ฒฝ์‚ฌํ•ญ์ด ๋กค๋ฐฑ๋˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ์–ด์š”. ์ฃผ์˜ํ•ด์„œ ์‚ฌ์šฉํ•ด์•ผ ํ•ด์š”!

2. @Async์™€์˜ ์กฐํ•ฉ

@Retryable์„ @Async์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๋ฉด ๋น„๋™๊ธฐ์ ์œผ๋กœ ์žฌ์‹œ๋„๋ฅผ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์–ด์š”. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋ฉ”์ธ ์Šค๋ ˆ๋“œ๋ฅผ ๋ธ”๋กœํ‚นํ•˜์ง€ ์•Š๊ณ  ์žฌ์‹œ๋„๋ฅผ ํ•  ์ˆ˜ ์žˆ์ฃ .


@Service
public class EmailService {

    @Async
    @Retryable(maxAttempts = 3)
    public CompletableFuture<void> sendEmail(String to, String subject, String body) {
        // ์ด๋ฉ”์ผ ์ „์†ก ๋กœ์ง
        return CompletableFuture.completedFuture(null);
    }
}
  </void>

์ด ๋ฐฉ์‹์€ ํŠนํžˆ ์™ธ๋ถ€ ์„œ๋น„์Šค ํ˜ธ์ถœ ๊ฐ™์€ ์‹œ๊ฐ„์ด ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๋Š” ์ž‘์—…์— ์œ ์šฉํ•ด์š”. ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ํ•ด์น˜์ง€ ์•Š์œผ๋ฉด์„œ๋„ ์•ˆ์ •์„ฑ์„ ๋†’์ผ ์ˆ˜ ์žˆ๊ฑฐ๋“ ์š”!

3. Spring Cloud Circuit Breaker์™€์˜ ์กฐํ•ฉ

@Retryable์„ Spring Cloud Circuit Breaker์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๋ฉด ๋”์šฑ ๊ฐ•๋ ฅํ•œ ์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ๊ตฌ์ถ•ํ•  ์ˆ˜ ์žˆ์–ด์š”. Circuit Breaker๋Š” ์ผ์ • ์ˆ˜์ค€ ์ด์ƒ์˜ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ์„œ๋น„์Šค ํ˜ธ์ถœ์„ ์ผ์‹œ์ ์œผ๋กœ ์ฐจ๋‹จํ•ด์„œ ์‹œ์Šคํ…œ์„ ๋ณดํ˜ธํ•˜๋Š” ์—ญํ• ์„ ํ•ด์š”.


@Service
public class ResillientService {

    @CircuitBreaker(name = "myService", fallbackMethod = "fallback")
    @Retryable(maxAttempts = 3)
    public String callExternalService() {
        // ์™ธ๋ถ€ ์„œ๋น„์Šค ํ˜ธ์ถœ ๋กœ์ง
    }

    public String fallback(Exception e) {
        return "์™ธ๋ถ€ ์„œ๋น„์Šค ํ˜ธ์ถœ ์‹คํŒจ. ๊ธฐ๋ณธ๊ฐ’ ๋ฐ˜ํ™˜.";
    }
}
  

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์žฌ์‹œ๋„ ํ›„์—๋„ ์„œ๋น„์Šค๊ฐ€ ์ •์ƒํ™”๋˜์ง€ ์•Š์œผ๋ฉด Circuit Breaker๊ฐ€ ์ž‘๋™ํ•ด์„œ ์‹œ์Šคํ…œ์„ ๋ณดํ˜ธํ•ด์š”. ์™„์ „ ์ฒ ๋ฒฝ ๋ฐฉ์–ด ์•„๋‹ˆ์—์š”? ๐Ÿ’ช

@Retryable๊ณผ Circuit Breaker์˜ ์กฐํ•ฉ @Retryable @Retryable @Retryable Circuit Breaker ์‹คํŒจ ์‹œ

์ด ๊ทธ๋ฆผ์„ ๋ณด์„ธ์š”. @Retryable์ด ์—ฌ๋Ÿฌ ๋ฒˆ ์‹œ๋„ํ•˜๊ณ  ์‹คํŒจํ•˜๋ฉด, Circuit Breaker๊ฐ€ ์ž‘๋™ํ•ด์„œ ์‹œ์Šคํ…œ์„ ๋ณดํ˜ธํ•˜๋Š” ๋ชจ์Šต์ด ๋ณด์ด์‹œ๋‚˜์š”? ์ด๋ ‡๊ฒŒ ์—ฌ๋Ÿฌ ๊ธฐ์ˆ ์„ ์กฐํ•ฉํ•˜๋ฉด ๋”์šฑ ๊ฐ•๋ ฅํ•œ ์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ ์‹œ์Šคํ…œ์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ์–ด์š”!

๐Ÿšฆ @Retryable ์‚ฌ์šฉ ์‹œ ์ฃผ์˜์‚ฌํ•ญ

@Retryable์€ ์ •๋ง ์œ ์šฉํ•œ ๊ธฐ๋Šฅ์ด์ง€๋งŒ, ์‚ฌ์šฉํ•  ๋•Œ ์ฃผ์˜ํ•ด์•ผ ํ•  ์ ๋“ค์ด ์žˆ์–ด์š”. ์–ด๋–ค ๊ฒƒ๋“ค์ด ์žˆ๋Š”์ง€ ์•Œ์•„๋ณผ๊นŒ์š”?

  1. ๋ฉฑ๋“ฑ์„ฑ ํ™•์ธ: ์žฌ์‹œ๋„ํ•˜๋Š” ๋ฉ”์„œ๋“œ๊ฐ€ ๋ฉฑ๋“ฑ์„ฑ์„ ๊ฐ€์ง€๋Š”์ง€ ๊ผญ ํ™•์ธํ•ด์•ผ ํ•ด์š”. ๋ฉฑ๋“ฑ์„ฑ์ด๋ž€ ๋™์ผํ•œ ์š”์ฒญ์„ ์—ฌ๋Ÿฌ ๋ฒˆ ๋ณด๋‚ด๋„ ๊ฒฐ๊ณผ๊ฐ€ ๊ฐ™์€ ์„ฑ์งˆ์„ ๋งํ•ด์š”. ์˜ˆ๋ฅผ ๋“ค์–ด, ๊ฒฐ์ œ ์ฒ˜๋ฆฌ ๊ฐ™์€ ๊ฒฝ์šฐ ์žฌ์‹œ๋„๋กœ ์ธํ•ด ์ค‘๋ณต ๊ฒฐ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ์•ˆ ๋˜๊ฒ ์ฃ ?
  2. ์žฌ์‹œ๋„ ๊ฐ„๊ฒฉ ์„ค์ •: ์žฌ์‹œ๋„ ๊ฐ„๊ฒฉ์„ ๋„ˆ๋ฌด ์งง๊ฒŒ ์„ค์ •ํ•˜๋ฉด ์‹œ์Šคํ…œ์— ๋ถˆํ•„์š”ํ•œ ๋ถ€ํ•˜๋ฅผ ์ค„ ์ˆ˜ ์žˆ์–ด์š”. ๋ฐ˜๋Œ€๋กœ ๋„ˆ๋ฌด ๊ธธ๊ฒŒ ์„ค์ •ํ•˜๋ฉด ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์ด ๋‚˜๋น ์งˆ ์ˆ˜ ์žˆ์ฃ . ์ ์ ˆํ•œ ๊ท ํ˜•์„ ์ฐพ๋Š” ๊ฒŒ ์ค‘์š”ํ•ด์š”.
  3. ๋ฌดํ•œ ์žฌ์‹œ๋„ ๋ฐฉ์ง€: maxAttempts๋ฅผ ์„ค์ •ํ•˜์ง€ ์•Š์œผ๋ฉด ๋ฌดํ•œํžˆ ์žฌ์‹œ๋„ํ•  ์ˆ˜ ์žˆ์–ด์š”. ์ด๋Š” ์‹œ์Šคํ…œ ๋ฆฌ์†Œ์Šค๋ฅผ ๊ณผ๋„ํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ์ฃผ์˜๊ฐ€ ํ•„์š”ํ•ด์š”.
  4. ์˜ˆ์™ธ ์ฒ˜๋ฆฌ ์ฃผ์˜: ๋ชจ๋“  ์˜ˆ์™ธ์— ๋Œ€ํ•ด ์žฌ์‹œ๋„ํ•˜๋Š” ๊ฒƒ์€ ์ข‹์ง€ ์•Š์•„์š”. ์ผ์‹œ์ ์ธ ๋ฌธ์ œ๋กœ ์ธํ•œ ์˜ˆ์™ธ๋งŒ ์žฌ์‹œ๋„ํ•˜๋„๋ก ์„ค์ •ํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.
  5. ๋กœ๊น…๊ณผ ๋ชจ๋‹ˆํ„ฐ๋ง: ์žฌ์‹œ๋„ ํšŸ์ˆ˜์™€ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜๋“œ์‹œ ๋กœ๊น…ํ•˜๊ณ  ๋ชจ๋‹ˆํ„ฐ๋งํ•ด์•ผ ํ•ด์š”. ์ด๋ฅผ ํ†ตํ•ด ์‹œ์Šคํ…œ์˜ ๋ฌธ์ œ๋ฅผ ์กฐ๊ธฐ์— ๋ฐœ๊ฒฌํ•˜๊ณ  ๋Œ€์‘ํ•  ์ˆ˜ ์žˆ์–ด์š”.

๊ฒฝ๊ณ ! @Retryable์„ ๋‚จ์šฉํ•˜๋ฉด ์˜คํžˆ๋ ค ๋ฌธ์ œ๋ฅผ ์ˆจ๊ธฐ๋Š” ๊ฒฐ๊ณผ๋ฅผ ๋‚ณ์„ ์ˆ˜ ์žˆ์–ด์š”. ๊ทผ๋ณธ์ ์ธ ๋ฌธ์ œ ํ•ด๊ฒฐ ์—†์ด ์žฌ์‹œ๋„๋งŒ ๋ฐ˜๋ณตํ•˜๋ฉด ์‹œ์Šคํ…œ์˜ ์•ˆ์ •์„ฑ์ด ๋–จ์–ด์งˆ ์ˆ˜ ์žˆ๋‹ต๋‹ˆ๋‹ค. ํ•ญ์ƒ ์‹ ์ค‘ํ•˜๊ฒŒ ์‚ฌ์šฉํ•ด์•ผ ํ•ด์š”!

๐ŸŒŸ @Retryable ์‹ค์ „ ํ™œ์šฉ ํŒ

์ž, ์ด์ œ @Retryable์„ ์‹ค์ „์—์„œ ๋”์šฑ ํšจ๊ณผ์ ์œผ๋กœ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ํŒ๋“ค์„ ์•Œ์•„๋ณผ๊นŒ์š”?

1. ์žฌ์‹œ๋„ ์ •์ฑ… ์ปค์Šคํ„ฐ๋งˆ์ด์ง•

Spring Retry๋Š” ๊ธฐ๋ณธ์ ์ธ ์žฌ์‹œ๋„ ์ •์ฑ… ์™ธ์—๋„ ๋‹ค์–‘ํ•œ ์ปค์Šคํ…€ ์ •์ฑ…์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ์–ด์š”. ์˜ˆ๋ฅผ ๋“ค์–ด, ์ง€์ˆ˜ ๋ฐฑ์˜คํ”„(Exponential Backoff) ์ •์ฑ…์„ ๊ตฌํ˜„ํ•ด๋ณผ๊นŒ์š”?


@Configuration
public class RetryConfig {

    @Bean
    public RetryTemplate retryTemplate() {
        RetryTemplate retryTemplate = new RetryTemplate();
        
        ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy();
        backOffPolicy.setInitialInterval(1000);
        backOffPolicy.setMultiplier(2);
        backOffPolicy.setMaxInterval(30000);
        
        retryTemplate.setBackOffPolicy(backOffPolicy);
        
        return retryTemplate;
    }
}
  

์ด๋ ‡๊ฒŒ ์„ค์ •ํ•˜๋ฉด ์žฌ์‹œ๋„ ๊ฐ„๊ฒฉ์ด 1์ดˆ, 2์ดˆ, 4์ดˆ, 8์ดˆ... ์ด๋ ‡๊ฒŒ ๋Š˜์–ด๋‚˜๋‹ค๊ฐ€ ์ตœ๋Œ€ 30์ดˆ๊นŒ์ง€ ์ฆ๊ฐ€ํ•ด์š”. ๋„คํŠธ์›Œํฌ ์ง€์—ฐ์ด ์‹ฌํ•œ ํ™˜๊ฒฝ์—์„œ ํŠนํžˆ ์œ ์šฉํ•˜๋‹ต๋‹ˆ๋‹ค!

2. ์กฐ๊ฑด๋ถ€ ์žฌ์‹œ๋„

๋•Œ๋กœ๋Š” ํŠน์ • ์กฐ๊ฑด์—์„œ๋งŒ ์žฌ์‹œ๋„๋ฅผ ํ•˜๊ณ  ์‹ถ์„ ์ˆ˜ ์žˆ์–ด์š”. ์ด๋Ÿด ๋•Œ๋Š” RetryTemplate๊ณผ ํ•จ๊ป˜ RetryCallback์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด์š”.


@Service
public class ConditionalRetryService {

    private final RetryTemplate retryTemplate;

    public ConditionalRetryService(RetryTemplate retryTemplate) {
        this.retryTemplate = retryTemplate;
    }

    public String conditionalRetry() {
        return retryTemplate.execute(context -> {
            // ์—ฌ๊ธฐ์— ์žฌ์‹œ๋„ํ•  ๋กœ์ง์„ ์ž‘์„ฑํ•ด์š”
            if (someCondition()) {
                throw new RetryableException("์žฌ์‹œ๋„ ํ•„์š”!");
            }
            return "์„ฑ๊ณต!";
        }, context -> {
            // ๋ชจ๋“  ์žฌ์‹œ๋„๊ฐ€ ์‹คํŒจํ–ˆ์„ ๋•Œ์˜ ๋กœ์ง
            return "์ตœ์ข… ์‹คํŒจ";
        });
    }
}
  

์ด ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•˜๋ฉด ํ›จ์”ฌ ๋” ์„ธ๋ฐ€ํ•œ ์ œ์–ด๊ฐ€ ๊ฐ€๋Šฅํ•ด์ ธ์š”. ์™„์ „ ํ”„๋กœ ๊ฐœ๋ฐœ์ž ์Šคํƒ€์ผ์ด์ฃ ? ๐Ÿ˜Ž

3. ์žฌ์‹œ๋„ ์ƒํƒœ ๊ด€๋ฆฌ

์žฌ์‹œ๋„ ๊ณผ์ •์—์„œ ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•˜๊ณ  ์‹ถ์„ ๋•Œ๊ฐ€ ์žˆ์–ด์š”. ์ด๋Ÿด ๋•Œ๋Š” RetryContext๋ฅผ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์–ด์š”.