[Flutter] API 캐싱 (Dio & Hive)
이번 글에서는 Flutter에서 API 캐싱을 이용하는 방법에 대해 글을 작성하겠습니다.
dio를 이용해서 캐싱을 진행하려고 하면 보통 두 개의 라이브러리가 제일 먼저 떠오를텐데요
dio
dio_cache_interceptor
통신 클라이언트 라이브러리인 dio와, dio에서 캐시를 진행하기 위한 interceptor 라이브러리입니다.
interceptor는 많은 분야에서 중개자로 통용됩니다. interceptor는 프록시의 역할인데
쿠키 데이터를 추가하거나 캐싱을 관리한다거나 등 네트워크 통신 사이에서 정보를 추가하는 작업을 진행할 수 있습니다.
만약 dio_cache_interceptor를 이용하고 계신다면, 기본적인 MemCacheStore를 이용하시고 계실 것입니다.
하지만 MemCacheStore는 이름 그대로, 메모리에 저장되는 캐싱이기 때문에 앱이 종료되면 캐싱 데이터가 모두 사라지게 됩니다.
디스크를 이용하여 캐싱을 활용하기 위해선 다음 라이브러리를 사용할 것입니다.
dio_cache_interceptor_hive_store
따라서 pubspec.yaml 파일에는 다음과 같은 라이브러리들이 추가된 상태입니다.
// pubspec.yaml
dio: ^5.4.0
dio_cache_interceptor: ^3.5.0
hive: ^2.0.5
dio_cache_interceptor_hive_store: ^3.2.0
crypto: ^3.0.1
crypto 디펜던시는, 추후 캐시 키를 활용하기 위해 사용했으니, 여러분도 사용하시면 좋을 것 같습니다.
이렇게 추가하셨으면, 이제 캐싱을 진행할 수 있습니다.
코드 예제를 보여드릴 테니, 입맛대로 바꿔서 활용하시면 좋을 것 같습니다.
late Dio _dio;
late HiveCacheStore cacheStore;
late CacheOptions customCacheOptions;
// Completer를 사용하여 초기화 완료 여부를 추적
Completer<void> _initializationCompleter = Completer<void>();
FinanceRepository() {
initialize();
}
Future<void> initialize() async {
var cacheDir = await getTemporaryDirectory();
cacheStore = HiveCacheStore(
cacheDir.path,
hiveBoxName: "your_app_name",
);
customCacheOptions = CacheOptions(
store: cacheStore,
policy: CachePolicy.forceCache,
priority: CachePriority.high,
maxStale: const Duration(days: 7),
hitCacheOnErrorExcept: [401, 404],
keyBuilder: (request) {
// crpyto 라이브러리를 이용해서 캐시 키를 생성합니다.
return sha256.convert(utf8.encode(request.uri.toString())).toString();
},
allowPostMethod: false,
);
_dio = Dio()
..interceptors.add(DioCacheInterceptor(options: customCacheOptions));
// 초기화가 완료되었음을 알림
_initializationCompleter.complete();
}
Future<dynamic> fetchAll() async {
// 초기화가 완료될 때까지 대기
await _initializationCompleter.future;
// 여기서부터 API 작업 진행하시면 됩니다
}
주석으로 설명을 남겨놓았는데요, 그래도 몇 가지 설명을 드리자면
Completer는 어떠한 작업의 초기화 여부를 추적하기 위해 사용합니다.
위 코드는 일반 클래스로 선언될텐데, initialize 함수를 통해 await을 이용한 비동기 작업들이 조금 요구됩니다.
하지만 생성자에서는 async를 사용할 수 없기에, 결론적으로 우리가 해당 클래스를 생성하고 fetchAll() 함수가 호출되기 전에
dio 초기화 작업이 모두 진행되지 않았을 수 있습니다.
따라서 dio 초기화 작업을 선행하고 나면 completer를 통해 신호를 보내주고, fetchAll에서는 completer가 끝날 경우까지 고려해주면 됩니다.
캐싱 저장소로 사용한 getTemporaryDirectory()는 기기 내 앱의 캐시데이터에 저장되기에, 이렇게 사용하시는 것을 추천드립니다.
또한 keyBuilder에서 캐시 키를 생성하기 위해 api 주소를 string으로 변환한 뒤에 sha256을 이용해서 해싱했는데
api 주소가 255자가 넘어가면 keyBuilder에서 예외가 발생하기 때문에, 해싱을 이용해서 글자수를 고정시켰습니다.
이렇게 이용하시면 어려운 예외사항들을 겪지 않으실 수 있습니다.
나머지 사항들은 읽어보시면 충분히 이해가 가실 수 있을 것 같고, 학습에도 도움이 될 것 같습니다.
도움이 되셨길 바랍니다!