개발일기

안드로이드 네트워크 + 로컬 캐싱 전략 (Room + Flow)

뱅우 2025. 9. 11. 09:41
반응형
안드로이드 네트워크 + 로컬 캐싱 전략 (Room + Flow)

네트워크 요청만으로 데이터를 처리하면 속도 저하오프라인 이슈가 발생할 수 있습니다. 이런 문제를 해결하기 위해 로컬 캐싱을 도입하면 앱의 안정성과 사용자 경험이 크게 향상됩니다. 이번 글에서는 Room + Flow를 활용해 네트워크와 로컬 캐시를 결합하는 방법을 소개합니다.


1. 로컬 캐싱의 필요성

  • 네트워크 불안정 시에도 데이터 유지
  • 앱 시작 속도 개선
  • 서버 트래픽 절감

2. Room 데이터베이스 설정

Room은 SQLite 기반의 ORM 라이브러리로, Flow와 결합하면 자동 데이터 갱신이 가능합니다.

@Entity
data class UserEntity(
    @PrimaryKey val id: Int,
    val name: String
)

@Dao
interface UserDao {
    @Query("SELECT * FROM UserEntity")
    fun getUsers(): Flow<List<UserEntity>>

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insertUsers(users: List<UserEntity>)
}

3. Repository에서 네트워크 + 캐시 통합

Repository에서 네트워크 → Room 저장 → Flow 구독 순으로 데이터 흐름을 설계합니다.

class UserRepository(
    private val api: ApiService,
    private val userDao: UserDao
) {
    fun getUsers(): Flow<List<UserEntity>> = flow {
        // 1. 먼저 로컬 캐시 emit
        emitAll(userDao.getUsers())

        // 2. 네트워크에서 최신 데이터 가져오기
        val remoteUsers = api.getUsers()
        userDao.insertUsers(remoteUsers.map { UserEntity(it.id, it.name) })
    }
}

이렇게 하면 앱이 시작될 때 Room에 저장된 기존 데이터를 즉시 보여주고, 이후 네트워크에서 새 데이터를 가져와 자동으로 UI에 반영합니다.


4. Compose UI에서 Flow 구독

ViewModel을 통해 Flow를 UI에서 관찰합니다.

@Composable
fun UserScreen(viewModel: UserViewModel) {
    val users by viewModel.users.collectAsState(initial = emptyList())

    LazyColumn {
        items(users) { user ->
            Text(user.name)
        }
    }
}

class UserViewModel(private val repository: UserRepository) : ViewModel() {
    val users = repository.getUsers()
}

5. 캐싱 전략 요약

  1. Room DB와 DAO 정의
  2. 네트워크 + Room을 Repository에서 결합
  3. UI는 Flow를 구독해 자동으로 최신 상태 표시

6. 결론

네트워크 + 로컬 캐싱 전략을 적용하면 오프라인에서도 데이터가 유지되고, 사용자에게 더 빠른 응답을 제공할 수 있습니다. 특히 Room과 Flow를 함께 사용하면 데이터 동기화를 쉽게 구현할 수 있습니다.

다음 글 예고: 다음 글에서는 Compose + Paging 3로 무한 스크롤 구현하기를 다뤄보겠습니다.


반응형