본문 바로가기
WIKI 기술 지식 베이스

libSQL Flutter / Dart 레퍼런스

원문 보기 위키 갱신

libSQL Flutter/Dart 패키지는 Turso, libSQL을 다루는 데 필요한 모든 기능을 담고 있어요. libSQL Rust 크레이트와 flutter_rust_bridge 패키지 위에 만들어져서 Rust와 Dart 사이의 통신이 매끄럽게 이뤄지고, 덕분에 모든 기능을 문제없이 활용할 수 있어요.

출처: 문서

본문

프로젝트에 패키지 추가하기

flutter pub add libsql_dart

또는 프로젝트의 pubspec.yaml에 직접 추가해도 돼요.

libsql_dart:

초기화

LibsqlClient 생성자를 호출해 데이터베이스 클라이언트를 만들어요. 다양한 구성을 지원해서 인메모리 데이터베이스, 로컬 sqlite 파일, 원격 Turso/libSQL 데이터베이스, 임베디드 레플리카 어디든 연결할 수 있어요.

인메모리 데이터베이스

영속성이 필요 없는 경우라면 libSQL의 인메모리 데이터베이스 연결을 활용할 수 있어요.

final client = LibsqlClient(":memory:");

로컬 개발

SQLite 파일을 사용해 로컬에서 작업하려면 경로를 LibsqlClient에 전달하면 돼요.

final dir = await getApplicationCacheDirectory();
final path = '${dir.path}/local.db';

final client = LibsqlClient(path);

원격 연결

Turso 데이터베이스 URL을 전달하면 원격 데이터베이스를 사용할 수 있어요.

final client = LibsqlClient('<TURSO_OR_LIBSQL_URL>')
	..authToken = '<TOKEN>';

임베디드 레플리카

Turso 데이터베이스 URL을 syncUrl에 전달하면 임베디드 레플리카를 사용할 수 있어요.

final dir = await getApplicationCacheDirectory();
final path = '${dir.path}/local.db';

final client = LibsqlClient(path)
	..authToken = '<TOKEN>'
	..syncUrl = '<TURSO_OR_LIBSQL_URL>'
	..readYourWrites = true;

Connect

await client.connect();

Manual Sync (수동 동기화)

sync() 함수를 쓰면 로컬 데이터베이스를 원격 데이터베이스와 직접 동기화할 수 있어요.

await client.sync();

Periodic Sync (주기 동기화)

클라이언트를 만들 때 syncIntervalSeconds 속성을 설정하면 일정 간격으로 자동 동기화돼요.

final dir = await getApplicationCacheDirectory();
final path = '${dir.path}/local.db';

final client = LibsqlClient(path)
	..authToken = '<TOKEN>'
	..syncUrl = '<TURSO_OR_LIBSQL_URL>'
	..syncIntervalSeconds = 5
	..readYourWrites = true;

저장 시 암호화 (Encryption at rest)

SQLite 파일에 암호화를 켜려면 encryptionKey를 전달해 주세요.

final dir = await getApplicationCacheDirectory();
final path = '${dir.path}/local.db';

final client = LibsqlClient(path)..encryptionKey = '<KEY>';

암호화된 데이터베이스는 일반 SQLite 데이터베이스처럼 읽을 수 없는 원시 데이터로 보여요. 모든 작업에 libSQL 클라이언트를 사용해야 해요 — 자세히 알아보기.

Execute

영향을 받은 행 수를 반환해요.

await client.execute("create table if not exists customers (id integer primary key, name text);");

Query

행을 List<Map<String, dynamic>> 형태로 반환해요. SELECT 쿼리가 아니면 빈 리스트를 반환해요.

await client.query("insert into customers(name) values ('John Doe')");

print(await client.query("select * from customers"));

플레이스홀더

libSQL은 SQL 문 안에서 위치 기반(positional)과 이름 기반(named) 플레이스홀더를 모두 지원해요.

final statement = await client
	.prepare("select * from customers where id = ?");

await statement.query(positional: [1])
final statement = await client
	.prepare("select * from customers where id = :id");

await statement.query(named: {"id": 1})

참고: libSQL은 SQLite와 동일한 이름 기반 플레이스홀더 문자 :, @, $를 지원해요.

배치 트랜잭션 (Batch Transactions)

배치는 여러 SQL 문을 묶어서 암시적 트랜잭션 안에서 순차 실행하는 방식이에요. 트랜잭션 처리는 백엔드가 담당해서, 성공하면 모든 변경 사항이 커밋되고 실패하면 전체 롤백되어 아무것도 수정되지 않아요.

await client.batch("""insert into customers (name) values ('Jane Doe'); insert into customers (name) values ('Jake Doe');""");

트랜잭션 모드 (Transaction Modes)

Mode SQLite command Description
LibsqlTransactionBehavior.immediate BEGIN IMMEDIATE 트랜잭션 안에서 데이터를 읽고 쓰는 문을 실행할 수 있어요. 레플리카에서 실행된 쓰기 트랜잭션은 기본(primary) 인스턴스로 전달되며 병렬로 동작할 수 없어요.
LibsqlTransactionBehavior.readOnly BEGIN TRANSACTION READONLY 트랜잭션 안에서 데이터를 읽는 문(select)만 실행할 수 있어요. 읽기 트랜잭션은 레플리카에서 가능하고 다른 읽기 트랜잭션과 병렬로 동작할 수 있어요.
LibsqlTransactionBehavior.deferred_ BEGIN DEFERRED 트랜잭션이 읽기 모드로 시작하고, 쓰기 문이 실행되는 순간 쓰기 모드로 바뀌어요. 기본 인스턴스에서 쓰기 트랜잭션이 진행 중이면 이 모드 전환은 실패할 수 있어요.

인터랙티브 트랜잭션 (Interactive Transactions)

SQLite의 인터랙티브 트랜잭션은 트랜잭션 범위 안에서 일련의 읽기·쓰기 작업이 일관성을 유지하도록 보장해요. 커밋할지 롤백할지 시점을 직접 제어할 수 있고, 다른 클라이언트 활동으로부터 격리돼요.

Method Description
execute() 트랜잭션 컨텍스트 안에서 실행된다는 점만 빼면 execute()와 같아요
query() 트랜잭션 컨텍스트 안에서 실행된다는 점만 빼면 query()와 같아요
commit() 트랜잭션 안의 모든 쓰기 문을 커밋해요
rollback() 트랜잭션 전체를 롤백해요
final tx = await client.transaction();

await tx
	.execute("update customers set name = 'John Noe' where id = 1");
await tx
	.execute("update customers set name = 'Jane Noe' where id = 2");
print(await tx
	.query("select * from customers where id = ?", positional: [1]));

await tx.commit();

경고: libSQL의 인터랙티브 트랜잭션은 커밋되거나 롤백될 때까지 데이터베이스에 쓰기 잠금을 걸어요. 타임아웃은 5초예요. 지연 시간이 길거나 사용량이 많은 데이터베이스에서는 성능에 영향을 줄 수 있어요.

ATTACH

ATTACH 구문을 사용하면 현재 연결에 여러 데이터베이스를 붙일 수 있어요.

final tx = await client.transaction(behavior: LibsqlTransactionBehavior.readOnly);

await tx.execute("ATTACH \"<database-id>\" AS attached");

print(await tx.execute("SELECT * FROM attached.customers"));

await tx.commit();

참고: ATTACH 허용을 켜고 데이터베이스를 붙일 권한이 있는 토큰을 만들어야 해요 — 자세히 알아보기

더 알아보기 (Learn more)