Room은 SQLite의 추상 레이어를 제공해 주어 DB에 편하게 접근할 수 있도록 해주는 ORM Library이다. JetPack, 그 안에서도 AAC 안에 속해있는 라이브러리 중 하나다.
SQLite에 비해 Room을 사용했을 때 얻을 수 있는 이점이 많다.보일러 플레이트 코드를 줄일 수 있다는 점부터, 컴파일 타임에 유효성 검사를 할 수 있고, schema가 변경되었을 때도 자동으로 업데이트가 되며, LiveData 등과 함께 사용해 데이터를 Observation 할 수 있다는 점 등 장점이 많다. 현재 Google은 SQLite 대신 room을 사용하기를 적극적으로 권장하고 있다.
Room 을 입력하고 alt + Enter를 누르면 dependency를 자동으로 추가할 수 있다.
(하지만 어차피 annotation 처리를 위해 gradle에 아래 내용을 추가해주어야 한다)
// kapt plugin 추가
apply plugin: 'kotlin-kapt'
dependencies {
implementation "androidx.room:room-runtime:$room_version"
// annotationProcessor 대신 kapt
kapt "androidx.room:room-compiler:$room_version"
}
class 위에 @Entity
어노테이션을 붙여주어 Room 에서 관리하는 Entity로 인식시킨다.
멤버 변수에 @PrimaryKey
어노테이션을 붙여주면 primary key로 지정할 수 있다.
이 때, autoGenerate = True
옵션으로 키값을 자동으로 증가시키게 할 수 있다.
@Entity
data class Todo(var title:String) {
@PrimaryKey(autoGenerate = true) var id:Int = 0
}
@Dao
어노테이션으로 Dao interface를 정의한다.
(DAO란 data access object로, Data에 직접 access하는 역할의 객체를 말한다. 실질적으로 db와의 정보 전달은 이 dao를 통해서만 이루어질 것이다)
@Query
어노테이션으로 직접 쿼리를 실행할 수 있고, 기본적으로 @Insert
, @Update
, @Delete
와 같은 어노테이션을 사용할 수 있다. 전달받은 객체를 insert하거나, 전달받은 객체와 primary key가 일치하는 객체를 업데이트 혹은 삭제한다.
아래 getAll 함수에서 반환형을 잘 보면 List<Todo>
가 아닌 LiveData<List<Todo>>
라는 것이 보이는데, LiveData와 연계하여 아래와 같이 사용할 수 있으며 놀랍게도 observing하고 있으면 db의 값이 변경될 때 마다 신호를 보내줄 수 있다! 다음에 LiveData를 다루면서 자세히 적어 보겠다.
@Dao
interface TodoDao {
@Query("SELECT * FROM Todo")
fun getAll(): LiveData<List<Todo>>
@Insert
suspend fun insert(todo: Todo)
@Update
suspend fun update(todo: Todo)
@Delete
suspend fun delete(todo: Todo)
}
실제로 사용하는 곳에서 호출하는 시작점이 될 객체다.
entities에 사용하는 entity들을 적어주고, Dao 객체를 가져올 수 있게 함수를 정의한다.
@Database(entities = [Todo::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
abstract fun todoDao(): TodoDao
}
먼저 db객체를 생성한다. (공식 문서에서는 액티비티나 뷰모델 마다 생성하지 않고 싱글톤으로 관리하는 것을 권장한다)
그리고 db객체에서 원하는 dao를 호출한 후, dao의 함수를 호출한다.
private val db = Room.databaseBuilder(
application,
AppDatabase::class.java,
"test_db"
)
// .allowMainThreadQueries() // 비동기 처리 하기 전 임시로 사용
.build()
fun getAll(): LiveData<List<Todo>> {
return db.todoDao().getAll()
}
fun insert(todo: String) {
viewModelScope.launch(Dispatchers.IO) { // 코루틴을 사용한 비동기 처리
db.todoDao().insert(Todo(todo))
}
}
DB는 main thread에서 동작시키려 하면 오류가 난다! 임시로 allowMainThreadQueries()
을 사용해 메인 쓰레드에서 돌아가게 허용해주거나 coroutine을 사용해서 IO thread에서 호출하도록 하자!
전에 진행하던 프로젝트(Roadline)에서는 Realm 데이터베이스를 사용했었다. 그때는 room에 대해 잘 몰랐기도 하고, '좋다고들 하니 써 보자!' 하는 생각이었는데 Room을 공부하면서 Realm과 어떤 차이가 있는지 궁금해졌다.
길어질 것 같아 언제 한번 정리해서 적도록 하겠습니다!