플러터 데이터베이스 Create Select Insert Delete – 메모장 앱 만들기 (7)

안녕하세요 지금까지 플러터(Flutter)에서 화면 간 데이터 전달을 구현하여 입력한 데이터를 표시하는 기능을 만들어보았습니다. 더 나아가 앱을 재실행해도 그 데이터가 유지되도록 플러터 데이터베이스 사용방법에 대해서 작성합니다.

플러터 강의 영상에서도 해당 글의 내용을 확인할 수 있습니다.

패키지 설치

Flutter 앱에서 데이터를 영구적으로 저장하기 위해서는 sqflite와 날짜를 표시해줄 intl 패키지가 필요합니다. 이 패키지들을 프로젝트에 추가하려면, pubspec.yaml 파일에 다음과 같이 의존성을 추가해야 합니다

dependencies:
  flutter:
    sdk: flutter
  path: ^1.8.3
  sqflite: ^2.0.0
  intl: ^0.18.1

데이터베이스

데이터베이스는 다양한 유형의 애플리케이션에서 필수적으로 사용되는 중요한 구성요소입니다. 앱을 개발하기 위해서 데이터를 CRUD: Create(생성), Read(읽기), Update(갱신), Delete(삭제) 하는 쿼리에 대해서는 반드시 알아야됩니다. 아래글에서 간단한 사용법을 참고하세요

데이터 모델 생성

데이터를 효율적으로 관리하기 위해, 먼저 메모 데이터를 위한 모델을 생성해야 합니다. MemoData 클래스는 메모의 내용, 생성 날짜 및 시간, 그리고 고유 ID를 포함합니다

// MemoData 클래스 정의: 메모 데이터를 위한 모델
class MemoData {
  int? id; // 메모의 고유 ID, nullable 타입으로 선언되어 있어서 null일 수 있음
  String content; // 메모의 내용을 저장하는 변수, 필수 입력 필드
  final DateTime createAt; // 메모가 생성된 시간을 저장하는 변수, final로 선언되어 생성 후 변경 불가

  // 생성자: id(옵션), content, createAt(필수)를 받아 객체를 초기화함
  MemoData({this.id, required this.content, required this.createAt});

  // toMap 메서드: 객체를 Map<String, dynamic>으로 변환해주는 메서드
  // Firestore 등의 데이터베이스에 저장하기 위한 준비 과정에서 사용될 수 있음
  Map<String, dynamic> toMap() {
    return {
      'id': id, // id를 Map에 추가, null일 수 있음
      'content': content, // content를 Map에 추가
      'createAt': createAt.toIso8601String(), // createAt을 ISO8601 문자열 형식으로 변환하여 Map에 추가
    };
  }
}

데이터베이스 헬퍼 클래스

데이터를 저장하고 관리하기 위해 DatabaseHelper 클래스를 생성합니다. 이 클래스는 데이터베이스 초기화, 메모 목록 조회, 메모 추가 및 삭제 기능을 담당합니다.

import 'package:path/path.dart'; 
import 'package:sqflite/sqflite.dart';

import '../models/memo_data.dart'; 

// DatabaseHelper 클래스 정의: 데이터베이스 관련 작업을 도와주는 유틸리티 클래스
class DatabaseHelper {
  static Database? _database; // 데이터베이스 인스턴스를 저장하는 private static 변수, nullable

  // initDatabase 메서드: 데이터베이스 초기화 및 생성
  Future<void> initDatabase() async {
    if (_database == null) { // _database가 null인 경우에만 실행
      var databasesPath = await getDatabasesPath(); // 데이터베이스 경로를 가져옴
      String path = join(databasesPath, 'memos.db'); // 'memos.db' 파일로 경로를 조합

      // 데이터베이스 열기 또는 생성
      _database = await openDatabase(path, version: 1,
          onCreate: (Database db, int version) async {
        // 데이터베이스를 처음 생성할 때 호출됨, 메모 테이블 생성
        await db.execute('''
          CREATE TABLE memos(
            id INTEGER PRIMARY KEY AUTOINCREMENT, 
            content TEXT,
            createAt TEXT
          )
        ''');
      });
    }
  }

  // getMemos 메서드: 모든 메모를 가져오는 메서드
  Future<List<MemoData>> getMemos() async {
    await initDatabase(); // 데이터베이스 초기화
    List<Map<String, dynamic>> maps = await _database!.query('memos'); // 'memos' 테이블의 모든 데이터를 조회

    // 조회한 데이터를 MemoData 객체 리스트로 변환
    return List.generate(maps.length, (index) {
      return MemoData(
          id: maps[index]['id'], // id 값
          content: maps[index]['content'], // content 값
          createAt: DateTime.parse(maps[index]['createAt'])); // createAt 값을 DateTime 객체로 변환
    });
  }

  // insertMemo 메서드: 새로운 메모를 데이터베이스에 추가
  Future<int> insertMemo(MemoData memo) async {
    await initDatabase(); // 데이터베이스 초기화
    return await _database!.insert('memos', memo.toMap()); // 'memos' 테이블에 메모 데이터 삽입
  }

