플러터에서 드리프트 쓰는 법
메이플 스타일러는 메이플스토리에 존재하는 코디 아이템 리스트를 보여주어야 한다.
코디템 리스트를 생성하는 방식을 새로운 방식으로 마이그레이션했다.
기존 방식은 네트워크가 빨라도 2초 이상 걸렸다. response가 도착하는 시간 자체도 길었지만, 이후에 response에서 5만개 정도의 객체를 훑으면서 보기 좋게 가공하는 과정이 너무 오래걸렸다.
가벼운 SQLite
를 쓰기로 했고, SQLite를 Flutter에서 사용하도록 도와주는 Drift
플러그인을 사용하기로 했다.
Drift는 Flutter Favorite에 소속된 패키지로, 기존의 SQLite에서 사용하는 SQL 문법을 Dart 문법으로 작성할 수 있게 도와주는 플러그인이다.
이제부터 Drift를 사용해 DB를 직접 만들어보자. 가장 대표적인 예시인 TO DO LIST의 테이블을 생성하고 데이터를 추가해보자. Drift로 어떻게 코디템 db파일을 만들었는지는 나중에 포스팅하겠다.
pubspec.yaml
파일에 다음과 같이 패키지를 추가한다.
dependencies:
drift: ^2.13.0
sqlite3_flutter_libs: ^0.5.0
path_provider: ^2.0.0
path: ^1.8.3
dev_dependencies:
drift_dev: ^2.13.0
build_runner: ^2.4.6
dependencies
drift
: drift 데이터베이스에 접근하는 데 사용되는 API를 정의하는 핵심 패키지sqlite3_flutter_libs
: 최신 sqlite3 버전을 제공하는 패키지path_provider
, path
: 데이터베이스를 저장할 위치를 찾아주는 패키지dev_dependencies
drift_dev
: 테이블의 쿼리 코드를 생성하는 패키지build_runner
: code generation에 필요한 패키지database.dart
파일을 생성하여 데이터베이스 테이블과 접근을 위한 클래스를 작성하자 (파일명과 위치는 자유다. 그리고 에러는 일단 무시하자)
import 'package:drift/drift.dart';
part 'database.g.dart';
class TodoItems extends Table {
IntColumn get id => integer().autoIncrement()();
TextColumn get title => text().withLength(max: 32)();
TextColumn get content => text().named('body')();
IntColumn get category => integer().nullable()();
}
(tables: [TodoItems])
class AppDatabase extends _$AppDatabase {
// 일단은 빈칸으로...
}
TodoItems
라는 이름의 데이터 테이블을 작성했다.
id
: primary key로써, 1부터 오름차순으로 번호를 매긴다.title
, : 계획 제목이다. 최대 32자까지 혀용한다.content
: 계획 내용이다. named()
메소드를 통해 column 이름을 "body"라고 지정해준다.category
: 카테고리. null값을 허용한다.이제 drift generator를 실행하자. database.g.dart
파일이 생성되어 필요한 코드가 한 번에 자동으로 작성되고 part
부분의 에러가 해결될 것이다. 터미널창이나 콘솔창에 다음과 같이 입력하자.
dart run build_runner build
build를 마치면 database.g.dart
파일이 생성되었을 것이고, database.dart
파일엔 오버라이드와 생성자 관련 오류가 등장했을 것이다.
아까 비워뒀던 AppDatabase 클래스 부분에 코드를 채워넣고, _openConnection()
메소드를 작성하자. AppDatabase의 생성자는 _openConnection()
메소드를 호출하여 데이터 테이블과의 연결을 시도한다. 추가로 SQLite 데이터베이스를 열기 위한 라이브러리도 import한다.
(tables: [TodoItems])
class AppDatabase extends _$AppDatabase {
// 일단은 빈칸으로...
}
↓↓↓↓↓
// SQLite 데이터베이스를 열기 위해 필요한 라이브러리 import
import 'dart:io';
import 'package:drift/native.dart';
import 'package:path_provider/path_provider.dart';
import 'package:path/path.dart' as p;
(tables: [TodoItems])
class AppDatabase extends _$AppDatabase {
AppDatabase() : super(_openConnection());
int get schemaVersion => 1;
}
LazyDatabase _openConnection() {
// the LazyDatabase util lets us find the right location for the file async.
return LazyDatabase(() async {
// put the database file, called db.sqlite here, into the documents folder
// for your app.
final dbFolder = await getApplicationDocumentsDirectory();
final file = File(p.join(dbFolder.path, 'db.sqlite'));
return NativeDatabase.createInBackground(file);
});
}
main 함수를 다음과 같이 작성해보자. DB 객체를 생성하고 TodoItems
테이블에 원하는 데이터를 삽입한 뒤, 다시 읽어보자. build_runner
로 코드를 생성했기 때문에 SQL CRUD
(Create, Read, Update, Delete) 는 이미 구현이 되어있다.
import 'package:practice_db/database.dart';
import 'package:flutter/material.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
final database = AppDatabase();
await database.into(database.todoItems).insert(TodoItemsCompanion.insert(
title: 'Flutter 공부하기',
content: 'Flutter에서 drift로 sqlite 다루는 방법 공부하기',
));
await database.into(database.todoItems).insert(TodoItemsCompanion.insert(
title: '우유 사기',
content: '엄마가 부탁한 저지방 우유 심부름하기',
));
await database.into(database.todoItems).insert(TodoItemsCompanion.insert(
title: '메이플 보스 잡기',
content: '주간 보스 초기화 되기 전에 스우, 데미안 잡기',
));
List<TodoItem> allItems = await database.select(database.todoItems).get();
print('items in database:');
for (int i = 0; i < allItems.length; i++) {
print('${i + 1}. ${allItems[i].title}\n${allItems[i].content}\n');
}
}
main 함수 실행 결과는 다음과 같다.
flutter: items in database:
flutter: 1. Flutter 공부하기
Flutter에서 drift로 sqlite 다루는 방법 공부하기
flutter: 2. 우유 사기
엄마가 부탁한 저지방 우유 심부름하기
flutter: 3. 메이플 보스 잡기
주간 보스 초기화 되기 전에 스우, 데미안 잡기
데이터가 잘 삽입되었다. 이제 main 함수를 수정해서 데이터를 삭제해보자. where
구문을 사용해서 title column이 'Flutter 공부하기' 이면 해당 데이터를 삭제한다.
import 'package:practice_db/database.dart';
import 'package:flutter/material.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
final database = AppDatabase();
// await database.into(database.todoItems).insert(TodoItemsCompanion.insert(
// title: 'Flutter 공부하기',
// content: 'Flutter에서 drift로 sqlite 다루는 방법 공부하기',
// ));
// await database.into(database.todoItems).insert(TodoItemsCompanion.insert(
// title: '우유 사기',
// content: '엄마가 부탁한 저지방 우유 심부름하기',
// ));
// await database.into(database.todoItems).insert(TodoItemsCompanion.insert(
// title: '메이플 보스 잡기',
// content: '주간 보스 초기화 되기 전에 스우, 데미안 잡기',
// ));
await (database.delete(database.todoItems)
..where((item) => item.title.equals('Flutter 공부하기')))
.go();
List<TodoItem> allItems = await database.select(database.todoItems).get();
print('items in database:');
for (int i = 0; i < allItems.length; i++) {
print('${i + 1}. ${allItems[i].title}\n${allItems[i].content}\n');
}
}
flutter: items in database:
flutter: 1. 우유 사기
엄마가 부탁한 저지방 우유 심부름하기
flutter: 2. 메이플 보스 잡기
주간 보스 초기화 되기 전에 스우, 데미안 잡기