[Flutter] 어플리케이션 내부 DB 관리 1: sqflite(개발 과정)

김민서·2023년 8월 25일

가림 치료 앱 개발

목록 보기
3/6

공식 문서

sqflite는 Flutter에서 SQLite를 사용할 수 있도록 하는 플러그인이다.
SQLite는 어플리케이션 내부 데이터베이스를 생성하고 관리하는 오픈 소스 RDBMS(Relational Database Management System: 관계형 데이터베이스를 생성, 수정, 관리할 수 있는 소프트웨어)이다.
SQLite는 가볍고, 간단하며 사용이 편리하다. 작동하기 위해 따로 설치해야 하는 외부 종속성이 없으며, 클라이언트 사이드 시스템이기 때문에 서버와의 통신이 필요 없다.

이 프로젝트에서는 sqflite 플러그인을 사용하여 실시간 BLE 기기(BLE 센서가 부착된 아이패치) 스캔 기록을 저장하였고 해당 파일을 CSV 파일로(전부 또는 사용자가 원하는 범위만) 추출하는 방식으로 구현했다.

해당 기능 구현을 위해 sqflite 플러그인을 설치하고 어플리케이션의 lib 디렉토리에 database 디렉토리를 생성하고 dbHelper.dart 파일을 만들었다. dbHelper 클래스 안에 공식 문서를 참고하여 데이터베이스 파일을 만드는 함수를 작성해주었다.

  • getDatabasesPath() (DB가 저장되는 경로)
    - 안드로이드 기본 설정 경로: data/data/<package_name>/databases
    - iOS/MacOS 기본 설정 경로: Documents directory
var _db;
Future<Database> get database async {
	if (_db != null) return _db;
    _db = openDatabase(
    	join(await getDatabasesPath(), 'EyePatch.db'),
        onCreate: (db, version) => _createDB(db),
        version: 1,
    );
    return _db;
}

처음에 공식 문서의 예제를 따라서 코드를 작성했는데, 이렇게 getter를 사용하여 EyePatch.db라는 고정된 이름의 파일을 만들어 주었다. 하지만 이 때 각각의 아이패치마다의 개별 파일(쉽게 구분하기 위해 아이패치 BLE 센서의 MAC주소를 파일 이름으로 사용하였다.)이 필요하다는 것을 간과하였고, 이후 개발을 진행하면서 이를 깨닫고 코드를 수정하게 되었다.
수정된 코드는 다음과 같다.

var _db;
Future<Database> getDatabase(String tableName) async {
	if (_db != null) return _db;
    _db = openDatabase(
    	join(await getDatabasesPath(), '$tableName.db'),
        onCreate: (db, version) => _createDB(db, tableName),
        version: 1,
    );
    return _db;
}

위에서 언급한 기능을 구현하기 위해서는 파일을 만들 때부터 파라미터로 파일명을 입력받아 동적으로 파일명을 생성해야 했다.
하지만 Dart의 getter을 구현하기 위한 get 키워드를 사용하면 파라미터를 사용할 수 없었다.

그래서 get 키워드를 지우고, String 형식의 파일명을 받아서 이를 파일 이름으로 넣어주었다.


아무튼
데이터베이스 파일을 만든 후,
SQL 테이블 생성 문법을 사용하여 테이블을 만드는 _createDB() 함수를 작성하고(테이블 이름도 파일 이름과 동일하게 해주었다.) 이를 openDatabase() onCreate 옵션으로 넣어주었다.

static void _createDB(Database db, String tableName) {
	db.execute(
    	"CREATE TABLE $tableName(timeStamp INTEGER PRIMARY KEY, ble String patchTemp DOUBLE, ambientTemp DOUBLE, patched STRING, rawData STRING)"
    );
}

이제 같은 파일에 이렇게 생성한 테이블을 관리하는 여러 함수들을 작성하면 되는데, 그 중 중요한 몇가지 함수를 알아보자면 먼저 기록(테이블의 행)을 삽입하는 함수가 필요하다.

Future<void> insertRecord(String tableName, BLE ble) async {
	final db = await getDatabase(tableName);
    await db.insert(tableName, ble.toMap(), conflictAlgorithm: ConflictAlgorithm.replace);
}

이렇게 기기 MAC 주소를 통해(tableName) db를 가져오고, db.insert 함수를 통해 테이블 이름과, 저장할 값(Map<String, Object?> 타입), 옵션(conflictAlgorithm - 값이 중복되었을 때 해결 방법 지정)을 넘겨주어 테이블에 값을 삽입한다.

0개의 댓글