| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | |||
| 5 | 6 | 7 | 8 | 9 | 10 | 11 |
| 12 | 13 | 14 | 15 | 16 | 17 | 18 |
| 19 | 20 | 21 | 22 | 23 | 24 | 25 |
| 26 | 27 | 28 | 29 | 30 | 31 |
- fetch
- flutter
- StreamProvider
- Happy moment
- FutureProvider
- StateNotifierProvider
- StateProvider
- 정리
- flutter_secure_storage
- 데이터
- riverpid
- Flutter 상태관리
- 가공
- Today
- Total
This Week's Record
Flutter에서 SQL사용하기 (feat. Drift) 본문
STEP1 패키지 설치
0. 작업 들어가기전에 패키지부터 설치해주자!
아래 4개는 dependencies에
drift
sqlite3_flutter_libs
path_provider
path
아래 2개는 dev_dependencies에
drift_dev
build_runner
STEP2 model 작성
1. 먼저 model폴더를 만들고 그 안에 사용할 table들을 정의해준다.
import 'package:drift/drift.dart';
class Schedule extends Table {
// PRIMARY KEY
IntColumn get id => integer().autoIncrement()(); //중복이 안생기도록 autoIncrement
// 내용
TextColumn get content => text()();
// 일정 날짜
DateTimeColumn get date => dateTime()();
// 시작 시간
IntColumn get startTime => integer()();
// 종료 시간
IntColumn get endTime => integer()();
// Category Color Table ID
IntColumn get colorId => integer()();
// 생성날짜
DateTimeColumn get createdAt => dateTime().clientDefault(
() => DateTime.now(), // 자동으로 now로 값이 저장됨!그리고 이건 default값이므로 값을 강제로 넣어주면 그 값으로 대체!
)();
}
STEP3 데이터베이스 코드 작성
2. drift로 데이터베이스에 연결할 코드를 작성하기위해, database > drift_database.dart 파일을 만들고
아래 'drift_database.g.dart'라는 파일을 part키워드를 이용해 불러온다.
* part는 import랑 달리 private 값들을 불러올 수 있다.
참고로 아직 'drift_database.g.dart'파일은 존재하지 않지만, 생성될 예정이다!
part 'drift_database.g.dart'; // 현재파일명.g.dart
4. DriftDatabase 데코레이터를 이용해서 어떤 클래스들을 테이블로 쓸지 정해준다. 아래와 같이!
@DriftDatabase(
tables: [
Schedule,
CategoryColor,
],
)
5. 실제로 데이터베이스를 형성할 클래스를 LocalDatabase라는 이름으로 만들어주고 _$LocalDatabase 클래스를 상속받도록 하기.
* 나중에 'drift_database.g.dart'가 생성될때 클래스 이름만 보고서 _$LocalDatabase라는 클래스를 만듦.
그래서 이 데이터 베이스에 관련된 모든 기능들을 다 사용할 수 있음! ㅎ_ㅎ (drift가 자동으로 그 기능들을 다 만들어주는거임)
class LocalDatabase extends _$LocalDatabase {
//여기서 _$LocalDatabase는 나중에 drift에서 만드는 'drift_database.g.dart';파일안에 있을 거임. 그리고 프라이빗인데 쓸 수 있는 이유는 part로 불러와서.
LocalDatabase() : super(_openConnection());
}
6. LocalDatabase에서 로컬데이터베이스를 만드는 작업을 drift가 다 해주지만, 우리가 제공해줘야하는게 하나 있음.
super값에도 넣었지만, 그건 바로 _openConnection클래스!
_openConnection의 내용을 살펴보면 OS에서 앱에 관한 파일을 관리할 특정 경로를 가져와서 그 파일로 데이터 베이스를 만들라고 알려주는 클래스이다.
LazyDatabase _openConnection() {
// 실제 데이터베이스 파일을 어떤 위치에 생성시킬건지 설정 - SQL은 보조기억장치에다가 저장하도록 연결해줌띠 - 보조기억장치 어뒤?인지 명시
return LazyDatabase(() async {
final dbFolder =
await getApplicationDocumentsDirectory(); // 앱설치시 os에서 앱별로 각각 사용할 수 있는 하드드라이브의 특정위치를 지정해둠. 그 배정받은 위치를 가져오는 방법!
final file = File(p.join(
dbFolder.path, 'db.sqlite')); // 실제 경로에다가 'db.sqlite' 파일 생성하고 file에 저장
return NativeDatabase(file);
});
}
아래는 drift_database.dart 파일 전체코드!
// 'private'값들은 불러올 수 없다.
import 'dart:io';
import 'package:calender_scheduler/models/category_color.dart';
import 'package:calender_scheduler/models/schedule.dart';
import 'package:drift/drift.dart';
import 'package:drift/native.dart';
import 'package:path/path.dart' as p;
import 'package:path_provider/path_provider.dart';
// private 값 까지 불러올 수 있다.
part 'drift_database.g.dart';
@DriftDatabase(
tables: [
Schedule,
CategoryColor,
],
)
class LocalDatabase extends _$LocalDatabase {
//여기서 _$LocalDatabase는 나중에 drift에서 만드는 'drift_database.g.dart';파일안에 있을 거임. 그리고 프라이빗인데 쓸 수 있는 이유는 part로 불러와서.
LocalDatabase() : super(_openConnection());
}
LazyDatabase _openConnection() {
// 실제 데이터베이스 파일을 어떤 위치에 생성시킬건지 설정 - SQL은 보조기억장치에다가 저장하도록 연결해줌띠 - 보조기억장치 어뒤?인지 명시
return LazyDatabase(() async {
final dbFolder =
await getApplicationDocumentsDirectory(); // 앱설치시 os에서 앱별로 각각 사용할 수 있는 하드드라이브의 특정위치를 지정해둠. 그 배정받은 위치를 가져오는 방법!
final file = File(p.join(
dbFolder.path, 'db.sqlite')); // 실제 경로에다가 'db.sqlite' 파일 생성하고 file에 저장
return NativeDatabase(file);
});
}
7. 터미널에 아래 내용을 실행시킨다.
이 코드는 drift에 한정된 코드가 아니라, 제너레이션을 해주는 모든 코드에 적용됨.
flutter pub run build_runner build
그럼, 터미널에 코드들이 와랄라 뜨다가 작업이 끝나면 아래와 같이 drfit_database.g.dart 라는 파일이 생김.
*이때 안생기면 기다려보거나 다시 켜보시길. 로드가 안 된 것 일 수도 있기 때문

