Mobile Storage를 마치 API 서버처럼 이용해보자!
flutter 모바일 환경의 로컬 스토리지를 마치 서버의 DB처럼 사용하는 방식은 어떨까 하는 아이디어에서 힌트를 얻어 이번 프로젝트를 시작하게 되었다.
Dec 11, 2024
서책임님과 이야기하던 중, 모바일 환경의 로컬 스토리지를 마치 서버의 DB처럼 사용하는 방식은 어떨까 하는 아이디어에서 힌트를 얻어 이번 프로젝트를 시작하게 되었다.
발상의 시작은 단순했다. "마치 서버의 DB에서 API를 통해 데이터를 가져오듯이 로컬 스토리지를 사용할 수 있지 않을까?”라는 생각이었다. 이를 응용해, 특정 데이터를 요청할 때마다 핸들러를 통해 필요한 데이터를 즉시 불러오는 구조를 구현하기로 했다.
프로젝트 핵심 아이디어
- 핸들러로 요청:
- 클라이언트가 서버에 API 요청을 보내는 것처럼, 모바일에서 특정 데이터를 요청하면 핸들러가 이를 처리하도록 설계.
- 예를 들어, 위치 정보를 요청할 때마다 핸들러가 동작해, 모바일 기기의 현재 위치 정보를 가져오는 구조.
- 실시간 데이터 제공:
- 요청 시마다 최신 데이터를 제공하는 방식으로, 정적인 로컬 데이터 사용이 아닌 동적인 데이터 활용을 가능하게 함.
- 위치 정보, 사진 등과 같은 실시간 데이터를 로컬 환경에서도 서버와 유사한 방식으로 처리.
- 로컬 스토리지의 확장성:
flutter_secure_storage
의 장점을 살린 개발 실행.
flutter_secure_storage: ^9.2.2
flutter_secure_storage
는 Flutter에서 보안성이 필요한 데이터를 로컬에 저장할 때 사용하는 패키지로, 특히 민감한 데이터를 다루는 데 강력한 장점을 제공합니다. 아래는 주요 장점들이다. 내가 왜 이 flutter_secure_storage
를 사용했는지를 명확하게 해줄 이유이다.1. 보안성
flutter_secure_storage
는 iOS Keychain과 Android Keystore를 활용해 데이터를 저장합니다.- iOS Keychain: Apple에서 제공하는 보안 저장소로, 민감한 데이터를 안전하게 보호합니다.
- Android Keystore: Android의 암호화 저장소로, 데이터를 암호화하고 시스템 수준의 보안을 제공합니다.
- 일반적인 파일 저장이나 SQLite와 달리, 데이터가 시스템 보안 계층에 의해 보호되므로, 외부 접근이 극히 어렵습니다.
2. 자동 암호화
- 데이터를 저장할 때 자동으로 암호화가 적용되며, 이를 복호화하지 않고는 접근할 수 없습니다.
- 개발자가 암호화 로직을 별도로 구현할 필요가 없어 간단하면서도 강력한 보안을 제공합니다.
이러한 이유로 나는
flutter_secure_storage
를 선택하였고 개발을 시작했다._registerJavaScriptHandler( handlerName: 'storeSecureData', callback: (args) async { if (args.isNotEmpty && args[0] is Map) { final Map<String, dynamic> data = Map<String, dynamic>.from(args[0]); try { // SecureStorage에 모든 키-값 쌍 저장 for (String key in data.keys) { final value = data[key]; if (value is String && value.isNotEmpty) { await secureStorage.write(key: key, value: value); } else { print("Skipping empty or invalid value for key: $key"); } } // WebView localStorage에도 저장 if (webViewController != null) { String jsCode = data.entries.map((entry) { final key = entry.key; final value = entry.value; return """ try { localStorage.setItem('$key', '${value.toString()}'); console.log('Stored in localStorage: key=$key, value=$value'); } catch (e) { console.error('Error storing key=$key in localStorage:', e); } """; }).join('\n'); await webViewController!.evaluateJavascript(source: jsCode); } else { print("WebViewController is not initialized."); } return 'All data stored successfully!'; } catch (e) { return 'Error occurred while storing data: $e'; } } else { return 'Invalid data format: args is empty or not a Map'; } }, );
- JavaScript 핸들러 등록:
handlerName: 'storeSecureData'
로 WebView에서 호출 가능한 JavaScript 핸들러를 등록했습니다! 이로 이제 vue3에서 바로 붙어서 요청할 수 있게 되었고- 이제 WebView에서
storeSecureData
를 호출하여 데이터를 전달하면, 이 데이터를 Flutter 쪽에서 처리할 수 있게 되었다.
- 데이터 유효성 검증:
- 전달받은
args
가 비어 있지 않고, 첫 번째 요소가Map
인지 확인한 후Map<String, dynamic>
형태로 변환한다. value
가String
이고 비어 있지 않을 경우에만 데이터를 저장하도록 설계해 유효하지 않은 데이터를 필터링하여 쓰레기 데이터가 쌓이지 않도록 노력한 부분!
flutter_secure_storage
에 데이터 저장:secureStorage.write()
를 통해 각 키-값 쌍을 안전하게 저장했다.- 민감한 데이터를 안전하게 관리하며, Keychain(iOS)과 Keystore(Android)를 활용하여 보안성을 100% 올렸다.
- WebView
localStorage
와 동기화: - WebView의
localStorage
에도 동일한 데이터를 저장하도록 처리하였다. evaluateJavascript
를 통해 각 키-값 쌍을 JavaScript 코드로 저장하고localStorage.setItem
에 실패할 경우 에러를 로깅하도록 JavaScript에try-catch
를 포함하였다.
- 에러 처리 및 반환값:
- 모든 단계에서 발생할 수 있는 예외를
try-catch
로 처리해 안정성을 높인 부분… 이건 역시 js 개발자로 빼놓을 수 없지 하면서 추가했다… - 성공 시
'All data stored successfully!'
, 실패 시 적절한 에러 메시지를 반환하였다~
Share article