성장기록지
사용하기 전에 알아보는 Jetpack Room 본문
xml을 활용한 개인 프로젝트를 통해 Jetpack Room을 사용해보려고 한다!
하지만 유명한 기술이라고 꼭 Room을 사용해야 할까? 다른 대안은 없을까? 하는 마음에
사용 근거에 대해 조사하고, 그런 김에 기본적인 사용법도 학습하고자 한다!
실제 사용 후 얻는 깨달은 점들도 추후 기록하고자 한다!
Jetpack Room이란?
Android의 SQLite 데이터베이스를 쉽게 사용할 수 있도록 도와주는 Jetpack 라이브러리이다.
SQLite란?
SQL(Structured Query Language)을 사용해 데이터를 관리하며,
애플리케이션 내에서 데이터베이스 파일을 직접 생성하고 사용할 수 있게하는 라이브러리이다.
안드로이드에선 앱을 종료해도 데이터가 남아있게 하고 싶을 때 사용한다.
- 데이터베이스 엔진이다.
- 모든 데이터는 단일 파일(.db)에 저장된다.
- SQL 문법을 사용하여 데이터를 삽입, 조회, 수정, 삭제 등의 작업을 수행할 수 있다.
SQLite의 문제점은 뭘까?
- 컴파일 시간에 SQL 쿼리를 검증하기 어렵다.
따라서 런타임 에러가 발생할 가능성이 높다. - 직접 SQL 쿼리를 다 작성해야 하기 때문에 복잡하다.
보일러 플레이트가 발생하기 쉽다. - ORM(Object-Relational-Mapping)을 제공하지 않기 때문에
데이터를 객체로 다루기 어렵다.
✨ORM이란?
- 데이터베이스의 테이블 구조와 애플리케이션의 객체 지향 구조를 매핑해주는 도구나 기술.
- ORM을 사용하면 SQL 쿼리를 직접 작성하지 않고도, 객체 형태로 데이터를 CRUD할 수 있다.
- 아래에 예시를 통해 서술하도록 하겠다 .
그렇다면 Room은 어떻게 이를 개선했을까?
1. 컴파일 타임에 SQL 쿼리를 검증한다.
컴파일 시간에 SQL 쿼리의 문법 오류나 매핑 문제를 미리 확인해주준다.
2. 어노테이션만으로 쿼리를 간소화해준다.
@Insert등의 어노테이션으로 간편하게 쿼리문을 사용할 수 있고, 보일러플레이트를 줄여준다.
아래에서 예시를 통해 비교하며 설명해보도록 하겠다!
3. SQLite 데이터베이스의 추상화 계층을 제공함
말이 어렵지만 풀어서 작성해보면, SQLite를 직접 다루는 복잡한 작업들을 대신 처리해 주는 라이브러리라는 뜻이다. 즉, 개발자가 직접 SQL 쿼리를 작성하거나 SQLite의 내부 동작을 이해하지 않아도, CRUD를 수행할 수 있도록 돕는다.
그러면 그냥 Room을 사용하면 되는건가?
공식문서를 참고해봐도, 사용하는 데 상당한 시간과 노력이 필요하므로,
Room 사용을 권장한다 라고 나와있다.
Room은 어떻게 이루어져있는가?
공식문서에 아래와 같은 아키텍쳐 다이어그램이 있다.
하지만 다이어그램을 보고 한번에 완벽히 이해하기는 어려우므로,
하나씩 무엇을 담당하는지 살펴보자.
1.Entity
- 빨간색의 Entities를 구성하는 요소이다.
- 데이터베이스 테이블의 각각의 행을 나타내는 클래스이다.
- @Entity 어노테이션을 붙여서 사용합니다.
- 아래의 예시와 같이 Primary key가 반드시 필요하다.
- PrimaryKey에 autoGenerate를 사용하면 자동으로 key 값이 증가한다.
- tableName으로 테이블 이름을 지정할 수 있다.
그렇다면 이 Entity로 어떻게 접근할 수 있을까?
바로 DAO(Data Access Objects)를 활용하면 된다.
2.DAO
- 데이터베이스 테이블의 데이터와 상호작용하는 추상화된 인터페이스이다.
- Room 라이브러리는 DAO에서 정의한 메서드를 자동으로 SQLite 쿼리로 변환하여 실행해준다.
- @Insert ,@Delete 같은 어노테이션을 통해 편의 메서드를 작성할 수 있다.
- 비교적 복잡한 쿼리문은 @Query 어노테이션을 통해 쿼리 메서드를 작성할 수 있다.
3.Room DataBase
- DAO를 접근 할 수 있는 레퍼런스를 제공한다. (Get Dao)
- Room은 이 클래스에 의해 데이터베이스 인스턴스를 생성, DAO를 사용할 수 있게 됨.
- Room이 해당 클래스를 상속하여 구체적인 구현을 제공할 수 있기 때문에, 추상 클래스로 선언해야 개발자는 Room의 자동화된 처리를 활용할 수 있다.
요약
Room DataBase를 통해서 Dao에 접근한다. 그 Dao를 통해
Entity 객체를 인자로 받아서 데이터를 삽입, 수정, 삭제, 조회 후 Entity 객체를 반환하게 한다.
SQLite와 코드가 얼마나 차이날까 ?
앞서서 Room은 어노테이션을 이용해 보일러 플레이트를 감소시킨다고 하였다.
그러면 SQLite로 작성한 코드와 어느정도 차이나는지 한번 비교해보자.
Room으로 작성한 DAO 코드.
@Dao
interface ProductDao {
@Insert
suspend fun insertProduct(product: Product)
@Query(value = "SELECT * FROM orders WHERE id = :productId")
suspend fun getProductById(productId: Int): Product?
@Query("SELECT * FROM orders")
suspend fun getAllProducts(): List<Product>
SQLite로 DAO를 옮겨본 코드.
class ProductDao(private val dbHelper: ProductDatabaseHelper) {
suspend fun insertProduct(product: Product) {
val db = dbHelper.writableDatabase
val values = ContentValues().apply {
put("name", product.name)
put("brand_name", product.brandName)
put("price", product.price)
}
db.insert("orders", null, values)
}
suspend fun getProductById(productId: Int): Product? {
val db = dbHelper.readableDatabase
val cursor = db.query(
"orders",
null,
"id = ?",
arrayOf(productId.toString()),
null, null, null
)
return if (cursor.moveToFirst()) {
Product(
id = cursor.getInt(cursor.getColumnIndex("id")),
name = cursor.getString(cursor.getColumnIndex("name")),
brandName = cursor.getString(cursor.getColumnIndex("brand_name")),
price = cursor.getDouble(cursor.getColumnIndex("price"))
)
} else {
null
}.also {
cursor.close()
}
}
suspend fun getAllProducts(): List<Product> {
val db = dbHelper.readableDatabase
val cursor = db.query("orders", null, null, null, null, null, null)
val products = mutableListOf<Product>()
while (cursor.moveToNext()) {
products.add(
Product(
id = cursor.getInt(cursor.getColumnIndex("id")),
name = cursor.getString(cursor.getColumnIndex("name")),
brandName = cursor.getString(cursor.getColumnIndex("brand_name")),
price = cursor.getDouble(cursor.getColumnIndex("price"))
)
)
}
cursor.close()
return products
}
한 눈에 봐도 코드길이가 상당히 차이난다.
추가적인 Room의 기능
FTS(Full-text search)
텍스트 검색를 위한 가상의 table(테이블)을 의미한다.
FTS 문서에 따르면,
517,430개의 data에서 어떠한 글자를 찾는데 일반 table은 22.5초가 걸린 반면,
FTS3에서는 0.03초 밖에 안 걸렸다고 한다.
Room에서 FTS 테이블을 사용하려면?
그냥 @Fts3이나 @Fts4 어노테이션만 붙이면 된다.
FTS 테이블에선 rowId가 기본적으로 PrimaryKey가 된다.
숫자 관련 필드는 검색에 포함되지 않고, 값을 저장할 수만 있다.
@Fts4
@Entity(tableName = "products")
data class Product(
@PrimaryKey @ColumnInfo(name = "rowid") val Id: Int = 0,
val label: String,
val price: Int, // FTS는 텍스트 기반 필드에 적합하므로 숫자 필드는 검색에 포함 x
@ColumnInfo(name = "brand_name") val brandName: String
)
이후 DAO에서 MATCH를 활용하면 된다.
@Dao
interface ProductDao {
@Query("SELECT * FROM products WHERE products MATCH :query")
fun searchProducts(query: String): List<Product>
}
참고자료
https://developer.android.com/training/data-storage/room?hl=ko
https://developer.android.com/training/data-storage/room/accessing-data?hl=ko
https://www.youtube.com/watch?v=s5PaI2wb4hM&ab_channel=%EC%9A%B0%EC%95%84%ED%95%9C%ED%85%8C%ED%81%AC
https://www.sqlite.org/fts3.html
'안드로이드' 카테고리의 다른 글
Compose State와 State hoisting (0) | 2025.01.08 |
---|---|
Android의 Clean Architecture란? (0) | 2025.01.07 |
android 권장 아키텍쳐란? (0) | 2025.01.02 |
Fragment Manager란? (0) | 2024.12.30 |
안드로이드 Life Cycle (with Activity,Fragment) (1) | 2024.12.20 |