Room (Kotlin 멀티플랫폼)

Room 지속성 라이브러리는 SQLite를 완벽히 활용하면서 강력한 데이터베이스 액세스를 지원하는 SQLite의 추상화 레이어를 제공합니다. 이 페이지에서는 Kotlin 멀티플랫폼 (KMP) 프로젝트에서 Room을 사용하는 방법을 중점적으로 설명합니다. Room 사용에 관한 자세한 내용은 Room을 사용하여 로컬 데이터베이스에 데이터 저장 또는 공식 샘플을 참고하세요.

종속 항목 설정

KMP를 지원하는 현재 Room 버전은 2.7.0-alpha01 이상입니다.

KMP 프로젝트에서 Room을 설정하려면 다음과 같이 모듈의 build.gradle.kts 파일에 아티팩트의 종속 항목을 추가합니다.

  • androidx.room:room-gradle-plugin - Room 스키마를 구성하는 Gradle 플러그인
  • androidx.room:room-compiler - 코드를 생성하는 KSP 프로세서
  • androidx.room:room-runtime - 라이브러리의 런타임 부분
  • androidx.sqlite:sqlite-bundled - (선택사항) 번들 SQLite 라이브러리

또한 Room의 SQLite 드라이버를 구성해야 합니다. 이러한 동인은 타겟 플랫폼에 따라 다릅니다. 사용 가능한 드라이버 구현에 관한 설명은 드라이버 구현을 참고하세요.

추가 설정 정보는 다음을 참고하세요.

데이터베이스 클래스 정의

공유 KMP 모듈의 공통 소스 세트 내에서 DAO 및 항목과 함께 @Database 주석이 달린 데이터베이스 클래스를 만들어야 합니다. 이러한 클래스를 공통 소스에 배치하면 모든 대상 플랫폼에서 공유할 수 있습니다.

// shared/src/commonMain/kotlin/Database.kt

