Jetpack은 2018년 5월 Google에서 발표한 라이브러리 및 도구 모음집이다. Jetpack 이전에 있었던 Support 라이브러리는 여러 가지 한계점을 가지고 있었고, 이를 개선하면서 Jetpack이 출시되었다. Android 9.0 (API Level 28)의 출시와 함께 Jetpack의 일부인 AndroidX 라이브러리가 제공되었고, AndroidX는 기존 Support 라이브러리와 Jetpack 구성요소들을 포함하게 되었다. 현재는 AndroidX 사용을 권장하고 있다.
이번 포스팅은 Jetpack의 Architecture 구성요소 중 하나인 Room에 대한 포스팅이다.
1. What is Room?
Room은 디바이스 내의 로컬 데이터베이스에 SQLite보다 더 효과적으로 데이터를 저장하기 위해 사용하는 Jetpack 라이브러리의 구성요소이다. Android Developers 공식 문서에서는 Room이 SQLite에 대한 추상화 레이어를 제공하여 원활한 데이터베이스 액세스를 지원하는 동시에 SQLite를 완벽히 활용한다고 설명한다. SQLite에 어떤 문제점이 있기 때문에 Room을 사용한다는 말인데, SQLite의 문제점을 아래 그림으로 설명한다.
일반적으로 디바이스 내의 로컬 데이터베이스를 활용할 때 얻을 수 있는 가장 큰 이점은 바로 데이터 캐싱이다. 구조화가 잘 되어있고 많은 양의 정보를 저장하는 데이터를 처리할 때 네트워크 접근에 대한 문제가 생길 경우, 오프라인 상태에서도 여전히 데이터를 잘 가져올 수 있어야 한다. 이는 나중에 디바이스가 온라인 상태가 되어도 사용자의 조작이 생긴 데이터는 서버와 동기화가 잘 되어야 할 것이다.
Room은 위와 같은 SQLite의 문제점들을 자동으로 처리한다. 따라서, Android Developers 공식문서에서는 SQLite 대신 Room을 사용하는 것을 적극 권장하고 있다.
아래 Room의 장점들을 소개한다.
1. 컴파일 타임에서의 에러 처리
2. 스키마 변경 시 업데이트 자동화
3. RxJava를 위한 Observable 제공 **
2. Room 주요 구성 요소 (Room Primary Components)
Room의 세 가지 구성요소이다.
1. 데이터베이스 (Database)
Database Holder를 포함하고, 어플리케이션의 지속적인 관계형 데이터의 기본 연결을 위한 Access Point 역할을 한다. 말 그대로 데이터베이스를 의미한다.
RoomDatabase( )를 확장하는 Abstract Class로 구현하며 @Database Annotation을 사용한다. Annotation안에는 일반적으로 데이터베이스와 연관된 Entity들을 포함하고, Version을 표기한다. 또한, @Dao로 표기된 Class를 Return 하는 Abstract Method를 포함한다.
아래와 같이 구현할 수 있다.
@Database(entities = arrayOf(User::class), version = 1)
abstract class AppDatabase: RoomDatabase() {
abstract fun userDao(): UserDao
}
2. 항목 (Entity)
데이터베이스 내의 테이블을 나타낸다. 테이블을 구현하기 때문에 data class로 Class를 생성하고 @Entity Annotation을 사용한다. Annotation 안에는 테이블의 이름을 지정할 수 있으며, @Entity를 사용하면 각 프로퍼티들에 제약조건을 지정할 수 있다. (PK, UNIQUE ..)
아래와 같이 구현할 수 있다.
@Entity(tableName = "UserInfo")
data class User (
@PrimaryKey val uId: Int,
@ColumnInfo(name = "first_name") val firstName: String,
@ColumnInfo(name = "last_name") val lastName: String,
@Ignore val isMember: Boolean
)
@PrimaryKey는 데이터베이스 내의 Primary Key를 지정한다. @ColumnInfo에는 데이터베이스의 각 Column을 지정한다. @Ignore는 데이터베이스 내에 저장하지는 않지만 Class내에 있어야 하는 프로퍼티를 나타낸다.
3. DAO (Database Access Object)
데이터베이스에 접근하는 데 사용되는 Method들을 포함한다. Dao는 일반 Class가 아닌 Abstract Class 혹은 Interface로 정의한다. Abstract Class로 구현할 경우 선택적으로 RoomDatabase( )를 유일한 파라미터로 사용하는 Constructor를 가질 수 있다.
Dao에서는 삽입, 삭제, 업데이트와 같은 질의어를 생성할 수 있다. 기존의 SQLite 쿼리는 컴파일에서 질의 오류를 검출할 수 없었다. 하지만, Room은 각각의 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)
}
로컬 데이터베이스에 접근한 결과를 기존 UI Thread가 아닌 비동기적으로 처리해야 하기 때문에 각 함수 앞에 suspend를 붙여 비동기 함수로 만들어 줄 수 있다. 혹은, RxJava Observables나 LiveData 혹은 Coroutines을 이용하여 비동기적으로 처리할 수도 있다.
Android Developers 공식 문서에서는 Room 아키텍처 다이어그램을 이용하여 Room의 다양한 구성요소들의 관계를 제시하고 있다.
초기에 어플리케이션에서 RoomDatabase( )를 사용하여 로컬 데이터베이스와 연결된 Dao를 가져온다. 이후, 각 Dao를 이용하여 데이터베이스의 Entity를 가져오고 Entity의 변경사항을 다시 로컬 데이터베이스에 저장한다. 최종적으로 Entity를 이용하여 로컬 데이터베이스 내의 테이블 Column에 매칭되는 값을 가져오게 된다.
마지막으로, 최종적인 사용을 위해 데이터베이스 Instance를 생성한다. Instance를 생성할 때 싱글톤 디자인 패턴을 따라야 한다. RoomDatabase Instance는 리소스를 많이 소모하는데, 어플리케이션 작동 중 단일 프로세스 내에서 여러 Instance에 접근할 필요가 없기 때문이다.
아래와 같이 구현할 수 있다.
val db = Room.databaseBuilder(
applicationContext,
AppDatabase::class.java,
"생성한 데이터베이스 이름"
).build()
References
https://developer.android.com/training/data-storage/room
https://blog.mindorks.com/data-access-objects-dao-in-room
'Android > Jetpack' 카테고리의 다른 글
[ViewModel] ViewModel 시작하기 (0) | 2022.03.11 |
---|