발생 상황
AsyncGuard를 통해 가공한 값을 사용하는 과정에서 nullcheck를 명시하였음에도 불구하고 Exception이 발생하는 상황
Sample Code
class TempAsyncNotifier extends _$TempAsyncNotifier{
...
foo() async {
state = await AsyncData.guard(
....
);
return state.value?.data ;
}
}
원인과 해결 방법
AsyncValue는 [AsyncData, AsyncLoading, AsyncError] 이렇게 3가지의 형태로 분리되어 사용됩니다. riverpod(provider)에서 제공하는 값이며 비동기 상태변화가 일어날 경우 사용하라고 제공한 타입들입니다.
throw가 발생할 가능성이 있는 작업은 AsyncValue.guard를 통해 구현하는 것을 권장하기도 합니다. AsyncValue.guard는 내부에서 발생하는 오류를 과할 정도로 잘 잡아줍니다. 상세한 내용은 각설하고 오류 발생 상황으로 넘어가겠습니다.
AsyncValue.guard() 내부의 콜백에서 throw를 발생시키는 경우 해당값은 AsyncError로 전환됩니다. 문제는 여기서 발생합니다.
만약 오류가 발생한다고 가정하면, 해당 값은 AsyncError로 변경되었을 것 입니다.
다음은 AsyncError 클래스의 value getter의 정의입니다.
@override
T? get value {
if (!hasValue) {
throwErrorWithCombinedStackTrace(error, stackTrace);
}
return _value;
}
해당 상태는 AsyncError로 변경되었기에 value호출 시 override가 발생하여 해당 getter가 호출됩니다.
즉, hasValue가 false인 상황이라면 null이 아닌 throw가 발생한다는 의미입니다.
따라서 상태가 전환될 수 있는 상황에서 value를 직접 조회하는 동작은 오류를 발생시킬 수 있습니다.
따라서 hasValue, hasError를 통해 직접 확인하여 직접 분기처리를 하거나
T? get valueOrNull {
if (hasValue) return value;
return null;
}
위와 같이 valueOrNull getter를 사용하는 것을 추천합니다.
valueOrNull은 AsyncValue의 확장함수이기 떄문에 override가 발생하지 않습니다.
비고
근본적인 원인은 Provider설계의 문제로 인하여 하나의 작업에 대하여 상태관리의 변화와 명시적 반환값을 모두 사용하는 부분 떄문이겠지만, 이정도는 개발이 진행되면 흔히 발생할 수 있는 사항이기도 합니다.
dart를 비롯한 현대적인 언어의 getter와 setter는 언제든지 가공될 수 있습니다. 이 부분을 인지하고 의심되는 상황이 발생할 경우 내부 코드를 직접 확인해보는게 좋을 수 있습니다.
value는 T? 타입입니다. 해당 인자를 사용할 시 null이 반환될 수 있다는 사실은 이미 명시되어 있다는 의미입니다. 그럼에도 수고스럽게 valueOrNull이라는 반환값이 있었습니다. 이 의미는 value와 valueOrNull 최소 한가지 이상의 값은 특수한 처리가 일어날 수 있다는 의미입니다.
물론 단순 실수일 가능성도 있겠지만 설령 실수라고 하여도 직접 확인하여 문제없음을 확인하는 것과 넘겨짚는건 큰 차이가 있다고 생각합니다. 진짜 실수라면 기여를 통해 수정할 수도 있을 겁니다.
'flutter' 카테고리의 다른 글
Flutter Hive box의 open을 어떻게 하는게 좋을까? (0) | 2024.10.12 |
---|---|
flutter_cache_manager를 사용한 Svg캐싱 (0) | 2024.09.04 |
제네릭 타입이 포함 된 data class 의 Freezed 적용 (0) | 2024.08.28 |
Google IO 2024 Fluttter 새로운 소식 후기 (0) | 2024.05.17 |
Bloc 환경에서 Testcase 적용 방법 (0) | 2024.04.25 |