SharedPreferences

샤워실의 바보·2023년 11월 16일
0
post-thumbnail

SharedPreferences는 Flutter 및 다른 많은 모바일 플랫폼에서 사용되는 경량의 데이터 저장 솔루션입니다. 이를 사용하여 간단한 데이터(예: 사용자 설정, 애플리케이션의 상태 등)를 키-값 쌍으로 로컬 디바이스에 저장할 수 있습니다. SharedPreferences는 주로 간단한 정보를 영구적으로 저장하고, 앱이 재시작되더라도 그 정보를 유지할 필요가 있을 때 유용합니다.

SharedPreferences의 기본 사용 방법:

  1. 의존성 추가: pubspec.yaml 파일에 shared_preferences 패키지를 추가합니다.

    dependencies:
      flutter:
        sdk: flutter
      shared_preferences: ^latest_version
  2. 인스턴스 가져오기: SharedPreferences의 인스턴스를 비동기적으로 가져옵니다.

    SharedPreferences prefs = await SharedPreferences.getInstance();
  3. 데이터 읽기 및 쓰기:

    • 데이터 쓰기: setInt, setString, setBool, setDouble, setStringList 등의 메소드를 사용하여 데이터를 저장합니다.
      await prefs.setString('myKey', 'myValue');
    • 데이터 읽기: getInt, getString, getBool, getDouble, getStringList 등을 사용하여 데이터를 읽어옵니다.
      String? value = prefs.getString('myKey');
  4. 데이터 제거: remove 메소드를 사용하여 특정 키의 데이터를 제거합니다.

    await prefs.remove('myKey');

SharedPreferences 사용 시 고려할 점:

  • SharedPreferences는 간단한 데이터 저장에 적합합니다. 복잡한 데이터 구조나 큰 데이터 세트를 저장하기에는 적합하지 않습니다.
  • 저장된 데이터는 암호화되지 않으므로, 중요한 개인 정보나 민감한 데이터를 저장하는 데 사용해서는 안 됩니다.
  • 비동기적으로 동작하기 때문에, 데이터를 읽고 쓸 때는 await 키워드를 사용하여 비동기 처리를 해야 합니다.

SharedPreferences는 사용자의 선호 언어 설정, 테마 모드, 로그인 상태 유지와 같은 간단한 사용자 설정을 유지하는 데 매우 유용합니다.

import 'package:shared_preferences/shared_preferences.dart';

class PlaybackConfigRepository {
  static const String _autoplay = "autoplay";
  static const String _muted = "muted";

  final SharedPreferences _preferences;

  PlaybackConfigRepository(this._preferences);

  Future<void> setMuted(bool value) async {
    _preferences.setBool(_muted, value);
  }

  Future<void> setAutoplay(bool value) async {
    _preferences.setBool(_autoplay, value);
  }

  bool isMuted() {
    return _preferences.getBool(_muted) ?? false;
  }

  bool isAutoplay() {
    return _preferences.getBool(_autoplay) ?? false;
  }
}

SharedPreferences는 Flutter에서 제공하는 플러그인으로, 키-값 쌍으로 데이터를 저장하고 읽어오는 간단한 영구 저장 메커니즘이며, 이는 Android의 SharedPreferences 및 iOS의 NSUserDefaults에 대한 래퍼입니다.

주어진 PlaybackConfigRepository 클래스에서, setMutedsetAutoplay 메서드가 async 키워드를 사용하여 비동기로 선언되어 있습니다. 그 이유는 다음과 같습니다:

  1. I/O Operations: SharedPreferences에서 값을 저장하거나 불러오는 것은 I/O 작업입니다. 이러한 작업들은 시간이 걸릴 수 있으므로, 메인 스레드에서 동기적으로 실행되면 사용자 인터페이스가 멈출 수 있습니다.

  2. Non-blocking: 비동기적으로 처리하면, 작업이 완료될 때까지 기다리지 않고 다른 작업을 계속 수행할 수 있습니다. Future를 반환함으로써, 작업의 완료를 기다리거나 다른 작업과 조합하여 실행할 수 있습니다.

  3. SharedPreferences API Design: SharedPreferencessetBool 메서드 자체가 비동기적으로 설계되어 있습니다. 따라서 해당 메서드를 호출할 때 await 키워드를 사용해야 합니다. 그러기 위해서는 함수를 async으로 선언해야 합니다.

그러나, isMutedisAutoplay 메서드에서는 비동기적인 코드가 없는데, 이는 SharedPreferences에서 값을 바로 읽어오기 때문입니다. 이러한 값을 읽어오는 메서드는 동기적으로 동작하므로 Future를 반환하지 않습니다.

요약하면, setMutedsetAutoplay 메서드는 SharedPreferences에 값을 저장하는 I/O 작업을 수행하기 때문에, 이를 비동기적으로 처리하여 UI 스레드가 차단되지 않도록 합니다.

import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:toonflix2/models/webtoon_detail_model.dart';
import 'package:toonflix2/models/webtoon_episode_model.dart';
import 'package:toonflix2/services/api_service.dart';
import 'package:toonflix2/widgets/episode_widget.dart';

class DetailScreen extends StatefulWidget {
  final String title, thumb, id;

  const DetailScreen({
    super.key,
    required this.title,
    required this.thumb,
    required this.id,
  });

  
  State<DetailScreen> createState() => _DetailScreenState();
}

class _DetailScreenState extends State<DetailScreen> {
  late Future<WebtoonDetailModel> webtoon;
  late Future<List<WebtoonEpisodeModel>> episodes;
  late SharedPreferences prefs;
  bool isLiked = false;

