일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- Button
- drift
- Flutter
- TEST
- LifeCycle
- scroll
- appbar
- 안드로이드
- data
- Coroutines
- 앱바
- Kotlin
- ScrollView
- livedata
- Dialog
- CustomScrollView
- intent
- textview
- DART
- tabbar
- Navigation
- 테스트
- textfield
- android
- activity
- viewmodel
- 앱
- Compose
- 계측
- binding
- Today
- Total
Study Record
[안드로이드] Repository pattern 과 Caching 본문
😶 Repository Pattern (레파지토리 패턴) 개요
레파지토리 패턴이란 디자인 패턴 중 하나로 앱의 나머지 부분으로부터 데이터 계층을 분리하는 패턴이다. 앱 아키텍처에서 권장하는 지침에 따르면, 사용자와 상호작용하며 화면을 그리거나 이벤트를 캡처하는 UI 관련된 모든 것을 제어하는 UI 계층(UI Layer)과 데이터와 관련된 작업을 하는 데이터 계층이 있다.
데이터 계층에서 데이터 유형 별로 Repository 클래스를 만들어 데이터 원본(웹 서비스, 캐시 등) 간의 충돌을 해결하고 데이터에 대한 변경 사항을 중앙 집중화할 수 있다. Repository 클래스에는 데이터를 가져오거나 처리하는 작업과 외부에서 데이터 엑세스에 대한 간결한 API 를 제공한다. ViewModel 에서 모든 데이터와 관련된 작업을 실행하지 않고 Repository 와 작업을 나눠 역할을 나누고 코드를 분리하는 것이 아키텍처에 권장되는 모법사례이다.
😶 Repository 장점
데이터를 가져올 때 하나의 서버를 사용한다면 그 서버에서 데이터를 가져와 단순히 사용자에게 보여줄 수 있다. 단순히 이런 작업은 네트워크에 이상이 생겼을 때 아무런 작업을 하지 못한 채 사용자에게 오류화면을 보여줄 수밖에 없다. 여기서 "캐시" 가 유용하게 활용될 수 있다. 한 번 서버에서 가져온 데이터를 데이터베이스라는 임시 저장소(캐시)에 저장했었더라면 네트워크에 이상이 생겼을 때, 데이터베이스에서 임시 저장된 데이터를 사용자에게 보여줄 수 있다.
또한 여러 서버에서 데이터를 가져온다던지, 데이터를 가져오는 코드만 변경해야 할 때, Repository 가 따로 있으면 코드를 수정하고 테스트하는 것이 쉬울 것이다.
😶 Caching
캐시는 앱에서 사용되는 데이터 저장소를 말한다. 예를 들어, 네트워크를 통해 가져온 데이터를 임시로 저장했다가 네트워크를 사용할 수 없을 때 캐시된 데이터에 의존하면서 데이터를 지속하는 데에 사용된다. 캐시는 특정 작업에 따라 다양한 형태를 취할 수 있으며, 간단하거나 복잡할 수 있다.
안드로이드에서 네트워크 캐싱을 구현하는 몇가지를 소개하자면 다음과 같다.
Retrofit 라이브러리 | 모든 네트워크 결과의 복사본을 로컬에 저장하도록 Retrofit을 구성할 수 있다. |
Data Store | 간단한 key-value 쌍으로 데이터를 저장할 수 있다. 많고 복잡한 양의 데이터에는 적합하지 않다. |
앱 내부 저장소 디렉터리 | 앱 내부 저장소 디렉터리에 접근하여 파일 시스템의 형식으로 데이터를 저장할 수 있다. |
Room | 복잡한 구조로 쿼리할 수 있는 데이터를 저장할 수 있으며 SQLite 데이터베이스에 저장된다. |
😶 Repository & Caching 예시
Retrofit 라이브러리를 사용하여 서버에 데이터를 가져오는 과정이 포함된 앱을 예시로 들어보면, 데이터에 대한 캐싱 과정 없이 서버에 데이터를 요청하고 응답받은 데이터를 사용자에게 보여준다.
네트워크 관련 코드이다.
/**
* A retrofit service to fetch a devbyte playlist.
*/
interface DevbyteService {
@GET("devbytes")
suspend fun getPlaylist(): NetworkVideoContainer
}
/**
* Main entry point for network access. Call like `DevByteNetwork.devbytes.getPlaylist()`
*/
object DevByteNetwork {
// Configure retrofit to parse JSON and use coroutines
private val retrofit = Retrofit.Builder()
.baseUrl("https://android-kotlin-fun-mars-server.appspot.com/")
.addConverterFactory(MoshiConverterFactory.create())
.build()
val devbytes = retrofit.create(DevbyteService::class.java)
}
ViewModel 에서 서버에서 데이터를 가져오는 부분이다. 서버에 응답받은 데이터를 _playlist 에 저장한다.
class DevByteViewModel(application: Application) : AndroidViewModel(application) {
private val _playlist = MutableLiveData<List<DevByteVideo>>()
val playlist: LiveData<List<DevByteVideo>>
get() = _playlist
init {
refreshDataFromNetwork()
}
// 데이터 가져오기
private fun refreshDataFromNetwork() = viewModelScope.launch {
try {
val playlist = DevByteNetwork.devbytes.getPlaylist()
_playlist.postValue(playlist.asDomainModel())
} catch (networkError: IOException) {
}
}
// ...
}
서버에서 받은 데이터를 데이터베이스에 캐싱하여 인터넷에 문제가 있을 때 데이터베이스에 있는 임시 데이터를 활용하는 방향으로 코드를 수정하면 다음과 같다.
먼저, 데이터베이스 DAO 클래스와 Entity 이다.
@Entity
data class DatabaseVideo constructor(
@PrimaryKey
val url: String,
val updated: String,
val title: String,
val description: String,
val thumbnail: String)
@Dao
interface VideoDao {
@Query("select * from databasevideo")
fun getVideos(): LiveData<List<DatabaseVideo>>
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertAll( videos: List<DatabaseVideo>)
}
Repository 코드 부분이다. ViewModel 에서 refreshVideos() 메서드로 데이터를 업그레이드하고 videos 변수로 데이터를 가져온다.
class VideosRepository(private val database: VideosDatabase) {
// 데이터베이스에서 임시 데이터를 가져온다.
val videos: LiveData<List<DevByteVideo>> = Transformations.map(database.videoDao.getVideos()) {
it.asDomainModel()
}
/**
* 네트워크로부터 playlist를 패치한 뒤, playlistf를 데이터베이스에 저장한다.
*/
suspend fun refreshVideos() {
withContext(Dispatchers.IO) {
val playlist = DevByteNetwork.devbytes.getPlaylist()
database.videoDao.insertAll(playlist.asDatabaseModel())
}
}
}
수정한 ViewModel 이다. Repository 의 videos 변수로 데이터를 가져오고 refreshVideos() 메서드로 업그레이드한다.
class DevByteViewModel(application: Application) : AndroidViewModel(application) {
/**
* private val _playlist = MutableLiveData<List<DevByteVideo>>()
* val playlist: LiveData<List<DevByteVideo>>
* get() = _playlist
*/
private val videosRepository = VideosRepository(getDatabase(application))
// 데이터 가져오기
val playlist = videosRepository.videos
init {
refreshDataFromNetwork()
}
// 데이터 업그레이드
private fun refreshDataFromNetwork() = viewModelScope.launch {
try {
videosRepository.refreshVideos()
} catch (networkError: IOException) {
}
}
// ...
}
'안드로이드' 카테고리의 다른 글
[Android] 앱 아키텍처 (0) | 2023.08.25 |
---|---|
[안드로이드] DataSotre (SharedPreferences 대체) (0) | 2023.08.21 |
[안드로이드] Room (database) 살펴보기 (ViewModel, Flow, ListAdapter, LiveData, Coroutines) (0) | 2023.08.19 |
[안드로이드] RecyclerView ListAdapter 살펴보기 (0) | 2023.08.10 |
[안드로이드] binding Adapter (결합 어댑터) (0) | 2023.08.10 |