
지난 포스팅에서 RxJava로 naver api를 이용하여 영화 검색 기능을 만들어 보았다.
2021.03.22 - [Android] - [Android] Naver검색 api - RxJava, Retrofit, MVVM, Hilt
이번에는 영화를 검색했을 때 최근 검색어 자동저장을 Room을 이용해 만들어보았다.
이번에도 RxJava로 구현을 하려고 했으나 먼저 Coroutine으로 작성하는 법을 포스팅하고 RxJava를 더 공부한 다음 리팩토링 하고자 한다.
1) build.gradle(module: app) dependency 추가
- 다음과 같이 Room 의존성을 추가해준다.
ext {
retrofit_version = "2.9.0"
lifecycle_version = "2.2.0"
room_version = "2.3.0-alpha04"
}
//Room
implementation "androidx.room:room-runtime:$room_version"
kapt "androidx.room:room-compiler:$room_version"
implementation "androidx.room:room-rxjava2:$room_version"
implementation "androidx.room:room-ktx:$room_version"
2) Entity 생성
- Room을 이용하여 테이블에 데이터를 저장하기 위해 Entity를 만들어준다.
- Entity 어노테이션을 통해서 table name을 지정해주고 저장하고자 하는 데이터 형식대로 데이터 클래스를 만들어준다.
- PrimaryKey는 참조를 위해서 필수적으로 존재해야한다.
(autoGenerate = true)로 설정을 하면 Entity가 새로 생길 때마다 Primary key는 자동으로 생성된다.
- ColumnInfo 어노테이션을 통해서 원하는 컬럼명을 지정해줄 수 있다.
@Entity(tableName = "search_history")
data class SearchEntity(
@PrimaryKey(autoGenerate = true)
val id: Int? = null,
@ColumnInfo(name = "search_query")
val searchQuery: String
)
3) Dao 작성
- Entity로 설정한 테이블에 대하여 쿼리문을 작성하는 인터페이스이다.
- Coroutine을 사용하기 위해서 dao내의 함수는 suspend function을 작성해준다.
- onConflict는 데이터베이스 충돌이 날 경우 어떻게 처리할 것인지를 명시하는 속성이다.
- 다음과 같이 데이터를 가져올 때는 Query문에 해당하는 쿼리문을 넣어주고, 데이터를 삽입할 때는 Insert, 삭제는 Delete를 사용한다.
@Dao
interface SearchDao {
@Query("SELECT * FROM search_history")
suspend fun getAll(): List<SearchEntity>
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(searchList: SearchEntity)
@Delete
suspend fun delete(searchEntity: SearchEntity)
}
4) Repository 생성
- Hilt를 통해 앞서 만들었던 Dao를 주입받아 Repository를 생성한다.
- 이 때도 마찬가지로 Coroutine에서 실행될 함수이기에 suspend function으로 작성해준다.
class SearchHistoryRepository @Inject constructor(
private val searchDao: SearchDao
){
suspend fun insert(searchEntity: SearchEntity) = searchDao.insert(searchEntity)
suspend fun getAll() = searchDao.getAll()
suspend fun delete(searchEntity: SearchEntity) = searchDao.delete(searchEntity)
}
5) Database 생성
- Database는 Room을 실제로 구현하는 부분이다.
- RoomDatabase 인스턴스를 만드는 작업은 매우 비싼 작업이기 때문에 싱글톤으로 만드는 것을 권장하고 있다.
- 먼저 RoomDatabase를 상속받는 추상클래스로 만들어주고 @Database 어노테이션을 달아준다.
- 이후 사용할 Entity를 배열형태로 입력해준다.
@Database(entities = [SearchEntity::class], version = 1, exportSchema = false)
abstract class AppDatabase : RoomDatabase() {
abstract fun getSearchDao(): SearchDao
}
6) Hilt Module
- Dao, Database, Repository 객체들은 다음과 같이 같이 외부에서 만들어 주입해주었다.
@Module
@InstallIn(ApplicationComponent::class)
object DatabaseModule {
@Provides
fun provideSearchDao(appDatabase: AppDatabase) = appDatabase.getSearchDao()
@Provides
@Singleton
fun provideAppDatabase(@ApplicationContext context: Context) =
Room.databaseBuilder(context, AppDatabase::class.java, "app database")
.fallbackToDestructiveMigration()
.build()
@Provides
@Singleton
fun provideSearchHistoryRespository(searchDao: SearchDao): SearchHistoryRepository =
SearchHistoryRepository(searchDao)
}
7) ViewModel에서 데이터 조회 및 삽입 구현
class SearchMovieViewModel @ViewModelInject constructor(
private val searchMovieRepository: SearchMovieRepository,
private val searchHistoryRepository: SearchHistoryRepository
) : ViewModel() {
private val _movieList = MutableLiveData<MovieResponseModel>()
val movieList: LiveData<MovieResponseModel>
get() = _movieList
val searchQuery: MutableLiveData<String> = MutableLiveData<String>()
private val _genreMovie = MutableLiveData<Int>()
val genreMovie: LiveData<Int>
get() = _genreMovie
private val _country = MutableLiveData<String>()
val country: LiveData<String>
get() = _country
private val _queryList = MutableLiveData<List<SearchEntity>>()
val queryList: LiveData<List<SearchEntity>>
get() = _queryList
init {
getSearchHistory()
}
fun changeCountryFilter(country: String) {
_country.value = country
}
@SuppressLint("CheckResult")
fun getMovieList() {
searchMovieRepository.getMovieList(
searchQuery.value!!,
country.value
).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
_movieList.postValue(it)
insertHistory()
getSearchHistory()
}, {
Log.e("error :", it.stackTraceToString())
})
}
private fun insertHistory() {
viewModelScope.launch {
searchHistoryRepository.insert(
SearchEntity(
null,
searchQuery.value!!
)
)
}
}
fun getSearchHistory() {
viewModelScope.launch {
_queryList.postValue(searchHistoryRepository.getAll())
}
}
}'Android' 카테고리의 다른 글
| [Android] Paging3 Library in Jetpack (0) | 2021.04.05 |
|---|---|
| [Android] RxJava로 Room구현하기 (0) | 2021.03.26 |
| [Android] Naver검색 api - RxJava, Retrofit, MVVM, Hilt (0) | 2021.03.22 |
| [Android] Naver Map API 띄우기 (kotlin) (0) | 2021.03.19 |
| Navigation으로 Fragment를 전환시켜보자! (0) | 2021.03.10 |