Flutter 로컬 데이터베이스
SQLite, Drift, Hive로 데이터 관리
학습 항목
이미지 로딩 중...
Flutter SQLite 데이터베이스 완벽 가이드
Flutter 앱에서 SQLite를 사용하여 로컬 데이터베이스를 구축하는 방법을 배웁니다. 기본적인 CRUD 작업부터 실무에서 바로 사용할 수 있는 패턴까지 단계별로 학습합니다.
들어가며
이 글에서는 Flutter SQLite 데이터베이스 완벽 가이드에 대해 상세히 알아보겠습니다. 총 12가지 주요 개념을 다루며, 각각의 개념에 대한 설명과 실제 코드 예제를 함께 제공합니다.
목차
- SQLite_패키지_설치
- 데이터베이스_헬퍼_클래스_생성
- 데이터베이스_초기화
- 테이블_생성_쿼리
- 모델_클래스_정의
- 데이터_삽입_CREATE
- 데이터_조회_READ
- 특정_데이터_조회
- 데이터_수정_UPDATE
- 데이터_삭제_DELETE
- 트랜잭션_사용하기
- 데이터베이스_닫기
1. SQLite_패키지_설치
개요
Flutter에서 SQLite를 사용하기 위해 sqflite와 path 패키지를 설치합니다. pubspec.yaml 파일에 의존성을 추가하는 것이 첫 단계입니다.
코드 예제
dependencies:
flutter:
sdk: flutter
sqflite: ^2.3.0
path: ^1.8.3
설명
sqflite는 Flutter용 SQLite 플러그인이며, path는 데이터베이스 파일 경로를 관리하는 데 사용됩니다. 패키지 추가 후 flutter pub get 명령어로 설치합니다.
2. 데이터베이스_헬퍼_클래스_생성
개요
데이터베이스 연결을 관리하는 싱글톤 패턴의 헬퍼 클래스를 만듭니다. 앱 전체에서 하나의 데이터베이스 인스턴스만 사용합니다.
코드 예제
class DatabaseHelper {
static final DatabaseHelper instance = DatabaseHelper._init();
static Database? _database;
DatabaseHelper._init();
Future<Database> get database async {
if (_database != null) return _database!;
_database = await _initDB('notes.db');
return _database!;
}
}
설명
싱글톤 패턴으로 구현하여 메모리 효율을 높이고, getter를 통해 데이터베이스가 없으면 초기화하고 있으면 기존 인스턴스를 반환합니다.
3. 데이터베이스_초기화
개요
데이터베이스 파일을 생성하고 테이블을 정의합니다. getDatabasesPath()로 앱의 데이터베이스 디렉토리 경로를 가져옵니다.
코드 예제
Future<Database> _initDB(String filePath) async {
final dbPath = await getDatabasesPath();
final path = join(dbPath, filePath);
return await openDatabase(
path,
version: 1,
onCreate: _createDB,
);
}
설명
openDatabase()는 데이터베이스를 열거나 생성하며, version을 통해 스키마 버전을 관리하고, onCreate 콜백으로 테이블을 생성합니다.
4. 테이블_생성_쿼리
개요
SQL CREATE TABLE 문으로 데이터를 저장할 테이블 구조를 정의합니다. 각 컬럼의 타입과 제약조건을 지정합니다.
코드 예제
Future _createDB(Database db, int version) async {
await db.execute('''
CREATE TABLE notes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
content TEXT NOT NULL,
createdAt TEXT NOT NULL
)
''');
}
설명
id는 자동 증가하는 기본 키이며, NOT NULL 제약으로 필수 필드를 지정합니다. 날짜는 TEXT 타입으로 ISO 8601 형식의 문자열로 저장합니다.
5. 모델_클래스_정의
개요
데이터베이스 테이블과 매핑되는 Dart 클래스를 만듭니다. JSON 변환 메서드로 데이터를 쉽게 다룰 수 있습니다.
코드 예제
class Note {
final int? id;
final String title;
final String content;
final DateTime createdAt;
Note({this.id, required this.title,
required this.content, required this.createdAt});
Map<String, dynamic> toMap() => {
'id': id, 'title': title,
'content': content, 'createdAt': createdAt.toIso8601String()
};
}
설명
toMap() 메서드는 객체를 Map으로 변환하여 데이터베이스에 저장할 수 있게 하고, DateTime은 ISO 8601 문자열로 변환합니다.
6. 데이터_삽입_CREATE
개요
insert() 메서드로 새로운 데이터를 테이블에 추가합니다. conflictAlgorithm으로 중복 처리 방식을 지정할 수 있습니다.
코드 예제
Future<int> createNote(Note note) async {
final db = await database;
return await db.insert(
'notes',
note.toMap(),
conflictAlgorithm: ConflictAlgorithm.replace,
);
}
설명
insert()는 삽입된 행의 id를 반환합니다. ConflictAlgorithm.replace는 기본 키가 중복되면 기존 데이터를 덮어씁니다.
7. 데이터_조회_READ
개요
query() 메서드로 테이블의 데이터를 조회합니다. where 절로 조건을 지정하거나 전체 데이터를 가져올 수 있습니다.
코드 예제
Future<List<Note>> readAllNotes() async {
final db = await database;
final result = await db.query('notes',
orderBy: 'createdAt DESC');
return result.map((json) => Note(
id: json['id'] as int,
title: json['title'] as String,
content: json['content'] as String,
createdAt: DateTime.parse(json['createdAt'] as String)
)).toList();
}
설명
query()는 Map 리스트를 반환하며, orderBy로 정렬 순서를 지정합니다. map()으로 각 Map을 Note 객체로 변환합니다.
8. 특정_데이터_조회
개요
where와 whereArgs를 사용하여 조건에 맞는 특정 데이터만 조회합니다. SQL 인젝션 방지를 위해 ?를 사용합니다.
코드 예제
Future<Note?> readNote(int id) async {
final db = await database;
final maps = await db.query(
'notes',
where: 'id = ?',
whereArgs: [id],
);
if (maps.isNotEmpty) {
return Note(id: maps.first['id'] as int, ...);
}
return null;
}
설명
where 절의 ?는 플레이스홀더로, whereArgs의 값이 순서대로 대입됩니다. 이 방식으로 SQL 인젝션 공격을 방지합니다.
9. 데이터_수정_UPDATE
개요
update() 메서드로 기존 데이터를 수정합니다. where 조건으로 수정할 행을 지정하고, Map으로 새 값을 전달합니다.
코드 예제
Future<int> updateNote(Note note) async {
final db = await database;
return await db.update(
'notes',
note.toMap(),
where: 'id = ?',
whereArgs: [note.id],
);
}
설명
update()는 수정된 행의 개수를 반환합니다. where 조건이 없으면 모든 행이 수정되므로 주의해야 합니다.
10. 데이터_삭제_DELETE
개요
delete() 메서드로 데이터를 삭제합니다. where 조건으로 삭제할 행을 지정하며, 조건 없이 호출하면 모든 데이터가 삭제됩니다.
코드 예제
Future<int> deleteNote(int id) async {
final db = await database;
return await db.delete(
'notes',
where: 'id = ?',
whereArgs: [id],
);
}
설명
delete()는 삭제된 행의 개수를 반환합니다. 여러 조건을 조합할 때는 where 절에 AND나 OR를 사용할 수 있습니다.
11. 트랜잭션_사용하기
개요
여러 데이터베이스 작업을 하나의 트랜잭션으로 묶어 원자성을 보장합니다. 하나라도 실패하면 모든 작업이 롤백됩니다.
코드 예제
Future<void> batchInsert(List<Note> notes) async {
final db = await database;
await db.transaction((txn) async {
for (var note in notes) {
await txn.insert('notes', note.toMap());
}
});
}
설명
transaction() 블록 내의 모든 작업은 하나의 단위로 처리되어, 중간에 에러가 발생하면 자동으로 롤백되어 데이터 무결성을 유지합니다.
12. 데이터베이스_닫기
개요
앱 종료 시 데이터베이스 연결을 안전하게 닫습니다. 메모리 누수를 방지하고 리소스를 정리합니다.
코드 예제
Future<void> close() async {
final db = await database;
await db.close();
_database = null;
}
설명
close()는 데이터베이스 연결을 종료하며, _database를 null로 설정하여 다음 접근 시 새로운 연결이 생성되도록 합니다.
마치며
이번 글에서는 Flutter SQLite 데이터베이스 완벽 가이드에 대해 알아보았습니다. 총 12가지 개념을 다루었으며, 각각의 사용법과 예제를 살펴보았습니다.
관련 태그
#Flutter #SQLite #sqflite #Database #LocalStorage