【로컬DB】Isar의 클래스 정의 및 데이터 조작 방법 소개!!

이전에 Isar에 대한 기초적인 기사를 게시했습니다.

이번에는 더욱 자세한 Isar의 클래스 정의 및 데이터 조작 방법에 대해 소개합니다.
개발에 참고가 되길 바랍니다!

준비

Isar를 사용하려면 다음과 같은 준비가 필요합니다.

pubspec.yaml 설정

// 다음 패키지를 추가하세요.
dependencies:
  isar:
  isar_flutter_libs:

dev_dependencies:
  build_runner:
  isar_generator:

Isar 인스턴스 생성

import 'package:flutter/material.dart';
import 'package:isar/isar.dart';
import 'package:path_provider/path_provider.dart';

// Riverpod을 사용할 경우
import 'package:flutter_riverpod/flutter_riverpod.dart';
/// 직접 만든 Isar용 프로바이더의 저장 위치
import 'package:myApp/email_repository.dart';


// main 내에서 생성
Future<void> main() async {
  // 로컬DB의 저장 위치 지정
  final dir = await getApplicationSupportDirectory();

  // Isar 인스턴스 생성
  final isar = await Isar.open([EmailSchema], directory: dir.path);

  // Isar 인스턴스 생성
  runApp(ProviderScope(
    overrides: [
      emailIsarProvider.overrideWithValue(isar),
    ],
    child: const MyApp(),
  ));


// 참고) 여러 컬렉션을 사용할 경우
Future<void> main() async {
  // 로컬DB의 저장 위치 지정
  final dir = await getApplicationSupportDirectory();

  // Isar 인스턴스 생성
  final isar = await Isar.open([UserSchema, EmailSchema], directory: dir.path);

  // Isar 인스턴스 생성
  runApp(ProviderScope(
    overrides: [
      userIsarProvider.overrideWithValue(isar),
      emailIsarProvider.overrideWithValue(isar),
    ],
    child: const MyApp(),
  ));
}

컬렉션 정의 방법

NoSQL에서는 컬렉션과 도큐먼트로 데이터가 관리됩니다.
관계형 데이터베이스와 비교하면 다음과 같은 관계입니다.

 테이블 : 컬렉션
 레코드 : 도큐먼트

Isar의 컬렉션을 정의하려면 데이터 구조를 클래스로 정의해야 합니다.

기본적인 컬렉션 정의

Isar에서 데이터 클래스를 정의하려면 @collection 애노테이션을 사용합니다.
다음은 Email이라는 이름의 컬렉션을 정의하는 예입니다.

@collection
class Email {
  Email {
    Id? id;
    required title;
    required updatedAt;
  }
  Id? id; 
  final String title;
  @Index()  // "@Index()"를 사용하면 그 아래 필드의 인덱스가 생성됩니다
  final DateTime updatedAt;
}

이 클래스에는 id, title, updatedAt이라는 세 가지 필드가 있습니다.
id 필드는 데이터베이스 내에서 각 항목을 고유하게 식별하는 데 사용됩니다.

id는 Isar에서 자동으로 번호를 부여합니다.
IsarDB에 title과 updatedAt을 추가하면 id가 자동으로 부여됩니다.

실제로 저장되는 데이터 이미지:

{
  "id": 1234, // 자동으로 부여된 id
  "title": "How to use Isar!!"
  "updatedAt": yyyy-mm-dd hh:mm
}

계층형 컬렉션 정의

경우에 따라 도큐먼트를 계층적 데이터 구조로 만들고 싶을 때가 있습니다.
이럴 때는 @embedded 애노테이션을 사용하여 자식 클래스를 정의합니다.

@collection
class Email {
  Email {
    Id? id;
    required title;
    required updatedAt;
    required recipient;
  }
  Id? id; 
  final String title;
  @Index()
  final DateTime updatedAt;
  final Recipient? recipient;
}

@embedded
class Recipient {
  String? name;
  String? address;
}

이 구조에서는 Email 클래스에 Recipient 클래스가 계층화되어 있습니다.

데이터 이미지:

{
  "id": 99999,
  "title": "How to use Isar!!",
  "recipient": {
    "name": "John Doe",
    "address": "john.doe@gmail.com"
  }
}

클래스 정의 후 build_runner를 실행하여 ~.g.dart 파일을 생성해야 합니다.
다음 명령을 터미널에서 실행하세요.

// 캐시 정리
flutter pub run build_runner clean
// 코드 생성 실행
flutter pub run build_runner build --delete-conflicting-outputs

등록 업데이트 삭제 쿼리 작성

다음은 Isar를 사용한 CRUD 작업입니다.

단일 데이터 등록 및 업데이트

새 데이터를 데이터베이스에 추가하려면 컬렉션 정의에서 인스턴스를 생성하여 데이터베이스에 저장합니다. “put“을 사용합니다.
같은 id의 데이터가 존재하면 업데이트, 존재하지 않으면 등록됩니다.

final Isar _isar; // 이후 생략

final email = Email()
  ..title = "How to use Isar CRUD!!"
  ..recipient = Recipient()
      ..name = "Jane Doe"
      ..address = "jane.doe@gmail.com";

await _isar.emails.put(email);
//emails는 생성 파일(이번 경우 email_dto.g.dart)에 정의된 컬렉션 클래스입니다.

여러 데이터 등록 및 업데이트

일반적으로 여러 데이터를 한 번에 처리하려면 writeTxn 메소드를 사용하여 트랜잭션 처리를 합니다.
여러 데이터를 등록 또는 업데이트하려면 “putAll“을 사용합니다.

await isar.writeTxn(() async {
  await isar.emails.putAll([email1, email2]);
});

단일 데이터 삭제

데이터 삭제는 삭제할 데이터의 id를 지정합니다. “delete“를 사용합니다.

await isar.emails.delete(emailId);

여러 데이터 삭제

조건에 맞는 여러 데이터를 삭제하려면 “deleteAll“을 사용합니다.

await isar.emails.delete(emailId);

검색 쿼리 작성

다음은 데이터베이스 내의 데이터를 검색할 때 유용한 메소드입니다.

Where 절

특정 조건을 충족하는 데이터를 찾으려면 “where“을 사용합니다.
단, where를 사용할 수 있는 필드는 인덱스를 가진(@Index()가 붙은) 필드만 가능합니다.

where를 사용할 경우 아래와 같이 “where()”과 “findAll()” 사이에 조건을 넣습니다.

// updatedAt이 지정된 시간의 도큐먼트를 검색
final results = isar.emails
    .where()
    .updatedAtEqualTo("2024-12-31 23:59")
    .findAll();

Where 절에서는 다음과 같은 조건을 사용할 수 있습니다.

Filter 절 사용 방법

더 복잡한 조건을 설정하려면 “filter“를 사용합니다.
filter는 인덱스가 없는 필드에도 사용할 수 있지만 where보다 느립니다.
(아마 데이터를 가져온 후에 필터링하기 때문입니다.)

filter를 사용할 경우 아래와 같이 “filter()”와 “findAll()” 사이에 조건을 넣습니다.

// title이 "How"(문자의 대소문자 구분 없이)로 시작하는 도큐먼트를 검색
final filteredResults = isar.emails
    .filter()
    .titleStartsWith("How", caseSensitive: false)
    .findAll();

쿼리 조건

위 외에도 다양한 조건으로 검색할 수 있습니다.
이들은 ~.g.dart 파일에 생성되어 있습니다.

And Or 등의 논리 연산자

And와 Or을 사용하여 여러 조건을 지정할 수 있습니다.

// title이 "How"로 시작하고, updatedAt이 지정된 시간의 도큐먼트를 검색
final filteredResults = isar.emails
    .filter()
    .titleStartsWith("How")
    .and()
    .updatedAtEqualTo("2024-12-31 23:59")
    .findAll();

그 외에도 SQL에서 자주 사용하는 연산자를 사용할 수 있습니다.

Where 절과 Filter 절의 조합

일반적으로 Where와 Filter를 조합한 쿼리를 작성합니다.

// title이 "How"보다 크고, updatedAt이 Null이 아닌 도큐먼트를 검색
final filteredResults = isar.emails
    .titleGreaterThan("How")
    .filter()
    .updatedAtIsNotNull()
    .findAll();

Where가 더 빠르기 때문에, 가능한 한 Where로 대상을 좁히고, Filter로 복잡한 조건을 지정합니다.

Offset과 Limit

데이터의 내용이 아닌 개수로 결과의 일부를 얻으려면 “offset“과 “limit“을 설정합니다.
offset은 처음부터 얻지 않을 개수, limit은 얻을 개수입니다.

final paginatedResults = isar.emails
    .where()
    .findAll(offset: 10, limit: 5);  // 11번째부터 5개를 가져옴

전체 검색

데이터베이스 내의 모든 데이터를 얻으려면 “findAll“만 사용합니다.

final allEmails = await isar.emails
    .findAll();

마지막으로

Flutter와 Isar를 사용하여 데이터를 효율적으로 관리하는 기본적인 방법을 설명했습니다.
아직도 편리한 기능이 많이 있습니다. 이번에 소개하지 못한 기능은 추후에 기사를 작성하겠습니다.

공식 사이트에도 사용 방법이 기재되어 있으니 참고하세요!
https://isar.dev/ko/crud.html

제목과 URL을 복사했습니다