Flutter 앱 개발시 데이터 저장은 필수적인 부분이다.
사용자의 설정, 앱의 상태, 또는 사용자 데이터 등을 저장하기 위해 여러 옵션이 있다. 이 글에서는 Flutter에서 사용할 수 있는 세 가지 주요 데이터 저장 방법인 SharedPreferences, 내부 저장소, SQLite의 차이점과 사용 사례를 간략하게 살펴보겠다.
SharedPreferences는 키-값 쌍으로 데이터를 저장하는 방법으로, 간단한 데이터를 지속적으로 저장할 때 주로 사용된다.
경량 데이터 저장: 사용자의 선호도나 설정 등 작은 데이터 세트를 저장하는 데 이상적이다.
간편성: 구현이 간단하고 사용하기 쉽다.
동기화: 데이터는 앱이 종료되거나 장치가 재부팅되더라도 유지된다.
Flutter에서 내부 저장소는 일반적으로 파일 시스템을 통해 데이터를 저장하는 방법을 말한다. 이는 주로 문자열, 이미지, 오디오 파일 등의 데이터를 저장하는 데 사용된다.
유연성: 다양한 형태의 데이터(텍스트, 이미지, 오디오 파일 등)를 저장할 수 있다.
대용량 데이터 저장: 큰 파일이나 복잡한 데이터 구조를 저장할 수 있다.
개인 정보 보호: 저장된 데이터는 앱 전용이므로 다른 앱에서 접근할 수 없다.
SQLite는 관계형 데이터베이스 관리 시스템으로, 복잡한 데이터 구조나 대용량 데이터를 효율적으로 관리할 수 있다.
특징
강력한 데이터 관리: 관계형 데이터베이스를 통해 복잡한 데이터 관리 및 쿼리가 가능하다.
트랜잭션 지원: 데이터 무결성을 보장한다.
대규모 데이터 처리: 대용량 데이터를 효율적으로 처리할 수 있다.
사용 사례
Flutter에서 데이터를 저장하는 방법을 선택할 때는 데이터의 종류, 크기, 복잡성 및 앱의 요구 사항을 고려해야 한다. SharedPreferences는 간단한 설정이나 경량 데이터를 저장하는 데 적합하고, 내부 저장소는 대용량 또는 복잡한 파일을 저장하는 데 사용된다. 반면, SQLite는 복잡한 데이터 구조와 대용량 데이터를 효율적으로 관리하고자 할 때 최적의 선택이다. 각각의 방법은 독특한 장단점을 가지며, 앱의 특성에 맞게 적절히 선택하는 것이 중요하다.
본인의 경우, 이미지와 여러 텍스트를 처리를 백엔드로 전송하기 전에 임시저장 기능을 구현하기 위해 데이터 종류를 고려하여 SQLite 데이터베이스를 구축했다.
구현 방법은 아래와 같이 설계했다.
pubspec.yaml
파일에 sqflite
와 path
패키지를 추가한다:dependencies:
flutter:
sdk: flutter
sqflite: ^2.0.0+4
path: ^1.8.0
import 'dart:io';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
class DatabaseHelper {
static final _databaseName = "MyDatabase.db";
static final _databaseVersion = 1;
static final table = 'words_table';
static final columnId = 'id';
static final columnEnglishWord = 'english_word';
static final columnEnglishExample = 'english_example';
static final columnKoreanWord = 'korean_word';
static final columnPartOfSpeech = 'part_of_speech';
static final columnSynonyms = 'synonyms';
static final columnAntonyms = 'antonyms';
static final columnImagePath = 'image_path';
// 싱글톤 클래스
DatabaseHelper._privateConstructor();
static final DatabaseHelper instance = DatabaseHelper._privateConstructor();
static Database _database;
Future<Database> get database async {
if (_database != null) return _database;
_database = await _initDatabase();
return _database;
}
_initDatabase() async {
String path = join(await getDatabasesPath(), _databaseName);
return await openDatabase(path,
version: _databaseVersion,
onCreate: _onCreate);
}
Future _onCreate(Database db, int version) async {
await db.execute('''
CREATE TABLE $table (
$columnId INTEGER PRIMARY KEY,
$columnEnglishWord TEXT NOT NULL,
$columnEnglishExample TEXT,
$columnKoreanWord TEXT,
$columnPartOfSpeech TEXT,
$columnSynonyms TEXT,
$columnAntonyms TEXT,
$columnImagePath TEXT
)
''');
}
// Helper 메서드
// DB에 row 삽입
Future<int> insert(Map<String, dynamic> row) async {
Database db = await instance.database;
return await db.insert(table, row);
}
Future<List<Map<String, dynamic>>> queryAllRows() async {
Database db = await instance.database;
return await db.query(table);
}
Future<int> deleteAll() async {
Database db = await instance.database;
return await db.delete(table);
}
}
void _addWord() async {
Map<String, dynamic> row = {
DatabaseHelper.columnEnglishWord: 'example',
DatabaseHelper.columnEnglishExample: 'This is an example.',
DatabaseHelper.columnKoreanWord: '예시',
DatabaseHelper.columnPartOfSpeech: 'noun',
DatabaseHelper.columnSynonyms: 'sample',
DatabaseHelper.columnAntonyms: 'antonym',
DatabaseHelper.columnImagePath: '/path/to/image.jpg'
};
final id = await DatabaseHelper.instance.insert(row);
print('inserted row id: $id');
}
void _queryAllWords() async {
final allRows = await DatabaseHelper.instance.queryAllRows();
allRows.forEach((row) => print(row));
}
void _deleteAllWords()