본문 바로가기

Flutter

🍎 Flutter의 SQLite 사용하여 데이터 저장하기 CRUD with 간단한예제

반응형

https://flutter-ko.dev/docs/cookbook/persistence/sqlite

 

SQLite에 데이터 저장하기

로컬 디바이스에 많은 데이터를 저장하고 쿼리를 요청해야 한다면, 로컬 파일이나 키-값 저장소 대신 데이터베이스를사용해보세요. 일반적으로 데이터베이스는 다른 로컬 솔루션보다 더 빠른

flutter-ko.dev

 

📕 SQLite 란?? 

・ 관계형 데이터베이스 관리 시스템을 서버가 아니라 응용프로그램에 넣어 제공하는 가벼운 소프트웨어 라이브러리 입니다. 다른 SQL 데이터베이스와 다르게 분리된 서버를 가지고 있지 않고 일반 디스크 파일을 직접 읽고 쓰며 오직 한 개의 파일에 멀티 테이블, 색인, 트리거, 뷰 등을 가지고 있습니다. 저장된 데이터는 data/data/패지지명/databases 경로의 디렉토리에 저장됩니다. 

 

잠시 비교를 위해 기존의 DBMS를 알아보겠습니다. 

일반적으로 MySQL, PostgreSQL 등과 같은 RDBMS는 동작하기 위해서 별도의 서버 프로세스가 필요합니다. 데이터베이스 서버에 액세스하려는 응용 프로그램은 TCP/IP 프로토콜을 사용하여 요청을 보내고 받습니다. 이것을 클라이언트/서버 아키텍처라고 합니다.

 

반면 SQLite는 서버를 실행할 필요가 없습니다. SQLite 데이터베이스는 액세스하는 애플리케이션과 통합됩니다. 응용 프로그램은 디스크에 저장된 데이터베이스 파일에서 직접 읽고 쓰는 SQLite 데이터베이스와 상호 작용합니다. 

 

📕 SQLite를 사용하는 이유

・ 독립성 : 운영 체제 또는 외부 라이브러리의 최소한의 지원을 받습니다. 따라서 아이폰, 안드로이드 폰, 게임 콘솔, 미디어 플레이어 등과 같은 모든 환경에서 사용할 수 이습니다. 

 

・ 설치할 필요 없음 : 서버가 없는 아키덱처이기에 사용하기 전에 설치할 필요가 없습니다. 구성, 시작 및 중지해야 하는 서버 프로세스가 없습니다. 

 

・ 트랜잭션 호환 : SQLite의 모든 트랜잭션과 원자성 독작을 지원하므로  프로그램 충돌이나 정전에도 데이터베이스가 손상되지 않습니다.

 

📕 SQLite 사용예제

1. Sqflite를 설치합니다.

2. Database를 엽니다.

3. Table을 만듭니다.

4. CRUD를 수행합니다. 

 

CRUD가 가장 기본인데 매번 찾기 귀찮아서 간단한 예제 코드와 함께 정리했습니다. 제가 참고한 영상은 아래에 올려뒀습니다. 

미국인인데 쉬운 영어만 쓰셔서 알아듣기 좋습니다. 자막 켜시고 보면 이해잘되실거에요! 

 

1. Sqflite 패키지와 path 패키지를 pubspec.yaml - dependencies 아래 추가합니다. 

path 패키지는 sqlite를 사용하여 디스크에 저장할 데이터베이스의 위치를 정확히 정의할 수 있는 함수를 제공해주는 패키지입니다. 

2.Table을 만들어보겠습니다.

 

2-1 Grocery클래스를 만들고 데이터베이스에 저장할 자료들을 입력합니다. 

class Grocery {
  final int? id;
  final String name;

  Grocery({this.id, required this.name});

  factory Grocery.fromMap(Map<String, dynamic> json) => new Grocery(
        id: json['id'],
        name: json['name'],
      );

  Map<String, dynamic> toMap() {
    return {
      'id': id,
      'name': name,
    };
  }
}

2.Database를 엽니다. 

2-1 db.dart 파일을 하나 만들고 그 안에서 db와 관련된 코드들을 담겠습니다. 

import 'dart:async';
import 'dart:io';
import 'package:crud_demo01/models/grocery.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';