  // deleteMemo 메서드: 특정 id를 가진 메모를 데이터베이스에서 삭제
  Future<void> deleteMemo(int id) async {
    await initDatabase(); // 데이터베이스 초기화
    await _database!.delete(
      'memos',
      where: 'id = ?', // id가 일치하는 레코드를 찾음
      whereArgs: [id], // 삭제할 메모의 id
    );
  }
}

앱 초기화 및 메모 관리

앱의 메인 페이지에서 DatabaseHelper를 사용하여 메모를 관리합니다. 사용자가 앱을 사용하여 메모를 생성하고, 조회하고, 삭제할 수 있도록 합니다

// MyMemoAppPage 위젯의 상태 클래스 정의
class _MyMemoAppPageState extends State<MyMemoAppPage> {
  DatabaseHelper dbHelper = DatabaseHelper(); // DatabaseHelper 인스턴스 생성
  late Future<List<MemoData>> memos; // 비동기적으로 메모 데이터를 불러올 Future 변수 선언

  @override
  void initState() {
    // initState 메서드: 위젯의 상태 객체가 생성될 때 호출됨
    super.initState(); // 부모 클래스의 initState 호출
    memos = dbHelper.getMemos(); // DatabaseHelper를 통해 메모 데이터를 비동기적으로 불러옴
  }
  // 이하 코드 생략...
}

메모 생성버튼

사용자가 버튼을 클릭하면 MemoInputPage 화면으로 이동합니다. 사용자가 MemoInputPage에서 메모 작성 후 돌아오면, then 콜백이 실행되며, 새로 작성된 메모가 있을 경우 (value != null), 상태 업데이트를 통해 새 메모를 데이터베이스에 추가하고 메모 목록을 갱신합니다.

// 아이콘 버튼 위젯: 사용자가 새 메모를 작성할 수 있게 하는 버튼
IconButton(
  onPressed: () {
    // 버튼 클릭 시, MemoInputPage로 화면 전환
    Navigator.push(
      context,
      MaterialPageRoute(builder: (context) => MemoInputPage()),
    ).then((value) {
      // MemoInputPage에서 돌아올 때 실행되는 콜백
      if (value != null) {
        // 사용자가 새 메모 입력 후 돌아왔을 경우
        setState(() {
          // 새로 입력된 메모를 데이터베이스에 추가하고, 메모 리스트를 다시 불러옴
          dbHelper.insertMemo(
              MemoData(content: value, createAt: DateTime.now()));
          memos = dbHelper.getMemos();
        });
      }
    });
  },
  style: IconButton.styleFrom(foregroundColor: Colors.white), // 버튼 스타일 지정
  icon: Icon(Icons.create), // 버튼 아이콘 지정
),

메모 삭제버튼

이 버튼을 클릭하면, 해당 메모의 id를 사용하여 DatabaseHelper 클래스의 deleteMemo 메소드를 호출하여 데이터베이스에서 메모를 삭제합니다.

// ListTile 위젯 반환: 각 메모를 나타내는 UI 구성요소
return ListTile(
  title: Text(memo.content), // 메모의 내용을 제목으로 표시
  subtitle: Text(DateFormat('yy/MM/dd').format(memo.createAt)), // 메모 생성 날짜를 부제목으로 표시, 'yy/MM/dd' 형식으로 날짜 포맷
  trailing: IconButton(
    icon: Icon(Icons.delete), // 삭제 아이콘 버튼
    onPressed: () async {
      // 아이콘 버튼 클릭 시 비동기로 실행되는 콜백 함수
      await dbHelper.deleteMemo(memo.id!); // dbHelper를 통해 해당 메모 삭제, '!'를 사용하여 null이 아님을 보증
      setState(() {
        // 메모 삭제 후 상태 업데이트
        memos = dbHelper.getMemos(); // 메모 리스트를 다시 불러와서 갱신, 삭제된 메모 반영
      });
    },
  ),
);

마무리

사용자가 메모를 추가하거나 삭제할 때마다, 메모 목록이 최신 상태로 유지되도록 합니다. 이렇게 함으로써 사용자는 자신이 작성한 메모를 쉽게 관리할 수 있습니다. 결론 Flutter에서 sqflite패키지를 활용해 데이터를 영구적으로 저장하고 관리하는 방법을 살펴보았습니다. 이를 통해 사용자가 앱을 종료한 후에도 데이터가 유지되는 메모장 앱을 만들 수 있습니다. Flutter와 이러한 패키지들을 사용함으로써, 개발자는 효율적으로 데이터를 관리하고, 사용자에게 더 나은 경험을 제공할 수 있습니다. Flutter로 앱 개발을 시작하는 분들에게 이 글이 도움이 되기를 바랍니다. 읽어주셔서 감사합니다.

전체 코드는 에서 확인할 수 있습니다.

Leave a Comment