주요 콘텐츠로 건너뛰기

자동 재시도

Riverpod에서 Provider는 실패하면 자동으로 재시도됩니다.

재시도는 provider를 계산하는 도중 예외가 발생하면 이루어집니다. 재시도 로직은 provider별로, 또는 모든 provider에 대해 전역으로 커스터마이즈할 수 있습니다.

기본적으로 provider는 최대 10번까지 재시도되며, 대기 시간은 200ms에서 6.4초까지 지수적으로 늘어납니다(exponential backoff). 기본 재시도 로직의 자세한 내용은 retry를 참고하세요.

재시도 로직 커스터마이즈하기​

커스텀 재시도 로직은 애플리케이션 전체에 적용할 수도 있고, 특정 provider에만 적용할 수도 있습니다.

구현 방식은 두 경우 모두 같습니다. 커스텀 재시도 로직은 Duration? 값을 반환하는 함수로, 이 값은 다음 재시도까지의 대기 시간을 나타냅니다(재시도를 멈추려면 null을 반환합니다).

다음은 최대 5번까지 재시도하고, 200ms부터 시작하는 지수 백오프를 사용하며, ProviderException은 무시하는 커스텀 retry 함수입니다.

Duration? myRetry(int retryCount, Object error) {
// ProviderException이면 재시도를 멈춥니다
if (retryCount >= 5) return null;
// ProviderException은 무시합니다
if (error is ProviderException) return null;

return Duration(milliseconds: 200 * (1 << retryCount)); // 지수 백오프
}

이 함수는 provider 안에서 사용해 해당 provider의 재시도 로직만 바꿀 수 있습니다.

final myProvider = Provider<int>(
retry: myRetry,
(ref) => 0,
);

또는 ProviderContainers/ProviderScopes에 전달해 전역으로 적용할 수도 있습니다.

// 순수 Dart 코드의 경우
final container = ProviderContainer(
retry: myRetry,
);

...

// Flutter 코드의 경우
runApp(
ProviderScope(
retry: myRetry,
child: MyApp(),
),
);

재시도 비활성화하기​

재시도를 비활성화하려면 재시도 함수에서 항상 null을 반환하기만 하면 됩니다. 애플리케이션 전체에서 재시도를 끄려면 다음과 같이 작성합니다.

runApp(
ProviderScope(
retry: (retryCount, error) => null,
child: MyApp(),
),
);

기본 재시도 로직에 대해​

기본 재시도 로직은 단순히 "실패하면 재시도"하는 방식보다 좀 더 영리하게 설계되었습니다. 특히 Error와 ProviderException은 재시도하지 않습니다.

Error는 복구할 수 없기 때문에 재시도하지 않습니다. Error는 코드에 버그가 있다는 뜻이므로 재시도해도 소용이 없습니다. 오히려 쓸모없는 재시도 기록으로 로그만 지저분해질 뿐입니다.

ProviderException을 재시도하지 않는 이유는, 이 예외가 provider 자체의 실패가 아니라 실패한 다른 provider의 예외를 다시 던진 것임을 나타내기 때문입니다. 다음 예시를 보세요.

final failedProvider = Provider<int>(
(ref) => throw Exception('This provider always fails'),
);

final myProvider = Provider<int>(
// 이 provider는 실패한 provider에 의존하므로
// ProviderException을 던집니다
(ref) => ref.watch(failedProvider),
);

이 예시에서 myProvider는 실패하긴 하지만, 실패의 원인은 아닙니다. 따라서 이를 재시도해도 소용이 없습니다. 재시도해야 하는 것은 failedProvider입니다.

즉, failedProvider의 재시도를 비활성화하면 myProvider도 재시도되지 않습니다.

재시도가 끝날 때까지 기다리기​

FutureProvider.future를 사용하면 비동기 provider가 완료될 때까지 기다릴 수 있다는 것은 이미 알고 계실 겁니다.

final value = await ref.watch(myProvider.future);

그렇다면 자동 재시도와는 어떻게 맞물려 동작할까요?

간단히 말해, 비동기 provider가 실패해 재시도되면 연결된 future는 다음 중 하나가 될 때까지 계속 기다립니다.

  • 모든 재시도가 소진되거나,
  • provider가 성공할 때까지.

덕분에 await ref.watch(myProvider.future)는 중간에 발생한 실패를 건너뜁니다.