본문 바로가기
스파르타/Flutter

[Flutter] shared_preference - ( week 3 )

by bakcoding_sparta 2023. 4. 23.

지금까지 만든 메모 앱은 메모를 작성하고 앱을 다시 켜게 되면 저장했던 메모는 지워지게 된다.

그 이유는 현재 메모의 작성은 램이라는 임시 메모리에 저장되기 때문이다. 메모를 램이 아닌 다른 곳에 저장을 해서 앱이 재시작되어도 이전 데이터를 유지할 수 있도록 만들어야 한다.

 

메모를 저장하는 방법은 크게 3가지 방법이 있다.

기기에 파일로 저장 

내용을 파일로 저장해 두고 앱을 시작할 때 파일을 읽어오는 방식이다. 주로 shared_preferences 패키지를 사용해서 구현한다.

 

기기 데이터베이스에 저장

모든 장치에는 SQLite라는 데이터 저장 전문 프로그램이 있다. 이 데이터베이스를 이용해서 데이터를 저장하고 관리할 수 있으며 복잡한 데이터를 기기에 저장하여 관리할 수 있다.

 

sqflite 패키지로 구현가능하다.

 

다른 컴퓨터(서버)에 저장

인터넷을 통해 다른 컴퓨터에 데이터를 전송하여 저장하는 방식이다.

 

 

이 중 shared_preferences 패키지를 이용하여 기기에 파일을 저장해 본다.

 

패키지 설명

SharedPreferences class

 $ flutter pub add shared_preferences

명령어를 콘솔에 입력하여 패키지를 설치한다.

 

객체 로드

shared_prefereces를 사용하기 위해서 우선 SharedPreferences 인스턴스를 가져와야 한다.

SharedPreferences prefs = await SharedPreferences.getInstance();

이때 로컬 저장소에서 데이터를 가져와야 하기 때문에 약간의 대기 시간이 발생하는데 불러오는 동작이 끝난 다음 사용하는 코드가 진행되도록 await을 사용해서 코드의 진행을 잠시 대기시킨다.

 

값 저장

값이 로컬 저장소에 저장될 때 Key와 Value로 구성된 Map 형태로 데이터를 저장한다. 저장 시 사용할 Key는 원하는 데로 정하면 된다.

prefs.setString("username", "John Doe");

저장할 수 있는 타입은 String, List <String>, double, int, bool이다.

 

 

값 호출

값을 불러올 때는 저장할 때 사용한 Key를 통해서 가져올 수 있다. 저장된 키나 값이 없는 경우 null이 반환된다.

String? value = prefs.getString("username");

 

 

마이메모에 적용

마이메모에서 작성한 메모는 Memo 타입으로 저장이 되기 때문에 이것을 그대로 pref로 저장할 수 없다.

따라서 Memo를 Map(json)으로 변환하고 Map을 String으로 변환해서 저장해야 한다.

 

그리고 가져올 때도 마찬가지로 String을 Map으로 Map을 다시 Memo 객체로 만들어내서 사용할 수 있다.

 

먼저 전역으로 prefs라는 변수를 선언하여 코드 어디서든 SharedPrefereces 객체에 접근할 수 있도록 한다.

late SharedPreferences prefs;

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  prefs = await SharedPreferences.getInstance();  runApp(
    MultiProvider(
      providers: [
        ChangeNotifierProvider(create: (context) => MemoService()),
      ],
      child: const MyApp(),
    ),
  );
}

await을 사용하기 위해서 main함수는 async로 선언해 준다.

 

그리고 서비스로 관리되는 Memo객체를 json으로 변환하기 위한 함수를 memo_service 파일에 작성해 준다.

class Memo {
  Memo({
    required this.content,
  });

  String content;

  Map toJson() {
    return {'content': content};
  }

  factory Memo.fromJson(json) {
    return Memo(content: json['content']);
  }
}

toJson 함수는 Memo를 Map으로 전환하고 json으로 바꾸어준다.

frmoJson은 json을 Map으로 변환하고 Memo 객체로 다시 반환한다.

 

메모를 저장하고 불러오는 함수도 만들어준다.

  saveMemoList() {
    List memoJsonList = memoList.map((memo) => memo.toJson()).toList();
    // [{"content": "1"}, {"content": "2"}]

    String jsonString = jsonEncode(memoJsonList);
    // '[{"content": "1"}, {"content": "2"}]'

    prefs.setString('memoList', jsonString);
  }

  loadMemoList() {
    String? jsonString = prefs.getString('memoList');
    // '[{"content": "1"}, {"content": "2"}]'

    if (jsonString == null) return; // null 이면 로드하지 않음

    List memoJsonList = jsonDecode(jsonString);
    // [{"content": "1"}, {"content": "2"}]

    memoList = memoJsonList.map((json) => Memo.fromJson(json)).toList();
  }

 

이제 메모가 생성, 변경, 삭제될 때마다 로컬에 저장된 정보를 갱신해주어야 한다.

  createMemo({required String content}) {
    Memo memo = Memo(content: content);
    memoList.add(memo);
    notifyListeners(); // Consumer<MemoService>의 builder 부분을 호출해서 화면 새로고침
    saveMemoList();
  }

  updateMemo({required int index, required String content}) {
    Memo memo = memoList[index];
    memo.content = content;
    notifyListeners();
    saveMemoList();
  }

  deleteMemo({required int index}) {
    memoList.removeAt(index);
    notifyListeners();
    saveMemoList();
  }

로컬에 데이터를 저장하는 saveMemoList를 각 함수마다 호출한다.

 

그리고 처음 앱이 실행될 때 로컬에 저장된 데이터를 불러오기 위한 코드를 추가한다.

 

class MemoService extends ChangeNotifier {
  MemoService() {
    loadMemoList();
  }

처음 MemoService가 생성될 때 메모 데이터가 로드되도록 생성자에서 loadMemoList 함수를 호출한다.

 

이제 메모를 작성하고 다시 앱을 실행시켜도 사라지지 않는 걸 확인할 수 있다.