Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[AN/USER] 축제 목록 캐싱 적용 (#591) #592

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions android/festago/app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,11 @@ dependencies {

// splash
implementation("androidx.core:core-splashscreen:1.1.0-alpha02")

// room
implementation("androidx.room:room-runtime:2.6.0")
implementation("androidx.room:room-ktx:2.6.0")
kapt("androidx.room:room-compiler:2.6.0")
}

fun getSecretKey(propertyKey: String): String {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.festago.festago.data.dao

import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase

@Database(
entities = [
FestivalEntity::class,
],
version = 1,
exportSchema = true,
)
abstract class FestagoDatabase : RoomDatabase() {
abstract fun festivalDao(): FestivalDao

companion object {
private const val DATABASE_NAME = "festago.db"

fun buildDatabase(context: Context): FestagoDatabase {
return Room.databaseBuilder(
context,
FestagoDatabase::class.java,
DATABASE_NAME,
).build()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.festago.festago.data.dao

import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import kotlinx.coroutines.flow.Flow

@Dao
interface FestivalDao {
@Query("SELECT * FROM festivals")
fun getFestivals(): Flow<List<FestivalEntity>>

@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertFestivals(festivals: List<FestivalEntity>): List<Long>

@Query("DELETE FROM festivals")
fun clearFestivals()

fun replaceFestivals(festivals: List<FestivalEntity>): List<Long> {
clearFestivals()
return insertFestivals(festivals)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.festago.festago.data.dao

import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity(tableName = "festivals")
data class FestivalEntity(
@PrimaryKey
val id: Int,
val schoolId: Int,
val name: String,
val startDate: String,
val endDate: String,
val thumbnail: String,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.festago.festago.data.dao.mapper

import com.festago.festago.data.dao.FestivalEntity
import com.festago.festago.model.Festival
import java.time.LocalDate

fun List<FestivalEntity>.toDomain() = map(FestivalEntity::toDomain)

fun FestivalEntity.toDomain() = Festival(
id = id.toLong(),
name = name,
startDate = LocalDate.parse(startDate),
endDate = LocalDate.parse(endDate),
thumbnail = thumbnail,
)

fun List<Festival>.toEntity() = map(Festival::toEntity)

fun Festival.toEntity() = FestivalEntity(
id = this.id.toInt(),
schoolId = -1,
name = this.name,
startDate = this.startDate.toString(),
endDate = this.endDate.toString(),
thumbnail = this.thumbnail,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.festago.festago.data.di.singletonscope

import android.content.Context
import com.festago.festago.data.dao.FestagoDatabase
import com.festago.festago.data.dao.FestivalDao
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent

@InstallIn(SingletonComponent::class)
@Module
object DaoModule {
@Provides
fun provideFestivalDao(@ApplicationContext context: Context): FestivalDao =
FestagoDatabase.buildDatabase(context).festivalDao()
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,37 @@
package com.festago.festago.data.repository

import com.festago.festago.data.dao.FestivalDao
import com.festago.festago.data.dao.mapper.toDomain
import com.festago.festago.data.dao.mapper.toEntity
import com.festago.festago.data.service.FestivalRetrofitService
import com.festago.festago.data.util.onSuccessOrCatch
import com.festago.festago.data.util.runCatchingResponse
import com.festago.festago.model.Festival
import com.festago.festago.model.Reservation
import com.festago.festago.repository.FestivalRepository
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.transform
import kotlinx.coroutines.launch
import javax.inject.Inject

class FestivalDefaultRepository @Inject constructor(
private val festivalDao: FestivalDao,
private val festivalRetrofitService: FestivalRetrofitService,
) : FestivalRepository {
override val festivals: Flow<List<Festival>>
get() = festivalDao.getFestivals().transform { emit(it.toDomain()) }

override suspend fun loadFestivals(): Result<List<Festival>> =
runCatchingResponse { festivalRetrofitService.getFestivals() }
.onSuccessOrCatch { it.toDomain() }
.onSuccessOrCatch { festivals ->
festivals.toDomain().also {
CoroutineScope(Dispatchers.IO).launch {
festivalDao.replaceFestivals(it.toEntity())
}
}
}
Comment on lines +23 to +34
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

서버에 새 데이터를 요청해서 불러오는 동안에 캐싱해둔 데이터를 보여줄 수 있고 좋네요 👍
(몰라서 하는 질문) 매번 내장 DB 테이블 전체를 Delete하고 Insert 하는 건 성능상 문제가 없나요??


override suspend fun loadFestivalDetail(festivalId: Long): Result<Reservation> =
runCatchingResponse { festivalRetrofitService.getFestivalDetail(festivalId) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,26 +28,34 @@ class FestivalListViewModel @Inject constructor(
private val _event = MutableSharedFlow<FestivalListEvent>()
val event: SharedFlow<FestivalListEvent> = _event.asSharedFlow()

init {
viewModelScope.launch {
festivalRepository.festivals.collect {
_uiState.value = FestivalListUiState.Success(
festivals = it.map { festival ->
FestivalItemUiState(
id = festival.id,
name = festival.name,
startDate = festival.startDate,
endDate = festival.endDate,
thumbnail = festival.thumbnail,
onFestivalDetail = ::showTicketReserve,
)
Comment on lines +34 to +43
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

개인적으로 private 함수로 분리했으면 좋겠습니당

},
)
}
}
}

fun loadFestivals() {
viewModelScope.launch {
festivalRepository.loadFestivals()
.onSuccess {
_uiState.value = FestivalListUiState.Success(
festivals = it.map { festival ->
FestivalItemUiState(
id = festival.id,
name = festival.name,
startDate = festival.startDate,
endDate = festival.endDate,
thumbnail = festival.thumbnail,
onFestivalDetail = ::showTicketReserve,
)
},
)
}.onFailure {
_uiState.value = FestivalListUiState.Error
analyticsHelper.logNetworkFailure(KEY_LOAD_FESTIVALS_LOG, it.message.toString())
}
festivalRepository.loadFestivals().onFailure {
_uiState.value = FestivalListUiState.Error
analyticsHelper.logNetworkFailure(
KEY_LOAD_FESTIVALS_LOG,
it.message.toString(),
)
}
Comment on lines +52 to +58
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Room 써서 캐싱까지 할거면 Error 상태일 때도 캐싱된 데이터를 보여주면 좋을 것 같은데 이 부분 상태나 이벤트 처리 어떻게 할지 얘기 해봐야할 것 같아요!

}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@ package com.festago.festago.repository

import com.festago.festago.model.Festival
import com.festago.festago.model.Reservation
import kotlinx.coroutines.flow.Flow

interface FestivalRepository {
val festivals: Flow<List<Festival>>

suspend fun loadFestivals(): Result<List<Festival>>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

실패 처리만 하고있어서 Result 만 반환해도 될 것 같네요

suspend fun loadFestivalDetail(festivalId: Long): Result<Reservation>
}
Loading