DoReMi

[Kotlin] Room 라이브러리 본문

Android

[Kotlin] Room 라이브러리

도레미누 2023. 12. 7. 22:41

Room이란!

공식문서 참조!

https://developer.android.com/training/data-storage/room?hl=ko#kts

 

Room을 사용하여 로컬 데이터베이스에 데이터 저장  |  Android 개발자  |  Android Developers

Room 라이브러리를 사용하여 더 쉽게 데이터를 유지하는 방법 알아보기

developer.android.com


Room은 AAC(Android Architecture Components), 스마트폰 내장 DB에 데이터를 저장하기 위해 사용하는 라이브러리이다!!

  1. ORM(Object Relational Mapping)라이브러리로서 DB 데이터를 Java 또는 코틀린 객체로 매핑해준다.
  2. SQLite를 내부적으로 사용하고 있지만, DB를 구조적으로 분리하여 데이터 접근의 편의성을 높여주고 유지보수에 편리하다.
  3. 다양한 Annotation을 통해 컴파일시 코드들을 자동으로 만들어주며 LiveData, RxJava와 같은 Observation 형태를 지원하고 MVP, MVVM과 같은 아키텍쳐 패턴에 쉽게 활용할 수 있도록 되어있다.

SQLite보다 Room 사용을 권장하는 이유

  1. SQlite는 쿼리의 컴파일 시간 검증을 할 수 없다. 그러나 Room에는 컴파일 타임에 SQL 유효성 검사가 있다.
  2. SQL 쿼리와 Java 데이터 개체 간에 변환하려면 많은 상용구 코드를 사용해야 한다. 하지만 Room은 상용구 코드 없이 데이터베이스 데이터를 Java 또는 Kotlin 객체에 매핑할 수 있다.

Room에서 허용 된 기능

  • 컴파일 도중 SQL에 대한 유효성 검사 가능
  • Schema가 변경될 시 자동으로 업데이트 가능
  • Java 데이터 객체를 변경하기 위해 상용구 코드 없이 ORM 라이브러리를 통해 매핑 가능
  • LiveData와 RX Java를 위한 Observation 생성 및 동작 가능

3가지 구성요소

  1. 데이터베이스 클래스
    • 데이터베이스를 보유하고 앱의 영구 데이터와의 기본 연결을 위한 기본 액세스 포인트 역할을 합니다.
  2. Entitiy - 데이터 항목
    • 앱 데이터베이스의 테이블을 나타냅니다.
  3. DAO - 데이터 액세스 객체
    • 앱이 데이터베이스의 데이터를 쿼리, 업데이트, 삽입, 삭제하는 데 사용할 수 있는 메서드를 제공합니다.

기본적인 코드

의존성 추가

build.gradle

dependencies {
    val room_version = "2.5.0"

    implementation("androidx.room:room-runtime:$room_version")
    annotationProcessor("androidx.room:room-compiler:$room_version")

		// 둘 중 하나 사용(지금은 kapt가 더 많이 쓰이는 거 같다!)
    kapt("androidx.room:room-compiler:$room_version")
    ksp("androidx.room:room-compiler:$room_version")
}

kapt가 뭐죠…?

코틀린 프로젝트를 컴파일할 때는 javac가 아닌 kotlinc로 컴파일을 하기 때문에 Java로 작성한 어노테이션 프로세서가 동작하지 않는다. 그렇기 때문에 코틀린에서는 이러한 어노테이션 처리기를 위해 KAPT(Kotlin Annotation Processing Tool)를 제공한다.

즉, 자바로 작성한 어노테이션을 코틀린 컴파일에서 처리할 수 있도록 도와준다!

ksp

젯브레인과 구글이 합작하여 KAPT보다 속도를 개선한 KSP(Kotlin Symbol Processing)를 내놓았다고 한다.

Entity

@Entity
data class User(
    @PrimaryKey val uid: Int,
    @ColumnInfo(name = "first_name") val firstName: String?,
    @ColumnInfo(name = "last_name") val lastName: String?
)

DAO

@Dao
interface UserDao {
    @Query("SELECT * FROM user")
    fun getAll(): List<User>

    @Query("SELECT * FROM user WHERE uid IN (:userIds)")
    fun loadAllByIds(userIds: IntArray): List<User>

    @Query("SELECT * FROM user WHERE first_name LIKE :first AND " +
           "last_name LIKE :last LIMIT 1")
    fun findByName(first: String, last: String): User

    @Insert
    fun insertAll(vararg users: User)

    @Delete
    fun delete(user: User)
}

@Insert

  • "onConflict = OnConflictStrategy.REPLACE" option으로 만일 동일한 PrimaryKey가 있을 경우 덮어쓸 수 있다.

@Update

  • Entity set 업데이트. Return 값으로 업데이트된 행 수를 받을 수 있다.

@Delete

  • Entity set 삭제. Return 값으로 삭제된 행 수를 받을 수 있다.