8. LocalDatabase에 override missing 에러가 뜨는데, schemaVersion을 오버라이딩해줘야함.
여기서 shcemaVersion은 처음에는 1을 넣어주면 되는데, 데이터베이스의 구조가 바뀔때마다 즉 table이 바뀔때마다 버전을 올려줘야함
class LocalDatabase extends _$LocalDatabase {
//여기서 _$LocalDatabase는 나중에 drift에서 만드는 'drift_database.g.dart';파일안에 있을 거임. 그리고 프라이빗인데 쓸 수 있는 이유는 part로 불러와서.
LocalDatabase() : super(_openConnection());
@override
int get schemaVersion => 1; // 데이터베이스에 설정한 상태들의 버전. 데이터베이스의 구조(table)가 바뀔때마다 버전 올려줘야함.
}
------- 이제 쿼리를 짤 준비 완료-! ------
9. 각각의 데이터베이스에 insert할 수 있는 쿼리를 짜자.
LocalDatabase 클래스 안에 코드를 짜면 되는데, 아래를 참고!
*사용된 ScheduleCompanion, schedule 모두 _$LocalDatabase안에 생성되어 있는 값임
*insert는 자동으로 primary key값을 int형태로 돌려줌!
class LocalDatabase extends _$LocalDatabase {
//여기서 _$LocalDatabase는 나중에 drift에서 만드는 'drift_database.g.dart';파일안에 있을 거임. 그리고 프라이빗인데 쓸 수 있는 이유는 part로 불러와서.
LocalDatabase() : super(_openConnection());
Future<int> createSchedule(ScheduleCompanion data) => into(schedule).insert(data);
//schedule 또한 _$LocalDatabase안에 생성되어 있는 값임, 그리고 insert는 자동으로 primary key값을 int형태로 돌려줌!
@override
int get schemaVersion =>
1; // 데이터베이스에 설정한 상태들의 버전. 데이터베이스의 구조(table)가 바뀔때마다 버전 올려줘야함.
}
10. 그럼 select하는 코드도 짜보자.
select는 값을 불러오는 것이다 보니 1.Stream으로 가져올수도있고(메모리 적게씀) 2.Future로 가져올수도 있음(한꺼번에 값을 다 받을 수 있음)

class LocalDatabase extends _$LocalDatabase {
LocalDatabase() : super(_openConnection());
...생략...
Future<List<CategoryColorData>> getCategoryColors() =>
select(categoryColor).get();
@override
int get schemaVersion => 1;
}
** 여기보면 나는 CategoryColorData 로 해뒀지만 많은 예시에서 CategoryColor로 적혀있을 것이다. (기존에 우리가 table만든 클래스)
하지만 그 데이터타입은 기존에 우리가 만들어둔 CategoryColor 클래스가 아니라 _$LocalDatabase안에 만들어진 클래스를 리스트타입으로 내보내는 것이므로, 만약 에러가 뜨고 끝에 Data를 붙여야한다고 하면 바꾸고 진행해보시라!
자자 그럼 SQL 코드짜기는 완료!
STEP4 데이터베이스 main 함수에 적용
11. main함수에 데이터베이스 선언해주기 -> 이제 database. 하면 그 안에 있던 함수들 불러올 수 있다!
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// 위의 함수는 플러터 프레임워크가 준비가 됐는지 확인하는 함수.
// runApp이 실행되면 자동으로 실행되는데, initializeDateFormatting같이 runApp가 실행되기 전에 실행할 코드가 있으면 준비됐는지 확인해봐야한다.
await initializeDateFormatting();
// main에서도 async가 된다.. 그리고 이렇게 초기화하는 함수도 미리 작동시켜놓을 수 있담
// 📍여기!!
final database = LocalDatabase();
runApp(
MaterialApp(
theme: ThemeData(
fontFamily: 'NotoSans',
),
home: HomeScreen(),
),
);
}
12. 데이터 불러오는 예시
*값은 모두 dirft 패키지에서 가져온 Value에 감싸야한다. ex) hexCode: Value(hexCode),
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await initializeDateFormatting();
final database = LocalDatabase();
// 📍여기!
final colors = await database.getCategoryColors();
if (colors.isEmpty) {
for (String hexCode in DEFAULT_COLORS) {
await database.createCategoryColor(
CategoryColorCompanion(
hexCode: Value(hexCode),
),
);
}
}
runApp(
MaterialApp(
theme: ThemeData(
fontFamily: 'NotoSans',
),
home: HomeScreen(),
),
);
}