성장기록지
Room을 활용한 쇼핑 상품 북마크 기능(Room 구조 구현) 본문
쇼핑앱을 구현하며 Room을 통해 로컬 데이터베이스에 상품을 저장하는 기능을 구현하고자 하였다.
이전에 공부한 Room의 구조들을 활용하며, MVVM 구조를 깨지 않도록 구현한 과정을 작성하고자 한다.
이전에 내가 학습한 글을 통해 프로젝트 내에 Room의 아키텍쳐대로 구성하고자 하였다.
https://codinghun.tistory.com/23
Entity 구성
Entity는 아래와 같이 구성하였다.
api호출 결과로 ItemsResponse를 가져오고,
거기서 각 검색 상품마다의 정보를 Item으로 가져왔다.
앱 내에서 ShoppingItem만을 사용하므로 Item의 확장함수를 통해
아래 코드와 같이 toShoppingItem으로 Entity로 전환해주도록 하였다.
PrimaryKey인 id는 상품의 고유값인 productId를 통해 구성하였다.
@Entity(tableName = "bookmark_items")
data class ShoppingItem(
@PrimaryKey val id: String,
val item: Item,
val isBookmarked: Boolean
) {
companion object {
fun placeholder(): ShoppingItem {
return ShoppingItem(
id = "",
item = Item.placeholder(),
isBookmarked = false
)
}
}
}
아래는 ItemsResponse, Item, toShoppingItem()이 담긴 코드이다.
package com.example.ishopping.data.model
import com.google.gson.annotations.SerializedName
data class ItemsResponse(
val lastBuildDate: String,
val total: Int,
val start: Int,
val display: Int,
val items: List<Item>
)
data class Item(
val title: String,
val link: String,
val image: String,
@SerializedName("lprice") val lowPrice: String,
@SerializedName("hprice") val highPrice: String?,
val mallName: String,
val productId: String,
val productType: String,
val brand: String?,
val maker: String?,
val category1: String,
val category2: String,
val category3: String,
val category4: String?
) {
companion object {
fun placeholder() = Item(
title = "",
link = "",
image = "",
lowPrice = "",
highPrice = null,
mallName = "",
productId = "",
productType = "",
brand = null,
maker = null,
category1 = "",
category2 = "",
category3 = "",
category4 = null
)
}
}
fun Item.toShoppingItem() = ShoppingItem(
id = this.productId,
item = this,
isBookmarked = false
)
DAO 구성
아래와 같이 사용할 쿼리문들을 Dao에 지정해두었다.
getAll은 FLow 반환이라 suspend를 붙이지 않았다.
이 글을 작성하며 suspend를 왜 붙여야 하는지 이해하게 되었는데, 현재 프로젝트에는 적용되어있지 않는다.
프로젝트에 적용하며 추가로 글을 작성하도록 하겠다.
@Dao
interface ShoppingItemDao {
@Insert
suspend fun insert(shoppingItem: ShoppingItem)
@Delete
suspend fun delete(shoppingItem: ShoppingItem)
@Query("SELECT * FROM bookmark_items")
fun getAll(): Flow<List<ShoppingItem>>
@Query("DELETE FROM bookmark_items")
suspend fun deleteAllBookmarkItems()
}
DATABASE 구성
의존성 주입을 위해 추상클래스로 RoomDatabase를 구성해주었다.
ShopingItemDao에 접근하는 추상 함수도 구현해주었다.
지금 보니 함수명이 적절하지 않은것 같다.
@Database(entities = [ShoppingItem::class], version = 2)
@TypeConverters(ShoppingItemTypeConverter::class)
abstract class AppDatabase : RoomDatabase() {
abstract fun bookmarkItemDao(): ShoppingItemDao
}
이후에 DatabaseModule을 구성하였다.
Room.databaseBuilder를 통해 데이터베이스 인스턴스를 만들고,
provideBookmarkItemDao()를 통해 Dao 인스턴스를 얻어올 수 있다.
@Module
@InstallIn(SingletonComponent::class)
object DatabaseModule {
@Singleton
@Provides
fun provideAppDatabase(@ApplicationContext context: Context): AppDatabase {
return Room.databaseBuilder(
context, AppDatabase::class.java,
"ishopping-db"
).build()
}
@Provides
fun provideBookmarkItemDao(appDatabase: AppDatabase) = appDatabase.bookmarkItemDao()
}
'개인 프로젝트' 카테고리의 다른 글
onCreate에 PersistableBundle이 추가되면 화면이 그려지지 않는 이유 (0) | 2025.02.10 |
---|---|
Room을 활용한 쇼핑 상품 북마크 기능 (기능 구현 과정) (0) | 2025.02.09 |
Jetpack Room의 TypeConverter 적용하기 (0) | 2025.02.05 |
Flow debounce와 프로젝트 적용(검색 기능 api 호출 최적화) (1) | 2025.02.01 |
Paging 학습 및 프로젝트 적용 (1) | 2025.01.28 |