  Future initPrefs() async {
    prefs = await SharedPreferences.getInstance();
    final likedToons = prefs.getStringList('likedToons');
    if (likedToons != null) {
      if (likedToons.contains(widget.id) == true) {
        setState(() {
          isLiked = true;
        });
      }
    } else {
      await prefs.setStringList('likedToons', []);
    }
  }

  
  void initState() {
    super.initState();
    webtoon = ApiService.getToonById(widget.id);
    episodes = ApiService.getLatestEpisodesById(widget.id);
    initPrefs();
  }

  onHeartTap() async {
    final likedToons = prefs.getStringList('likedToons');
    if (likedToons != null) {
      if (isLiked) {
        likedToons.remove(widget.id);
      } else {
        likedToons.add(widget.id);
      }
      await prefs.setStringList('likedToons', likedToons);
      setState(() {
        isLiked = !isLiked;
      });
    }
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.white,
      appBar: AppBar(
        elevation: 2,
        backgroundColor: Colors.white,
        foregroundColor: Colors.green,
        actions: [
          IconButton(
            onPressed: onHeartTap,
            icon: Icon(
              isLiked ? Icons.favorite : Icons.favorite_outline,
            ),
          ),
        ],
        title: Text(
          widget.title,
          style: const TextStyle(
            fontSize: 24,
          ),
        ),
      ),
      body: SingleChildScrollView(
        child: Padding(
          padding: const EdgeInsets.all(50),
          child: Column(
            children: [
              Row(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  Hero(
                    tag: widget.id,
                    child: Container(
                      width: 250,
                      clipBehavior: Clip.hardEdge,
                      decoration: BoxDecoration(
                        borderRadius: BorderRadius.circular(15),
                        boxShadow: [
                          BoxShadow(
                            blurRadius: 15,
                            offset: const Offset(10, 10),
                            color: Colors.black.withOpacity(0.3),
                          )
                        ],
                      ),
                      child: Image.network(widget.thumb),
                    ),
                  ),
                ],
              ),
              const SizedBox(
                height: 25,
              ),
              FutureBuilder(
                future: webtoon,
                builder: (context, snapshot) {
                  if (snapshot.hasData) {
                    return Column(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: [
                        Text(
                          snapshot.data!.about,
                          style: const TextStyle(fontSize: 16),
                        ),
                        const SizedBox(
                          height: 15,
                        ),
                        Text(
                          '${snapshot.data!.genre} / ${snapshot.data!.age}',
                          style: const TextStyle(fontSize: 16),
                        ),
                      ],
                    );
                  }
                  return const Text("...");
                },
              ),
              const SizedBox(
                height: 25,
              ),
              FutureBuilder(
                future: episodes,
                builder: (context, snapshot) {
                  if (snapshot.hasData) {
                    return Column(
                      children: [
                        for (var episode in snapshot.data!)
                          Episode(
                            episode: episode,
                            webtoonId: widget.id,
                          )
                      ],
                    );
                  }
                  return Container();
                },
              ),
            ],
          ),
        ),
      ),
    );
  }
}

이 코드에서 SharedPreferences는 사용자의 '좋아요' 상태를 로컬 저장소에 저장하고 불러오는 데 사용됩니다. DetailScreen은 웹툰의 상세 정보와 에피소드를 표시하는 StatefulWidget이며, 사용자가 특정 웹툰을 '좋아요' 했는지의 여부를 SharedPreferences를 통해 관리합니다.

SharedPreferences의 활용 방법

  1. 인스턴스 초기화:

    • initPrefs 함수에서 SharedPreferences.getInstance()를 호출하여 SharedPreferences의 인스턴스를 초기화합니다. 이는 비동기적으로 수행되므로 await 키워드가 필요합니다.
  2. 데이터 저장 및 불러오기:

    • likedToons는 사용자가 '좋아요'한 웹툰의 ID 목록을 저장하는 키입니다.
    • 사용자가 웹툰을 '좋아요'하면 그 웹툰의 ID가 likedToons 목록에 추가되고, '좋아요'를 취소하면 해당 ID가 목록에서 제거됩니다.
    • prefs.getStringList('likedToons')를 호출하여 현재 '좋아요'한 웹툰의 목록을 불러옵니다.
    • prefs.setStringList('likedToons', likedToons)를 사용하여 수정된 목록을 다시 저장합니다.
  3. 상태 업데이트:

    • onHeartTap 함수에서는 사용자가 하트 아이콘을 탭할 때마다 '좋아요' 상태를 토글하고, 이 변경 사항을 SharedPreferences에 저장합니다.
    • setState를 호출하여 UI를 최신 상태로 업데이트합니다.

코드 내 SharedPreferences의 역할

  • 이 코드에서 SharedPreferences는 사용자가 특정 웹툰을 '좋아요' 했는지 여부를 저장하고 추적하는 데 사용됩니다. 이를 통해 앱을 종료하고 다시 시작해도 사용자의 '좋아요' 상태가 유지됩니다.
  • SharedPreferences를 사용함으로써 사용자 경험을 개선하고, 앱의 사용성을 높일 수 있습니다.

이 예제는 SharedPreferences를 사용하여 간단한 사용자 설정이나 상태를 유지하고 관리하는 방법을 잘 보여줍니다. Flutter 앱 개발에서 SharedPreferences는 이와 같이 간단한 데이터를 영구적으로 저장하고 불러오는 데 유용하게 사용됩니다.

profile
공부하는 개발자

0개의 댓글