Drift(이전 Moor)는 자체 DSL을 가진 SQLite 기반의 Flutter와 Dart용 리액티브 ORM입니다. 기존 ORM과 달리 Drift는 빌드 시점에 Dart 쿼리를 SQL로 컴파일하여 런타임 오류를 제거합니다. Drift Docs, 2024에 따르면, Drift는 수동 SQL 쿼리보다 40% 많은 코드를 생성하지만, 수동 SQL 작성을 완전히 제거하고 타입 안전한 Dart 구문으로 대체합니다.
중요 포인트
Drift는 Dart와 Flutter용 ORM으로, 이전에는 Moor라고 알려졌습니다. 2019년 Simon Binder가 개발하였으며, 그 이후 여러 메이저 버전을 거쳐왔습니다. Drift는 drift_dev와 build_runner를 사용하여 빌드 시에 Dart 쿼리를 SQL로 컴파일하여 완전한 타입 안전성을 제공하고 런타임에서 SQL 구문 오류를 제거합니다.
Floor와 달리, Drift는 쿼리 구축을 위해 자체 DSL(도메인 특화 언어)을 사용합니다 — 개발자는 Dart로 작성하고 생성기가 그것을 SQL로 번역합니다. 이를 통해 IDE가 구문을 확인하고, 테이블 필드를 자동 완성하며, 쿼리를 깨뜨리지 않고 데이터 모델을 리팩토링할 수 있습니다.
Drift(2024)에 따르면, 이 라이브러리는 8000개 이상의 Flutter 프로젝트에서 사용됩니다. 다음을 지원합니다: Android(sqflite), iOS(sqflite), Web(sqlite3 WASM), 데스크탑(sqlite3 네이티브 드라이버).
Moor는 버전 2.0(2022)에서 Drift로 개명되었습니다. 이유는 다른 프로젝트와의 이름 충돌과 고전 코드와의 거리둈입니다. API는 호환이 유지되었습니다: 마이그레이션하려면 import를 moor에서 drift로 바꾸고 의존성을 업데이트하면 됩니다.
Drift가 제공하는 기능: 데이터 변경 시 자동 업데이트되는 내장 Stream 쿼리, 롤백 가능한 트랜잭션 지원, rawQuery를 통한 사용자 정의 SQL 쿼리, 로직 캡슐화를 위한 DAO 패턴, 크로스 플랫폼 마이그레이션, 그리고 drift_riverpod과 drift_bloc 패키지를 통한 Riverpod 및 BLoC와의 연동.
Drift는 컴파일 시점에 코드 생성을 사용합니다. 개발자는 @DataClass 어노테이션이나 Table을 확장하는 Dart 클래스를 통해 테이블을 설명합니다. 생성기는 보조 클래스를 만듭니다: Companion(삽입/업데이트 시 null 가능 필드용), DriftDatabase(입구 점), 그리고 DAO 구현.
Drift는 SQLite 쿼리를 직접 실행하지 않습니다. 대신 개발자가 Dart로 작성합니다: select(tasks).where(tasks.priority.greaterThan(3)).build(). 생성기가 이를 SQL로 번역하고, 런타임에서 Drift는 준비된 SQL 쿼리를 SQLite로 보냅니다. 이는 Dart 구문의 편의성과 고유 SQL 성능을 결합합니다.
Drift는 두 가지 쿼리 모드를 지원합니다: DSL(권장) 및 고유 SQL. DSL 쿼리가 작성하기 더 안전합니다 — 컴파일러가 필드 이름, 타입 및 호환성을 확인합니다. 고유 SQL은 DSL이 다루지 못하는 복잡한 쿼리(윈도우 함수, 재귀 CTE, 특정 SQLite 확장)에 필요합니다.
Drift는 쿼리 작성 방법을 두 가지 제공합니다: Dart DSL(고유) 및 고유 SQL(복잡한 경우). 90% 시나리오에서 DSL이 더 좋습니다: 더 안전하고, 읽기 쉽고, 리팩토링을 지원합니다. 고유 SQL은 DSL이 필요한 구조를 다루지 못하는 경우에만 사용됩니다.
| 측면 | Drift DSL | Drift에서 고유 SQL |
|---|---|---|
| 타입 안전성 | 완전(컴파일 시) | 없음(런타임) |
| 자동 완성 | 예(IDE) | SQL 파일에서만 |
| 리팩토링 | 자동 | 수동 문자열 검색 |
| 복잡한 JOIN | 지원됨 | 완전한 자유 |
| 윈도우 함수 | 제한적 | 완전 지원 |
| 리액티브성 | 내장(Stream) | .watch() 통해 |
Drift DSL은 주요 작업 방식입니다. SELECT, INSERT, UPDATE, DELETE, WHERE, ORDER BY, LIMIT, JOIN 그리고 그룹화를 다룹니다. 모든 일반적인 CRUD 쿼리에서 DSL을 사용하세요: 더 빠르고, 안전하고, 변경 시 Stream을 자동 업데이트합니다.
Drift에서 고유 SQL은 다음에 필요합니다: 사용자 정의 SQLite 함수(FTS5, JSON1), EXISTS를 사용한 복잡한 서브쿼리, INSERT OR REPLACE, CASE를 사용한 대량 UPDATE, 그리고 성능이 중요하고 DSL이 최적의 실행 계획을 생성하지 못하는 쿼리. 고유 SQL은 drift_dev를 통한 타입 지원과 함께 .sql 파일에 작성할 수 있습니다.
Drift는 Table을 확장하거나 @DataClass 어노테이션을 사용하는 클래스를 사용합니다. 아래는 DSL, 고유 SQL, 그리고 리액티브 업데이트를 통한 쿼리가 있는 Task 모델의 완전한 예입니다. build_runner를 실행하면 생성된 모든 클래스가 준비됩니다.
Tasks 클래스는 Table을 확장하고 열을 정의합니다. 각 열은 Column<T> 타입의 식입니다. 매개변수: withDefault()는 기본값을 설정하고, autoIncrement()는 자동 증가를 설정합니다. 데이터베이스는 $DriftDatabase를 확장하는 추상 클래스입니다.
class Tasks extends Table {
IntColumn get id => integer().autoIncrement();
TextColumn get title => text().withDefault(const Constant(''))();
BoolColumn get isCompleted => boolean().withDefault(const Constant(false))();
IntColumn get priority => integer().withDefault(const Constant(0))();
}
@DriftDatabase(tables: [Tasks])
class AppDatabase extends $AppDatabase {
AppDatabase(QueryExecutor e) : super(e);
}
Drift는 테이블에 대한 메서드 into(tasks).insert(), select(tasks), update(tasks) 그리고 delete(tasks)를 생성합니다. 모든 작업은 Future를 반환합니다 — SQLite 작업은 비동기적입니다. 변경사항을 추적하려면 .get() 대신 .watch()를 사용하세요.
// 삽입
await into(tasks).insert(TasksCompanion.insert(
title: Value('식륜구매'),
priority: Value(3),
));
// 필터로 읽기
final highPriority = await (select(tasks)
..where((t) => t.priority.greaterThan(2))
..orderBy([(t) => OrderingTerm(expression: t.priority, mode: OrderingMode.desc)]))
.get();
// 리액티브 관찰
select(tasks).watch().listen((tasksList) {
// tasksList — List, 테이블이 변경될 때마다 업데이트됨
updateUi(tasksList);
});
복잡한 쿼리의 경우, Drift는 타입을 유지하면서 고유 SQL을 작성할 수 있습니다. customSelect 메서드는 쿼리 문자열을 받아 코드 생성기를 통해 타입화된 결과를 반환합니다. 이 접방식은 SQL의 유연성과 Drift의 타입 안전성을 결합합니다.
final result = await customSelect(
'SELECT title, COUNT(*) as cnt FROM tasks GROUP BY title',
readsFrom: { tasks },
).get();
for (final row in result) {
print('${row.readString("title")}: ${row.readInt("cnt")}');
}
Drift는 간단한 변경에 대한 자동 마이그레이션과 복잡한 변환에 대한 수동 마이그레이션을 모두 지원합니다. 데이터베이스 버전은 AppDatabase 생성자에서 설정됩니다. 버전이 일치하지 않으면 Drift는 모든 미루 마이그레이션을 순차적으로 적용합니다.
기본값이 있는 열을 추가하려면 Drift가 MigrationStrategy를 통해 자동으로 마이그레이션을 생성할 수 있습니다. 변경이 기존 데이터를 깨지 않는 경우(null 가능 필드 추가), 버전 확인과 ALTER TABLE 실행이 있는 beforeOpen을 사용할 수 있습니다.
복잡한 변경(테이블 이름 변경, 데이터 병합, 열 타입 변경)은 Drift에서 수동 SQL 마이그레이션이 필요합니다. 마이그레이션은 데이터베이스 클래스의 migrations 매개변수를 통해 지정됩니다. 각 마이그레이션은 from/to 번호와 SQL 쿼리가 있는 객체입니다.
Drift는 NativeDatabase.memory()를 통한 테스트 모드 실행을 지원합니다. 메모리 내 데이터베이스는 각 테스트 전에 처음부터 생성되고 테스트 후 삭제됩니다. 목업을 위해 목업된 QueryExecutor와 함께 mocktail 패키지를 사용하세요. Drift는 마이그레이션과 쿼리 검증을 통한 통합 테스트를 위한 DatabaseTestHelper도 제공합니다.
Drift는 @DriftAccessor 어노테이션이 있는 추상 클래스를 통해 DAO(데이터 액세스 객체)를 지원합니다. DAO는 하나 이상의 테이블에 대한 쿼리를 캡슐화하고 데이터베이스와 별도로 테스트할 수 있습니다. Database를 통한 직접 쿼리와 달리 DAO를 통해 애플리케이션의 다른 부분간에 쿼리 로직을 재사용할 수 있고 단위 테스트가 간소화됩니다.
@DriftDatabase(tables: [Tasks])
class AppDatabase extends $AppDatabase {
AppDatabase(QueryExecutor e) : super(e) {
migrations.add(Migration(1, 2, (m) async {
await m.addColumn(tasks, tasks.dueDate);
await m.createIndex(tasks.idxPriority);
}));
}
}
자주 묻는 질문
Drift는 SQL 문자열 대신 자체 DSL을 사용하여 완전한 타입 안전성과 IDE에서의 자동 완성을 제공합니다. Floor는 @Query 어노테이션에서 SQL 문자열을 사용합니다. Drift는 더 많은 플랫폼(web 포함)을 지원하고 Stream을 통한 내장 리액티브성이 있지만, Floor에서 Stream은 수동으로 선언해야 합니다.
네, Drift는 데이터 보존을 통한 마이그레이션을 지원합니다. 열을 추가하려면 Migration에서 addColumn을 사용하세요. 복잡한 변환(이름 변경, 병합)은 마이그레이션 내에서 고유 SQL을 작성하세요. 마이그레이션이 지정되지 않으면 스키마가 일치하지 않을 때 Drift가 데이터 손실과 함께 데이터베이스를 재생성합니다.
Drift는 build_runner와 drift_dev를 통한 코드 생성이 필요합니다. 생성 없이는 타입화된 쿼리를 만들 수 없습니다. 그러나 작은 프로젝트의 경우, Drift는 sqlparser를 지원합니다 — 자동 타입으로 수동 SQL 파일 작성이기는 하지만 여전히 생성 단계가 필요합니다.
Riverpod과의 연동에는 drift_riverpod 패키지를 사용하세요. Database, DAO 그리고 Stream 쿼리에 대한 프로바이더를 제공합니다. 예: final tasksProvider = databaseProvider.select((db) => db.select(db.tasks).watch()) — 데이터가 변경되면 UI가 자동으로 재건설됩니다.
Drift에는 내장 암호화가 없지만 SEE(SQLite Encryption Extension)를 사용하여 사용자 정의 sqlite3 라이브러리를 연결할 수 있습니다. 모바일 플랫폼의 경우 QueryExecutor로 sqflite_sqlcipher를 사용하세요 — Drift는 추상 QueryExecutor를 통해 모든 SQLite 구현과 작동합니다.
요약
턴키 방식의 모바일 애플리케이션을 개발해 드립니다
IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.