-
[FLUTTER] shared_preferences 패키지 사용해보기 (앱 자체 데이터 저장소)Flutter 2023. 4. 6. 18:37
Flutter에서는 앱 자체 데이터베이스를 사용할 수 있다.
그러나 Flutter에서는 iOS와 Android에서 사용하는 기본 데이터베이스가 다르다.
나는 iOS만 사용해봤는데, iOS에서 사용하는 기본 데이터베이스는 CoreData이고, Android에서 사용하는 기본 데이터베이스는 SQLite라고한다.
Flutter에서는 이러한 기본 데이터베이스를 직접 사용할 수 있지만, 이를 위해서는 각 플랫폼에서 지원하는 네이티브 코드를 작성해야 야하는 불편함이 존재한다. 또한, shared_preferences 패키지를 통해 앱 자체 데이터베이스를 대체할 수 있는 영구적인 데이터 저장소를 제공하지만, 이는 일종의 key-value 저장소로, 복잡한 데이터 모델링을 지원하지 않는다고 한다. (ㅠㅠ따라서 사용 시에 주의해야 한다ㅠㅠ)
https://org9899.tistory.com/121
[FLUTTER] 화면전환 (Navigator)
[Navigator 화면전환] 각 화면을 라우트(Route)라고 부르며, 화면을 이동할 때 네비게이터(Navigator)를 사용 다음페이지로 이동 Navigator.push( context, MaterialPageRoute(builder: (context) => SecondPage()), // 이동하려
org9899.tistory.com
위 스토리를 보면 온보딩 페이지를 볼 수 있다. (하단의 온보딩 페이지 전체 소스코드 참고)
온보딩을 완료했지만 Restart를 눌러 앱을 재시작 하는 경우, 온보딩 페이지로 계속 시작되는 문제가 있다. 이런 경우 보통 온보딩 완료 여부를 어딘가에 저장해두고 앱 실행시 해당 값을 불러와 온보딩을 완료한 경우 바로 HomePage를 보여주는 방식으로 해결한다.
그런데 지금까지 작성한 모든 코드는 메모리(Memory)라는 공간에서 실행되는데, 이 공간은 앱을 재시작 하거나 종료하는 경우 모두 사라져버리는 문제가 있다!
💡 데이터를 메모리가 아닌 다른 곳에 저장해 앱을 재시작해도 이전 데이터를 유지하는 방법
- 기기에 파일로 저장하기 내용을 파일로 저장해두고 앱을 시작할 때 파일을 읽어오는 방식. (패키지 : shared_preferences)
- 기기 데이터베이스에 저장하기 모든 폰에는 SQLite라는 데이터베이스(데이터 저장 전문 프로그램)이 있는데 이 데이터베이스를 이용하여 데이터를 보존할 수 있다 -좀 더 복잡한 데이터를 기기에 저장할 수 있음. (패키지 : sqflite)
- 다른 컴퓨터(서버)에 저장하기 인터넷을 통해 다른 컴퓨터에 데이터를 전송하여 저장하는 방식.
여기선 shared_preferences 패키지를 이용하여 1. 기기에 파일로 저장 방법을 배워보도록 할 것이다!
1. 패키지 설치
https://pub.dev/packages/shared_preferences/install
shared_preferences | Flutter Package
Flutter plugin for reading and writing simple key-value pairs. Wraps NSUserDefaults on iOS and SharedPreferences on Android.
pub.dev
- 터미널에서 아래 명령어를 입력
flutter pub add shared_preferences- 사용할 페이지에 import
import 'package:shared_preferences/shared_preferences.dart';2. shared_preferences 패키지 사용법
2-1. 사용준비
SharedPreferences를 이용하려면 먼저 SharedPreferences 인스턴스를 가져와야한다. (가져온 인스턴스를 prefs 변수에 저장)
// 인스턴스 생성 SharedPreferences prefs = await SharedPreferences.getInstance();기기에 저장된 파일을 읽어오는데, 다소 시간이 걸리는데 완료될 때 까지 기다리도록 앞에 await이라는 키워드를 붙여준다.
await란?2-2. 값 저장하기
SharedPreferences는 데이터를 Key와 Value로 구성된 Map 형태로 데이터를 저장한다.
: 저장시 사용되는 Key는 원하는 이름으로 정하시면 된다!
예를들어, 온보딩을 완료했는지 여부를 isOnboarded라는 이름의 Key에 bool 타입을 저장한다면 아래와 같다.
prefs.setBool("isOnboarded", true);2-3. 값 불러오기
저장한 isOnboarded라는 Key를 이용해 다시 값을 가져올 수 있다. (저장된 값이 없는 경우 null을 반환하는 점 유의!)
bool? value = prefs.getBool("isOnboarded");SharedPreferences에는String, List<String>, double, int, bool 타입을 저장 할 수 있다.
좀 더 자세한 사항은 공식 문서 참고
SharedPreferences class - shared_preferences library - Dart API
Wraps NSUserDefaults (on iOS) and SharedPreferences (on Android), providing a persistent store for simple data. Data is persisted to disk asynchronously. Properties hashCode → int The hash code for this object. read-onlyinherited runtimeType → Type A r
pub.dev
3. shared_preferences 사용준비
3-1. main 함수에서 앱이 시작되기 전에, SharedPreferences 인스턴스를 불러오기
void main() async { // main() 함수에서 async를 쓰려면 필요 WidgetsFlutterBinding.ensureInitialized(); // shared_preferences 인스턴스 생성 SharedPreferences prefs = await SharedPreferences.getInstance();위 코드를 main에 추가하면, 아래와 같다. 아마 위 코드를 추가하면 오류가 나올것이다. 그 오류는 await 키워드를 사용할때는 async를 사용해야한다는 키워드이다! → 파일을 읽을 때 시간이 걸리므로 await을 앞에 적어 기다리도록 만들어준다. 단, await 사용시 해당 함수의 소괄호와 중괄호 사이에 async를 추가해야한다!
왜 async를 사용해야하는지?
→ await는 Promise 객체가 resolve되기를 기다리는 데 사용되는 키워드인데, await를 사용하면 Promise가 resolve될 때까지 코드 실행이 중지되고, Promise가 resolve되면 결과값을 반환한다. async 함수는 비동기적인 코드를 작성할 때 사용되며, await를 포함하는 함수가 비동기 함수이기 때문이다. async 함수 내에서 await를 사용하면 자동으로 해당 함수는 Promise를 반환한다.
(async 함수가 아닌 일반적인 함수에서 await를 사용하려고 하면 SyntaxError가 발생!)
void main() async { // main() 함수에서 async를 쓰려면 필요 WidgetsFlutterBinding.ensureInitialized(); // shared_preferences 인스턴스 생성 SharedPreferences prefs = await SharedPreferences.getInstance(); runApp(MyApp()); }→ 또한, main 함수에서 async & await을 사용하려면 WidgetsFlutterBinding.ensureInitialized();를 넣어줘야한다!
- 위 코드에서 보면 현재 SharedPreferences 인스턴스인 prefs는 main 함수 내부에서만 사용 가능하다.
향후 prefs 변수를 OnboardingPage 클래스와 HomePage 클래스 등 여러 곳에서 사용할 예정인데, 각 클래스 생성자에 전달하면 코드가 길어지므로 어디서든 접근 가능한 전역 변수로 변경!
late SharedPreferences prefs; // 전역변수 void main() async { // main() 함수에서 async를 쓰려면 필요 WidgetsFlutterBinding.ensureInitialized(); // shared_preferences 인스턴스 생성 prefs = await SharedPreferences.getInstance(); runApp(MyApp()); }late 키워드를 붙여주지않는다면?
: late를 붙이지않고 마우스를 올려봤을때, 비어있을 수 없는 변수(non-nullable variable)이므로 초기값을 넣어라는 에러가 발생한다.
→ SharedPreferences 뒤에 비어있을 수 있다는 의미의 물음표(?)를 붙여주어 해결할 수 있지만, main 함수에서 값을 넣어주므로 나중에 값을 할당해 준다는 의미로 7번째 줄 앞에 late 키워드를 추가해 문제를 해결한다!
- late 키워드 배우기
아래 코드는 String name = null;과 동일한 코드로 에러가 발생한다.
String name; // null이 될 수 없는 타입에 null이 할당되어 에러 발생위 코드를 고치는 첫 번째 방법은 String?로 null 또는 String이라고 표시하는 것이다.
String? name; // null 또는 String두 번째 방법은 앞에 late라는 키워드를 붙여 나중에 String 을 넣어줄 거라고 선언하는 방식이다.
late String name; // 나중에 String을 넣을거다.위와 같은 경우 나중에 아래와 같이 값을 넣어주면 된다.
late String name; // 나중에 String을 넣을거다. name = "철수"; print(name); // 철수단, 값을 넣기 전에 호출하면 에러가 난다.
late String name; // 나중에 String을 넣을거다. print(name); // 아직 값을 안넣었으므로 에러 발생
4. 온보딩 여부 저장하고 불러오기
isOnboarded라는 Key로 온보딩 완료 여부를 저장하고, 만약 값이 true인 경우 앱 실행시 HomePage를 바로 띄워주는 방식으로 구현
4-1. 처음 어떤 화면을 보여줄지 결정하는 MyApp 위젯에서 온보딩 여부를 나타내는 isOnboarded 값을 가져오기
// SharedPreferences에서 온보딩 완료 여부 조회 // isOnboarded에 해당하는 값에서 null을 반환하는 경우 false 할당 bool isOnboarded = prefs.getBool("isOnboarded") ?? false;→ getBool("isOnboarded") 함수를 호출시 아무런 값이 없는 경우 null을 반환하는데, null인 경우 false를 반환하도록 ?? false를 붙여주었음.
4-2. isOnboarded 값이 true인 경우 바로 HomePage 위젯을 보여주고, 그렇지 않은 경우 OnboardingPage 위젯을 보여주도록 하겠습니다. 아래 코드스니펫을 복사해서 31번째 줄을 교체한 뒤 저장
home: isOnboarded ? HomePage() : OnboardingPage(),- MyApp
class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { // SharedPreferences에서 온보딩 완료 여부 조회 // isOnboarded에 해당하는 값에서 null을 반환하는 경우 false 할당 bool isOnboarded = prefs.getBool("isOnboarded") ?? false; return MaterialApp( debugShowCheckedModeBanner: false, theme: ThemeData( textTheme: GoogleFonts.getTextTheme('Jua'), ), home: isOnboarded ? HomePage() : OnBoardingPage(), ); } }4-3. OnboardingPage에서 Done 버튼을 누르는 시점에 온보딩을 완료했다고 볼 수 있음. 이 때 isOnboarded 값을 true로 저장.
// Done 클릭시 isOnboarded = true로 저장 prefs.setBool("isOnboarded", true);- onDone
onDone: () { // When done button is press // Done 클릭시 isOnboarded = true로 저장 prefs.setBool("isOnboarded", true); // Done 클릭시 페이지 이동 Navigator.pushReplacement( // 기존의 OnboardingPage는 없애고, HomePage로 교체하는 방식으로! (push->pushReplacement) context, // 이동하고 싶은 페이지 이름 적어주기 MaterialPageRoute(builder: (context) => HomePage()), ); },: 여기까지 완료했으면 restart 버튼을 눌러도 온보딩 페이지가 보이지않음.
+ 초기화하는 코드를 추가해 확인해보는 과정
actions: [ // 삭제 버튼 IconButton( onPressed: () { // SharedPreferences에 저장된 모든 데이터 삭제 prefs.clear(); }, icon: Icon(Icons.delete), ) ],: 아래와 같은 위치에 위 코드 추가
class HomePage extends StatelessWidget { // 페이지 이름 : HomePage const HomePage({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("Home Page!"), actions: [ // 삭제 버튼 IconButton( onPressed: () { // SharedPreferences에 저장된 모든 데이터 삭제 prefs.clear(); }, icon: Icon(Icons.delete), ) ], ), body: Center( child: Text( "환영합니다!", style: TextStyle( fontSize: 24, ), ), ), ); } }: prefs.clear();를 호출하면 저장된 모든 SharedPreferences를 초기화
- 결과
: 휴지통 버튼을 누르면 저장된 데이터가 전부 사라지면서 온보딩 페이지가 다시 나옴, 그 전까지는 restart를 눌러도 HomePage 화면만 나옴
'Flutter' 카테고리의 다른 글
[FLUTTER] 리펙토리(Refactory) (0) 2023.04.07 [FLUTTER] 비동기처리 (async와 await) -화면 간 데이터 전달 (0) 2023.04.07 [FLUTTER] 화면전환 (Navigator) (0) 2023.03.23 [FLUTTER] Stack (0) 2023.03.23 [FLUTTER] Column & Row (0) 2023.03.23