@Database(entities = [TodoEntity::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
  abstract fun getDao(): TodoDao
}

@Dao
interface TodoDao {
  @Insert
  suspend fun insert(item: TodoEntity)

  @Query("SELECT count(*) FROM TodoEntity")
  suspend fun count(): Int

  @Query("SELECT * FROM TodoEntity")
  fun getAllAsFlow(): Flow<List<TodoEntity>>
}

@Entity
data class TodoEntity(
  @PrimaryKey(autoGenerate = true) val id: Long = 0,
  val title: String,
  val content: String
)

실제 / 예상 선언을 사용하여 플랫폼별 Room 구현을 만들 수 있습니다. 예를 들어 expect를 사용하여 공통 코드에 정의된 플랫폼별 DAO를 추가한 다음 플랫폼별 소스 세트에 추가 쿼리를 사용하여 actual 정의를 지정할 수 있습니다.

데이터베이스 빌더 만들기

각 플랫폼에서 Room을 인스턴스화하려면 데이터베이스 빌더를 정의해야 합니다. 이는 파일 시스템 API의 차이로 인해 플랫폼별 소스 세트에 있어야 하는 API의 유일한 부분입니다. 예를 들어 Android에서 데이터베이스 위치는 일반적으로 Context.getDatabasePath() API를 통해 가져오는 반면 iOS에서는 NSHomeDirectory를 사용하여 데이터베이스 위치를 가져옵니다.

Android

데이터베이스 인스턴스를 만들려면 데이터베이스 경로와 함께 Context를 지정합니다. 데이터베이스 팩토리를 지정할 필요가 없습니다.

// shared/src/androidMain/kotlin/Database.kt

fun getDatabaseBuilder(ctx: Context): RoomDatabase.Builder<AppDatabase> {
    val appContext = ctx.applicationContext
    val dbFile = appContext.getDatabasePath("my_room.db")
    return Room.databaseBuilder<AppDatabase>(
        context = appContext,
        name = dbFile.absolutePath
    )
}

iOS

데이터베이스 인스턴스를 만들려면 데이터베이스 경로와 함께 데이터베이스 팩토리를 제공합니다. 데이터베이스 팩토리는 KClass<T> 유형의 수신자로 이름이 instantiateImpl인 생성된 확장 함수를 호출하는 람다 함수입니다. 여기서 T@Database 주석이 지정된 클래스의 유형입니다.

// shared/src/iosMain/kotlin/Database.kt

fun getDatabaseBuilder(): RoomDatabase.Builder<AppDatabase> {
    val dbFilePath = NSHomeDirectory() + "/my_room.db"
    return Room.databaseBuilder<AppDatabase>(
        name = dbFilePath,
        factory =  { AppDatabase::class.instantiateImpl() }
    )
}

JVM (데스크톱)

데이터베이스 인스턴스를 만들려면 데이터베이스 경로만 지정하세요. 데이터베이스 팩토리를 제공할 필요가 없습니다.

// shared/src/commonMain/kotlin/Database.kt

fun getDatabaseBuilder(): RoomDatabase.Builder<AppDatabase> {
    val dbFile = File(System.getProperty("java.io.tmpdir"), "my_room.db")
    return Room.databaseBuilder<AppDatabase>(
        name = dbFile.absolutePath,
    )
}

데이터베이스 인스턴스화

플랫폼별 생성자 중 하나에서 RoomDatabase.Builder를 가져온 후에는 실제 데이터베이스 인스턴스화와 함께 일반 코드로 Room 데이터베이스의 나머지 부분을 구성할 수 있습니다.

// shared/src/commonMain/kotlin/Database.kt

fun getRoomDatabase(
    builder: RoomDatabase.Builder<AppDatabase>
): AppDatabase {
    return builder
        .addMigrations(MIGRATIONS)
        .fallbackToDestructiveMigrationOnDowngrade()
        .setDriver(BundledSQLiteDriver())
        .setQueryCoroutineContext(Dispatchers.IO)
        .build()
}

SQLiteDriver 선택

이전 코드 스니펫은 BundledSQLiteDriver를 사용합니다. 이 드라이버는 소스에서 컴파일된 SQLite를 포함하는 권장 드라이버로, 모든 플랫폼에서 가장 일관된 최신 버전의 SQLite를 제공합니다. OS에서 제공하는 SQLite를 사용하려면 플랫폼별 드라이버를 지정하는 플랫폼별 소스 세트에서 setDriver API를 사용하세요. Android의 경우 AndroidSQLiteDriver를 사용하고 iOS의 경우 NativeSQLiteDriver를 사용할 수 있습니다. NativeSQLiteDriver를 사용하려면 iOS 앱이 시스템 SQLite와 동적으로 링크되도록 링커 옵션을 제공해야 합니다.

// shared/build.gradle.kts

kotlin {
    listOf(
        iosX64(),
        iosArm64(),
        iosSimulatorArm64()
    ).forEach { iosTarget ->
        iosTarget.binaries.framework {
            baseName = "TodoApp"
            isStatic = true
            // Required when using NativeSQLiteDriver
            linkerOpts.add("-lsqlite3")
        }
    }
}

차이점

Room은 원래 Android 라이브러리로 개발되었지만 나중에 API 호환성에 중점을 두고 KMP로 이전되었습니다. Room의 KMP 버전은 플랫폼 및 Android 전용 버전과 약간 다릅니다. 이러한 차이점은 다음과 같습니다.

DAO 함수 차단

KMP용 Room을 사용할 때 Android가 아닌 플랫폼용으로 컴파일된 모든 DAO 함수는 Flow와 같은 반응형 반환 유형을 제외하고 suspend 함수여야 합니다.

// shared/src/commonMain/kotlin/MultiplatformDao.kt

@Dao
interface MultiplatformDao {
    // ERROR: Blocking function not valid for non-Android targets
    @Query("SELECT * FROM Entity")
    fun blockingQuery(): List<Entity>

    // OK
    @Query("SELECT * FROM Entity")
    suspend fun query(): List<Entity>

    // OK
    @Query("SELECT * FROM Entity")
    fun queryFlow(): Flow<List<Entity>>

    // ERROR: Blocking function not valid for non-Android targets
    @Transaction
    fun blockingTransaction() { // … }

    // OK
    @Transaction
    suspend fun transaction() { // … }
}

Room은 Kotlin이 여러 플랫폼에 제공하는 다양한 기능의 비동기 kotlinx.coroutines 라이브러리를 활용합니다. 최적의 기능을 위해 KMP 프로젝트에서 컴파일된 DAO에 suspend 함수가 적용됩니다. 단, 기존 코드베이스와의 하위 호환성을 유지하기 위한 Android 전용 DAO는 예외입니다.

KMP와의 기능 차이

이 섹션에서는 Room의 KMP와 Android 플랫폼 버전 간에 기능이 어떻게 다른지 설명합니다.

@RawQuery DAO 함수

Android가 아닌 플랫폼용으로 컴파일된 @RawQuery 주석이 지정된 함수는 오류를 생성합니다. Room의 향후 버전에 @RawQuery 지원을 추가할 예정입니다.

쿼리 콜백

쿼리 콜백을 구성하기 위한 다음 API는 일반적으로 사용할 수 없으므로 Android 이외의 플랫폼에서는 사용할 수 없습니다.

  • RoomDatabase.Builder.setQueryCallback
  • RoomDatabase.QueryCallback

향후 버전의 Room에 쿼리 콜백 지원을 추가할 예정입니다.

콜백 인터페이스 RoomDatabase.QueryCallback와 함께 쿼리 콜백 RoomDatabase.Builder.setQueryCallbackRoomDatabase를 구성하는 API는 일반적으로 사용할 수 없으므로 Android가 아닌 다른 플랫폼에서는 사용할 수 없습니다.

데이터베이스 자동 종료

시간 초과 후 자동 닫기를 사용 설정하는 API RoomDatabase.Builder.setAutoCloseTimeout는 Android에서만 사용할 수 있으며 다른 플랫폼에서는 사용할 수 없습니다.

사전 패키징 데이터베이스

기존 데이터베이스 (즉, 사전 패키징된 데이터베이스)를 사용하여 RoomDatabase를 만드는 다음 API는 일반적으로 사용할 수 없으므로 Android가 아닌 다른 플랫폼에서는 사용할 수 없습니다. 이러한 API는 다음과 같습니다.

  • RoomDatabase.Builder.createFromAsset
  • RoomDatabase.Builder.createFromFile
  • RoomDatabase.Builder.createFromInputStream
  • RoomDatabase.PrepackagedDatabaseCallback

Room의 향후 버전에서 사전 패키징된 데이터베이스 지원을 추가할 예정입니다.

멀티 인스턴스 무효화

다중 인스턴스 무효화를 사용 설정하는 API인 RoomDatabase.Builder.enableMultiInstanceInvalidation는 Android에서만 사용할 수 있으며 일반 플랫폼이나 다른 플랫폼에서는 사용할 수 없습니다.