티스토리 뷰

Flutter 앱 개발 중 사용자 설정이나 간단한 데이터를 로컬에 저장하고 싶을 때, Key-Value 저장 방식은 매우 간단하면서도 효과적인 솔루션입니다. 이 블로그에서는 Flutter에서 SharedPreferences를 사용해 Key-Value 데이터를 저장하고 불러오는 방법과 활용 사례를 소개합니다.

참고. Store key-value data on disk

Key-Value 저장이란?

Key-Value 저장은 데이터를 키와 값의 쌍으로 관리하는 간단한 방식입니다. 예를 들어, 사용자의 설정값을 저장할 때 "themeMode: dark"와 같은 구조로 데이터를 보관합니다. 이러한 방식은 다음과 같은 장점을 제공합니다:

  1. 빠른 데이터 접근: 데이터베이스보다 간단하고 빠른 액세스 가능.
  2. 가벼운 저장소: 소량의 데이터 저장에 적합.
  3. 구현의 간편성: 복잡한 데이터 구조 없이 간단히 구현 가능.

SharedPreferences란?

Flutter에서 Key-Value 저장을 구현하려면 SharedPreferences 패키지를 사용합니다. 이 패키지는 데이터를 간단히 로컬에 저장하고 다시 불러올 수 있는 인터페이스를 제공합니다.

SharedPreferences 설정

1. 패키지 추가

Flutter 프로젝트에 SharedPreferences를 사용하려면 pubspec.yaml에 의존성을 추가해야 합니다.

dependencies:
  shared_preferences: ^2.0.15

그런 다음, 아래 명령어로 패키지를 설치합니다:

flutter pub get

SharedPreferences 사용 방법

1. 데이터 저장

SharedPreferences에 데이터를 저장하는 것은 매우 간단합니다. 예를 들어, themeMode라는 키에 값을 저장하려면 다음과 같이 작성합니다:

import 'package:shared_preferences/shared_preferences.dart';

Future<void> saveThemeMode(String themeMode) async {
  final prefs = await SharedPreferences.getInstance();
  await prefs.setString('themeMode', themeMode);
}

2. 데이터 읽기

저장된 데이터를 불러오려면 아래와 같이 구현합니다:

Future<String?> getThemeMode() async {
  final prefs = await SharedPreferences.getInstance();
  return prefs.getString('themeMode');
}

3. 데이터 삭제

특정 키에 해당하는 데이터를 삭제하거나 전체 데이터를 초기화할 수도 있습니다.

Future<void> removeThemeMode() async {
  final prefs = await SharedPreferences.getInstance();
  await prefs.remove('themeMode');
}

Flutter 앱에서 SharedPreferences 활용하기

예제: 다크모드 저장 및 적용

간단한 테마 변경 기능을 구현해 보겠습니다. 사용자가 선택한 테마 모드를 저장하고, 앱 재실행 시 적용되는 예제입니다.

import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: ThemeSwitcher(),
    );
  }
}

class ThemeSwitcher extends StatefulWidget {
  @override
  _ThemeSwitcherState createState() => _ThemeSwitcherState();
}

class _ThemeSwitcherState extends State<ThemeSwitcher> {
  bool isDarkMode = false;

  @override
  void initState() {
    super.initState();
    loadThemeMode();
  }

  Future<void> loadThemeMode() async {
    final prefs = await SharedPreferences.getInstance();
    setState(() {
      isDarkMode = prefs.getBool('isDarkMode') ?? false;
    });
  }

  Future<void> toggleThemeMode() async {
    final prefs = await SharedPreferences.getInstance();
    setState(() {
      isDarkMode = !isDarkMode;
      prefs.setBool('isDarkMode', isDarkMode);
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('SharedPreferences 예제')),
      body: Center(
        child: Switch(
          value: isDarkMode,
          onChanged: (value) => toggleThemeMode(),
        ),
      ),
      backgroundColor: isDarkMode ? Colors.black : Colors.white,
    );
  }
}

주요 사용 사례

  1. 사용자 설정 저장:
    • 테마 모드, 언어 설정, 알림 옵션 등.
  2. 세션 관리:
    • 로그인 상태, 사용자 토큰 등.
  3. 앱 상태 유지:
    • 사용자가 앱을 닫았다가 다시 열 때 마지막 상태를 기억.

SharedPreferences 사용 시 주의사항

  1. 데이터 크기 제한: SharedPreferences는 소량의 데이터를 저장하는 데 적합하며, 대량 데이터는 데이터베이스를 사용하는 것이 좋습니다.
  2. 암호화 필요: 민감한 정보는 암호화를 통해 저장해야 보안이 강화됩니다.
  3. 비동기 처리: 모든 작업이 비동기로 처리되므로 적절한 에러 처리가 필요합니다.

결론

Flutter에서 Key-Value 저장 방식은 사용자 설정이나 간단한 데이터 저장에 매우 유용합니다. SharedPreferences는 이러한 작업을 쉽고 빠르게 구현할 수 있는 도구를 제공합니다. 위의 가이드를 참고하여 Flutter 앱에서 로컬 데이터를 효율적으로 관리해 보세요!

 

import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: 'Shared preferences demo',
      home: MyHomePage(title: 'Shared preferences demo'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  @override
  void initState() {
    super.initState();
    _loadCounter();
  }

  /// Load the initial counter value from persistent storage on start,
  /// or fallback to 0 if it doesn't exist.
  Future<void> _loadCounter() async {
    final prefs = await SharedPreferences.getInstance();
    setState(() {
      _counter = prefs.getInt('counter') ?? 0;
    });
  }

  /// After a click, increment the counter state and
  /// asynchronously save it to persistent storage.
  Future<void> _incrementCounter() async {
    final prefs = await SharedPreferences.getInstance();
    setState(() {
      _counter = (prefs.getInt('counter') ?? 0) + 1;
      prefs.setInt('counter', _counter);
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Text(
              'You have pushed the button this many times: ',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headlineMedium,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}