class DatabaseHelper {
  DatabaseHelper._privateConstructor();
  static final DatabaseHelper instance = DatabaseHelper._privateConstructor();

  static Database? _database;
  Future<Database> get database async => _database ??= await _initDatabase();

  Future<Database> _initDatabase() async {
    Directory documentsDirectory = await getApplicationDocumentsDirectory();
    String path = join(documentsDirectory.path, 'groceries.db');
    return await openDatabase(
      path,
      version: 1,
      onCreate: _onCreate,
    );
  }
  
   Future _onCreate(Database db, int version) async {
    await db.execute('''
    CREATE TABLE groceries(
      id INTEGER PRIMARY KEY,
      name TEXT
    )
    ''');
  }

2-2 main.dart에서 초기화해주는 코드를 추가합니다. 

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  runApp(const MyApp());
}

3. CRUD를 실행할 코드를 작성합니다. 

3-1 CREATE를 작성하는 코드입니다. 

Future<int> add(Grocery grocery) async {
    Database db = await instance.database;
    return await db.insert('groceries', grocery.toMap());
  }

3-2 READ를 불러오는 코드입니다. 

Future<List<Grocery>> getGroceries() async {
    Database db = await instance.database;
    var groceries = await db.query('groceries', orderBy: 'name');
    List<Grocery> groceryList = groceries.isNotEmpty
        ? groceries.map((c) => Grocery.fromMap(c)).toList()
        : [];
    return groceryList;
  }

 

3.3 UPDATE : 수정합니다. 

Future<int> update(Grocery grocery) async {
    Database db = await instance.database;
    return await db.update('groceries', grocery.toMap(),
        where: 'id = ?', whereArgs: [grocery.id]);
  }

 

3.4 DELETE : 삭제합니다. 

 Future<int> remove(int id) async {
    Database db = await instance.database;
    return await db.delete('groceries', where: 'id = ?', whereArgs: [id]);
  }

4. 마지막으로 homepage의 화면을 꾸며주면 완성입니다. 

import 'package:crud_demo01/models/db.dart';
import 'package:crud_demo01/models/grocery.dart';
import 'package:flutter/material.dart';

class HomePage extends StatefulWidget {
  HomePage({Key? key}) : super(key: key);

  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  int? selectedId;
  final textController = TextEditingController();
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: TextField(
          controller: textController,
        ),
      ),
      body: Center(
        child: FutureBuilder<List<Grocery>>(
          future: DatabaseHelper.instance.getGroceries(),
          builder:
              (BuildContext context, AsyncSnapshot<List<Grocery>> snapshot) {
            if (!snapshot.hasData) {
              return Center(
                child: Text('Loading'),
              );
            }
            return snapshot.data!.isEmpty
                ? Center(child: Text('No Groceries in List'))
                : ListView(
                    children: snapshot.data!.map((grocery) {
                      return Center(
                        child: Card(
                          color: selectedId == grocery.id
                              ? Colors.white70
                              : Colors.white,
                          child: ListTile(
                            onTap: () {
                              setState(() {
                                if (selectedId == null) {
                                  textController.text = grocery.name;
                                  selectedId = grocery.id;
                                } else {
                                  textController.text = '';
                                  selectedId = null;
                                }
                              });
                            },
                            title: Row(
                              mainAxisAlignment: MainAxisAlignment.spaceBetween,
                              children: [
                                Text(grocery.name),
                                IconButton(
                                  icon: Icon(Icons.delete),
                                  onPressed: () {
                                    setState(() {
                                      DatabaseHelper.instance
                                          .remove(grocery.id!);
                                    });
                                  },
                                ),
                              ],
                            ),
                          ),
                        ),
                      );
                    }).toList(),
                  );
          },
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () async {
          selectedId != null
              ? await DatabaseHelper.instance.update(
                  Grocery(id: selectedId, name: textController.text),
                )
              : await DatabaseHelper.instance.add(
                  Grocery(name: textController.text),
                );
          setState(() {
            textController.clear();
            selectedId = null;
          });
        },
        child: Icon(Icons.add),
      ),
    );
  }
}

 

참고한 영상 

https://youtu.be/noi6aYsP7Go

 

반응형