| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 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 |
| 31 |
- compose
- 코루틴
- 팩토리 메소드
- kotlin multiplatform
- 디자인패턴
- Coroutines
- material3
- 코틀린멀티플랫폼
- 빌터패턴
- 프로토타입 패턴
- 함수형프로그래밍
- 추상 팩토리
- factory method
- builderPattern
- ㅋㅁ
- 옵저버 패턴
- android designsystem
- designPattern
- Abstract Factory
- 안드로이드 디자인시스템
- Functional Programming
- define
- Kotlin
- kmp
- 추상팩토리패턴
- 디자인패턴 #
- 코틀린
- Observer Pattern
- Design Pattern
- PrototypePattern
- Today
- Total
오늘도 더 나은 코드를 작성하였습니까?
안드로이드 개발에서 사용하는 캐싱 전략1 본문
Cache란?
캐시(Cache)는 자주 사용되는 데이터를 빠르게 접근할 수 있는 임시 저장 공간에 보관하는 기술입니다. 원본 데이터 소스(디스크, 네트워크, DB 등)보다 접근 속도가 빠른 곳(메모리 등)에 데이터를 두어 반복적인 요청의 응답 시간을 단축시킵니다. Android에서는 LruCache(메모리 캐시), OkHttp Cache(네트워크 응답 캐시), Room 등을 활용해 성능과 사용자 경험을 개선합니다.
in-memory cache
In-memory cache는 앱 프로세스의 RAM에 데이터를 보관하는 방식으로, 접근 속도가 매우 빠르지만 앱 종료나 OS의 메모리 회수 시 사라집니다. 용도에 따라 적절한 도구를 선택하는 것이 핵심입니다.
1. 이미지 캐싱 (Coil / Glide)
- 이미지는 크기가 크고 디코딩 비용이 비싸기 때문에 전용 라이브러리를 쓰는 것이 표준입니다. 두 라이브러리 모두 메모리 캐시 + 디스크 캐시 2단계를 자동으로 관리합니다.
Coil
// 기본 사용 - 캐시 자동 처리
AsyncImage(model = url, contentDescription = null)
// 전역 설정
val imageLoader = ImageLoader.Builder(context)
.memoryCache {
MemoryCache.Builder(context)
.maxSizePercent(0.25) // 가용 메모리의 25%
.build()
}
.build()
// 캐시 정책 개별 제어
imageView.load(url) {
memoryCachePolicy(CachePolicy.ENABLED)
diskCachePolicy(CachePolicy.ENABLED)
}
Glide
Glide.with(context)
.load(url)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.skipMemoryCache(false)
.into(imageView)
2. Coroutine / Flow 기반 패턴
라이브러리 없이 직접 캐시 레이어를 만들 때 자주 쓰는 패턴. API 호출시에 사용한다.
class UserRepository {
private val _users = MutableStateFlow<Map<String, User>>(emptyMap())
val users: StateFlow<Map<String, User>> = _users.asStateFlow()
suspend fun getUser(id: String): User {
return _users.value[id] ?: api.fetchUser(id).also { user ->
_users.update { it + (id to user) }
}
}
}
3. Paging3 - cachedIn
cachedIn(scope)의 scope가 캐시의 생명주기를 결정한다. 일반적으로 viewModelScope 캐싱한다.
Configuration Change 시 처음부터 다시 로드
사용자가 100번째 아이템까지 스크롤한 상태에서 화면을 회전할때 cachedIn 없다면
- Activity가 재생성됨
- Fragment/Activity/Compose가 다시 collectLatest로 구독
- cold flow이므로 PagingSource가 처음부터 다시 실행
- 1페이지부터 다시 로드, 사용자는 맨 위로 돌아감
cachedIn(viewModelScope)로 사용
- Activity가 재생성됨
- ViewModel은 살아있고, 캐시된 PagingData도 메모리에 있음
- 새 구독자는 이미 로드된 100개 데이터를 그대로 받음
class ProductViewModel(repo: ProductRepository) : ViewModel() {
val products: Flow<PagingData<Product>> = Pager(
config = PagingConfig(pageSize = 20),
pagingSourceFactory = { repo.getProductPagingSource() }
).flow
.cachedIn(viewModelScope) // ← 핵심
}
여러 구독자가 있으면 크래시
같은 Flow<PagingData>를 두 곳에서 구독하면 Paging3는 런타임 예외를 던집니다.
cachedIn은 내부적으로 SharedFlow 형태로 변환해서 여러 구독자가 같은 데이터를 공유할 수 있게 만들어줍니다.
// ⚠️ cachedIn 없이
viewModel.products.collectLatest { adapter1.submitData(it) }
viewModel.products.collectLatest { adapter2.submitData(it) } // 💥 크래시
viewModelScope에 캐싱했기 때문에 ViewModel이 살아있는 동안 PagingData가 메모리에 유지됩니다. ViewModel이 onCleared()로 사라지면 캐시도 함께 정리되어 메모리 누수가 없습니다.
DataStore(Sharedpreferences) cache
DataStore는 SharedPreferences의 후속으로, 작은 키-값 데이터를 비동기로 안전하게 저장하기 위한 라이브러리입니다.
캐시라고 부르긴 하지만 실제로는 영속 저장소이며, 그 특성을 이해해야 어떤 데이터를 둘지 결정할 수 있습니다.
| 특성 | 내용 |
| 저장 방식 | 파일 기반 (Preferences는 XML 유사, Proto는 바이너리) |
| 접근 | Flow 기반 비동기 |
| 트랜잭션 | O (원자적 업데이트 보장) |
| 크기 한계 | 전체 파일을 메모리에 로드 — 작아야 함 |
| 쿼리 | 불가 (전체 읽기만) |
| 동시성 | Coroutine |
사용자 환경설정
val Context.settingsDataStore by preferencesDataStore("settings")
object SettingsKeys {
val DARK_MODE = booleanPreferencesKey("dark_mode")
val LANGUAGE = stringPreferencesKey("language")
val FONT_SCALE = floatPreferencesKey("font_scale")
val NOTIFICATION_ENABLED = booleanPreferencesKey("notification_enabled")
}
- 다크모드, 언어, 폰트 크기, 알림 on/off
- 화면별 UI 옵션상태 (탭 선택, 정렬 순서, 필터 옵션)
인증/세션 메타데이터
object AuthKeys {
val ACCESS_TOKEN = stringPreferencesKey("access_token")
val REFRESH_TOKEN = stringPreferencesKey("refresh_token")
val TOKEN_EXPIRES_AT = longPreferencesKey("token_expires_at")
val USER_ID = stringPreferencesKey("user_id")
}
- 토큰, 토큰 만료 시간, 로그인 상태
- 토큰은 EncryptedSharedPreferences 또는 Android Keystore로 암호화 저장 권장.다.
앱 상태 플래그
- 온보딩 완료 여부 (최초 앱실행)
- 튜토리얼 표시 여부
- "이 메시지 다시 보지 않기" 플래그
- 마지막 앱 버전 (마이그레이션 트리거용)
- 첫 실행 여부
