예외 발생 후 재시도하는 방법을 다룰 때 retry 키워드에 대해 소개한 적이 있습니다. 그런데 retry에는 잘 알려지지 않은 짝꿍이 있는데, 바로 redo입니다. 두 키워드는 비슷하게 동작하지만, redo는 블록 전체가 아니라 루프의 개별 반복(iteration)만 다시 실행한다는 점이 다릅니다.
redo 키워드란?
이전 아카데미 글에서 배웠듯이, retry를 사용하면 블록 안의 코드를 처음부터 다시 실행할 수 있습니다.
begin
puts "Iteration"
raise
rescue
retry
end이 예제는 예외를 발생시키기 전에 콘솔에 "Iteration"이라는 문자열을 출력합니다. 이후 rescue 블록이 실행되면서 retry를 호출하고, 블록 전체가 처음부터 다시 시작됩니다. 결과적으로 프로그램은 Iteration을 무한히 출력하게 됩니다.
redo 키워드를 사용하면 루프에서도 비슷한 효과를 낼 수 있습니다. 특히 반복 작업 도중에 재시도가 필요한 상황에서 매우 유용합니다.
10.times do |i|
puts "Iteration #{i}"
redo if i > 2
end이 코드는 다음과 같이 출력됩니다.
$ ruby redo.rb
Iteration 0
Iteration 1
Iteration 2
Iteration 3
Iteration 3
Iteration 3
...
반복 카운트가 그대로 유지되는 것을 눈치채셨나요? redo는 실행을 루프의 시작 지점으로 되돌리기 때문에, 같은 인덱스로 계속 반복됩니다. 참고로 retry를 사용한 아래 코드도 정확히 동일한 출력을 보여줍니다.
10.times do |i|
begin
puts "Iteration #{i}"
raise if i > 2
rescue
retry
end
end루프에서 재시도 로직 구현하기
redo를 활용하면 루프 안에서 손쉽게 재시도 로직을 구현할 수 있습니다. 다음 예제에는 잡(job) 큐가 있으며, 각 잡은 :success 또는 :failure를 반환합니다. 잡이 성공할 때까지 루프의 동일한 반복을 계속 다시 실행하는 방식입니다.
[job_1, job_2, job_3, job_4].each do |job|
redo unless job.call == :success
endRuby 1.8과의 차이점
retry와 redo의 동작 방식은 Ruby 1.8과 1.9 사이에서 변경되었습니다. 과거에는 두 키워드 모두 루프의 반복을 다시 시작했지만, 서로 다른 방식으로 동작했습니다. Ruby 1.9부터는 retry는 begin/rescue 블록에서만, redo는 루프 내부에서만 사용할 수 있습니다.
next 키워드란?
현재 반복의 시작점으로 되돌아가는 것이 아니라, 다음 반복으로 건너뛰고 싶다면 next를 사용하면 됩니다.
10.times do |i|
puts "Iteration #{i}"
next if i > 2
puts "Iteration done"
end이 코드는 다음과 같이 출력됩니다.
$ ruby next.rb
Iteration 0
Iteration done
Iteration 1
Iteration done
Iteration 2
Iteration done
Iteration 3
Iteration 4
...
반복 카운터가 계속 증가하는 것을 확인할 수 있습니다. 대부분의 경우 개발자가 원하는 동작은 next입니다. 반면, 정확한 횟수만큼 실행되어야 하는 루프가 필요하거나 배열을 순회하면서 에러 처리를 해야 한다면 redo를 검토해 보세요.
루프에서 반복을 다시 실행하는 방법에 대해 새로운 것을 배우셨기를 바랍니다. 이 글이나 AppSignal Academy 시리즈의 다른 글에 대한 의견이 있다면 언제든지 알려주세요. 더 깊이 알아보고 싶은 Ruby 주제가 있다면 편하게 제안해 주세요.