@Query

  • @Query를 사용하여 DB를 조회할 수 있다.

데이터 베이스

@Database(entities = [User::class], version = 1, exportSchema = false)
abstract class AppDatabase : RoomDatabase() {
    abstract fun userDao(): UserDao

		companion object {
        private var INSTANCE: AppDatabase? = null

        fun getInstance(context: Context): AppDatabase {
            if (INSTANCE == null) {
                synchronized(AppDatabase::class) {
                    INSTANCE = Room.databaseBuilder(
                        context.applicationContext,
                        AppDatabase::class.java,
                        "database_name.db"
                    ).build()
                }
            }
            return INSTANCE!!
        }
    }
}

entities

  • 이 DB에 어떤 테이블들이 있는지 명시한다.

version

  • Scheme가 바뀔 때 이 version도 바뀌어야 한다.

exportSchema

  • Room의 Schema 구조를 폴더로 Export 할 수 있다.

데이터 베이스 클래스는 조건이 있다!.

  1. 클래스에는 데이터베이스와 연결된 데이터 항목을 모두 나열하는 [entities](<https://developer.android.com/reference/kotlin/androidx/room/Database?hl=ko#entities>) 배열이 포함된 [@Database](<https://developer.android.com/reference/kotlin/androidx/room/Database?hl=ko>) 주석이 달려야 합니다.
  2. 클래스는 [RoomDatabase](<https://developer.android.com/reference/kotlin/androidx/room/RoomDatabase?hl=ko>)를 확장하는 추상 클래스여야 합니다.
  3. 데이터베이스와 연결된 각 DAO 클래스에서 데이터베이스 클래스는 인수가 0개이고 DAO 클래스의 인스턴스를 반환하는 추상 메서드를 정의해야 합니다.

실제 사용 코드

인스턴스 생성

val db = Room.databaseBuilder(
            applicationContext,
            AppDatabase::class.java, "database-name"
        ).build()

접근

val userDao = db.userDao()
val users: List<User> = userDao.getAll()

파일 확인

Android Studio 파일 위치

  1. Device File Explorer를 통해 디바이스 파일 탐색기를 연다!
  2. /data/data/package_name/databases/ 경로로 이동한다.!
  3. databases 폴더 안에 Room 데이터베이스 파일이 저장되어 있다.
  4. 일반적으로 파일 이름은 databaseBuilder 메서드의 3번째 매기변수로 databse_name을 지정한다.

Databse Inspector(안해봄)

데이어베이스 디버그 - 공식문서

  1. View > Tool Windows > App Inspection
  2. Database Inspector 탭을 선택
  3. 실행 중인 앱 프로세스를 메뉴에서 선택한다.
  4. 현재 실행 중인 앱 데이터베이스가 Databases 창을 나타낸다.

그러면 요런식으로 확인할 수 있나봐여~


ChatGPT 왈 왈

질문 - Room 효율적으로 사용하기

  1. 백그라운드 스레드에서 데이터베이스 액세스
    • Room은 **@Query**나 @Insert 등의 어노테이션에 suspend 키워드를 사용하여 비동기적으로 메서드를 호출할 수 있도록 지원합니다.
    • 안드로이드에는 Main(UI) Thread와 Background Thread가 있다. 그 중 Main(UI) Thread는 긴 시간이 걸리는 작업을 중단하고 에러를 발생시킨다. 때문에 db 처리 작업을 하려 하면 에러가 발생해서 비동기로 처리해줘야 한다고 합니다!~!~!
  2. LiveData 또는 RxJava 사용
    • 데이터베이스의 변경 사항을 실시간으로 감지하려면 LiveData같은 것을 사용하세요~
  3. Database Inspector 활용
    • 디버깅 시 Database Inspector를 활용하여 앱이 실행 중일 때 데이터베이스를 살펴보고 쿼리를 실행하여 결과를 확인하세요.
  4. 워크매니저 사용
    • 데이터베이스 작업을 예약하고 조절하기 위해 **WorkManager**와 같은 Android에서 제공하는 백그라운드 작업 스케줄링 도구를 사용하세요.
  5. 오프라인 지원 및 데이터 동기화
    • 네트워크에서 데이터를 가져와서 로컬 데이터베이스에 저장할 때, 오프라인 모드를 지원하고 데이터를 효율적으로 동기화하기 위한 적절한 전략을 수립하세요.
  6. 디버그 메서드 생성
    • Room은 디버그 모드에서 동작 중일 때 SQL 쿼리 및 오류 메시지를 로그로 출력할 수 있습니다. 디버깅에 도움이 되도록 fallbackToDestructiveMigration(), exportSchema 등을 활용하세요.

'Android' 카테고리의 다른 글

안드로이드 스튜디오 구버전 다운로드  (0) 2023.12.12
[Android] API 버전  (0) 2023.10.18