Skip to content

warrenth/Mocksy

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

41 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

DeepDive

Architecture: MVVM, Clean Architecture, Multi-Module
UI: Compose, SharedTransition, Design System
Testing & Code Quality: JUnit, MockK, Turbine, Spotless, MockAPI.io

๐Ÿ“ท Previews

drawing

s

1. core:navigation

1.1 ๋ฉ€ํ‹ฐ ๋ชจ๋“ˆ Compose ์—์„œ ํ™”๋ฉด๊ฐ„ ๋ฐ์ดํ„ฐ ์ „๋‹ฌ ๋ฐฉ๋ฒ•. Navigation ์‚ฌ์šฉ๋ฒ•

1.1.1 ๊ฐ์ฒด๋ฅผ Navigation์— ์ง์ ‘ ์ „๋‹ฌ (NavType ์‚ฌ์šฉ)

@Serializable
data class Detail(val article: Article) : RouteScreen() {
    companion object {
        val typeMap = mapOf(typeOf<Detail>() to ArticlesType)
    }
}
object ArticlesType : NavType<Article>(isNullableAllowed = false) {
    override fun put(bundle: Bundle, key: String, value: Article) {
        bundle.putParcelable(key, value)
    }

    override fun get(bundle: Bundle, key: String): Article? =
        BundleCompat.getParcelable(bundle, key, Article::class.java)

    override fun parseValue(value: String): Article =
        Json.decodeFromString(Uri.decode(value))

    override fun serializeAsValue(value: Article): String =
        Uri.encode(Json.encodeToString(value))
}

์žฅ์ : ์ปดํŒŒ์ผ ํƒ€์ž„์— ํƒ€์ž… ์ฒดํฌ ๊ฐ€๋Šฅ (ํƒ€์ž… ์•ˆ์ „์„ฑ), ๊ฐ์ฒด ์ง๋ ฌํ™”๋ฅผ ํ†ตํ•ด route์— ์ง์ ‘ encode ๊ฐ€๋Šฅ
๋‹จ์ : ๊ฐ์ฒด๋งˆ๋‹ค NavType ํด๋ž˜์Šค๋ฅผ ๋”ฐ๋กœ ๊ตฌํ˜„ํ•ด์•ผ ํ•จ, ์œ ์ง€๋ณด์ˆ˜, ์ฝ”๋“œ๋Ÿ‰ ์ฆ๊ฐ€

1.1.2 ID ๊ฐ’๋งŒ ์ „๋‹ฌํ•˜๊ณ , Detail ํ™”๋ฉด์—์„œ ViewModel๋กœ ๋‹ค์‹œ ์กฐํšŒ

์žฅ์ : NavType์„ ๋งŒ๋“ค ํ•„์š” ์—†์Œ, ๊ฒฝ๋กœ๊ฐ€ ๊ฐ€๋ณ๊ณ  ๋ช…ํ™•ํ•จ, Deep link ์ง€์›์ด ์‰ฌ์›€
๋‹จ์ : ํ™”๋ฉด์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค์‹œ ์กฐํšŒํ•ด์•ผ ํ•จ, (cache๊ตฌํ˜„ or API)

1.1.3 SharedViewModel ์‚ฌ์šฉ (์ƒํƒœ ๊ณต์œ )

์žฅ์ : NavArgs ์—†์ด ๋ณต์žกํ•œ ๊ฐ์ฒด ์ „๋‹ฌ ๊ฐ€๋Šฅ, ๋น ๋ฆ„ (๋ฉ”๋ชจ๋ฆฌ์—์„œ ์ง์ ‘ ๊ฐ€์ ธ์˜ด)
๋‹จ์ : ViewModel ์ƒ๋ช…์ฃผ๊ธฐ๋ฅผ ์ž˜ ๊ด€๋ฆฌํ•ด์•ผ ํ•จ, ํ™”๋ฉด ๋ณต์›, deep link ์ฒ˜๋ฆฌ ์–ด๋ ค์›€

1.1.4 ๋‹จ์ˆœํ•œ ๋ฐ์ดํ„ฐ (String, Int ๋“ฑ)๋Š” ๊ธฐ๋ณธ NavArgs ์‚ฌ์šฉ

NavHost(navController = navHostController, startDestination = "home") {
    composable("home") {
        HomeScreen(
            onNavigateToDetail = { id, title ->
                navHostController.navigate("detail/$id/$title")
            }
        )
    }
    composable(
        route = "detail/{id}/{title}",
        arguments = listOf(
            navArgument("id") { type = NavType.IntType },
            navArgument("title") { type = NavType.StringType }
        )
    ) {
        val id = it.arguments?.getInt("id")
        val title = it.arguments?.getString("title")
        DetailScreen(id = id, title = title)
    }
}

์žฅ์ : ์„ค์ •์ด ๊ฐ„๋‹จํ•˜๊ณ  ์ง๊ด€์ , ๊ธฐ๋ณธ ํƒ€์ž…์€ NavType ์ž๋™ ์ง€์›
๋‹จ์ : ๋ณต์žกํ•œ ๊ฐ์ฒด๋Š” ์ „๋‹ฌํ•  ์ˆ˜ ์—†์Œ

๐Ÿ“Œ ์ƒํ™ฉ์— ๋”ฐ๋ผ ์ถ”์ฒœ๋˜๋Š” ๋ฐฉ์‹

  • ๋‹จ์ˆœ ํƒ€์ž…(String, Int ๋“ฑ) ์ „๋‹ฌ -> ๊ธฐ๋ณธ NavArgs ์‚ฌ์šฉ
  • ๊ฐ์ฒด ์ „๋‹ฌ & deep link ํ•„์š” -> NavType + Serializable ์‚ฌ์šฉ
  • ID ๊ธฐ๋ฐ˜ ์กฐํšŒ ๊ตฌ์กฐ -> ID๋งŒ ๋„˜๊ธฐ๊ณ  ViewModel ์—์„œ ์กฐํšŒ (์บ์‹œ or API)

2. Core:Data

2.1 ์ฝ”๋ฃจํ‹ด Dispatcher ์ „๋žต ํŠธ๋ ˆ์ด๋“œ ์˜คํ”„

2.1.1 ViewModel ์—์„œ Dispatchers.IO ์ง์ ‘ ์‚ฌ์šฉ

์žฅ์ : ๊ฐ„๋‹จํ•˜๊ณ  ์ง๊ด€์ 
๋‹จ์ : ํ…Œ์ŠคํŠธ ์‹œ Dispatcher.IO ๋Œ€์ฒด ์–ด๋ ค์›€
๋Œ€์•ˆ: Dispatchers.setMain(...) ๊ฐ™์€ ๋ณ„๋„ ์„ธํŒ… ํ•„์š”

2.1.2 ViewModelScope์—์„œ Dispatcher ์ƒ๋žต (launch {})

์žฅ์ : ์ฝ”๋“œ ๊น”๋”, Retrofit + suspend ์กฐํ•ฉ์—์„  ๋‚ด๋ถ€์ ์œผ๋กœ Dispatcher ์‚ฌ์šฉํ•˜์—ฌ ๋ณ„๋„ ์ •์˜ ํ•„์š” ์—†์Œ
๋‹จ์ : Room, ๋น„-Retrofit ์ž‘์—… ์„ ๊ฐ™์ด ์“ธ ๊ฒฝ์šฐ MainThread ์—์„œ ์‹คํ–‰ -> ANR
์ฐธ๊ณ  ์˜คํ”ˆ์†Œ์Šค : DroidKnights โ†’ ๋Œ€๋ถ€๋ถ„์˜ suspend ํ˜ธ์ถœ์—์„œ Dispatcher๋ฅผ ์ƒ๋žต

2.1.3 Dispatcher ์ฃผ์ž… + Repository ๋‚ด๋ถ€์—์„œ flowOn(...) ์‚ฌ์šฉ

internal class ArticlesRepositoryImpl @Inject constructor(
    @Dispatcher(IO) private val ioDispatcher: CoroutineDispatcher
) {
    override fun getArticles(): Flow<List<Article>> =
        flow {
            emit(api.getArticles())
        }.flowOn(ioDispatcher)
}

์žฅ์ : Dispatcher ์ฃผ์ž…์œผ๋กœ ํ…Œ์ŠคํŠธ ์‹œ ์œ ์—ฐํ•˜๊ฒŒ ๋ณ€๊ฒฝ ๊ฐ€๋Šฅ
๋‹จ์ : ์ฝ”๋“œ ์ดˆ๊ธฐ ์„ธํŒ… ํ•„์š”
์ฐธ๊ณ  ์˜คํ”ˆ์†Œ์Šค : Now in Android, Skydoves โ†’ Repository์—์„œ Dispatcher ์ฃผ์ž… ํ›„ flowOn(...) ์ฒ˜๋ฆฌ

๐Ÿ“Œ ์ƒํ™ฉ์— ๋”ฐ๋ผ ์ถ”์ฒœ ๋ฐฉ์‹

1. ๋น ๋ฅด๊ฒŒ ์•ฑ ๊ตฌ์„ฑ / Dispatcher ์ดํ•ด ๋‚ฎ์Œ ->  1๋ฒˆ ๋ฐฉ๋ฒ•  ViewModel์—์„œ Dispatchers.IO   
2. ๋Œ€๋ถ€๋ถ„ Retrofit์ด๊ณ , UI์— ์ง‘์ค‘ -> 2๋ฒˆ Dispatcher ์ƒ๋žต    
3. ํ…Œ์ŠคํŠธ, ๋ฉ€ํ‹ฐ๋ชจ๋“ˆ, ์•„ํ‚คํ…์ฒ˜ ์ค‘์š” -> โ‘ข Dispatcher ์ฃผ์ž… + flowOn ์ฒ˜๋ฆฌ    
4. (์ถ”์ฒœ) ์‹ค๋ฌด์šฉ + Dispatcher ์ฃผ์ž… ์ถ”๊ฐ€ + ํŒŒ์‹ฑ ์ตœ์ ํ™”

ViewModel.launch {                        // ์ •์˜ ์—†์Œ MainThread
    โ””โ”€โ”€ repository.getArticles()          // Flow ์‹œ์ž‘๋จ
        โ””โ”€โ”€ flowOn(IO)                    // API ํ˜ธ์ถœ IO์—์„œ ์ˆ˜ํ–‰
    โ””โ”€โ”€ .map { sort }                    
    โ””โ”€โ”€ .flowOn(Default)                  // ์ •๋ ฌ, ํŒŒ์‹ฑ์€ Default์—์„œ ์ˆ˜ํ–‰
    โ””โ”€โ”€ .collect { _uiState.value = it }  // ์ตœ์ข… UI ๋ฐ˜์˜์€ Main (๊ธฐ๋ณธ)
}

2.2 sandwich

2.2.1 ์™œ sandwich ๋ฅผ ์“ฐ๋Š”๊ฐ€?

Retrofit ์— ๋ฌธ์ œ์ 

  • try/catch + null ์ฒ˜๋ฆฌ ์ง€์˜ฅ
  • ๊ณตํ†ต ์—๋Ÿฌ ์ฒ˜๋ฆฌ
  • ๋ฐ˜๋ณต์ ์ธ if (isSuccessful) ์ฒดํฌ
// Retrofit์˜ ๊ธฐ๋ณธ Call<T> โ†’ ApiResponse<T>๋กœ ์ž๋™ ๋ž˜ํ•‘
addCallAdapterFactory(ApiResponseCallAdapterFactory.create())
// ๊ธฐ๋ณธ ํ˜•ํƒœ
suspend fun getArticles(): Response<List<Article>>

// Sandwich๊ฐ€ ๋งŒ๋“  ํ˜•ํƒœ
suspend fun getArticles(): ApiResponse<List<Article>>

2.2.2 suspendOnError ๋ž€?

์‘๋‹ต์€ ๋ฐ›์•˜์ง€๋งŒ ์‘๋‹ต์ฝ”๋“œ๊ฐ€ 200์ด ์•„๋‹Œ ๊ฒฝ์šฐ
์˜ˆ) HTTP 4xx, 5xx

2.2.3 onException ๋ž€? ์—๋Ÿฌ๋Š” ๋ถ„๊ธฐ์ฒ˜๋ฆฌํ•˜๊ณ ์‹ถ์„๋•Œ?

์„œ๋ฒ„ ์—ฐ๊ฒฐ ์‹คํŒจ, ์‘๋‹ต ํŒŒ์‹ฑ ์‹คํŒจ
์˜ˆ) ์ธํ„ฐ๋„ท ์—†์Œ (UnknownHostException)
ํƒ€์ž„์•„์›ƒ (SocketTimeoutException)
JSON ํŒŒ์‹ฑ ์‹คํŒจ (JsonParseException)

apiService.getSomething().onException {
    if (exception is UnknownHostException) {
        // ๋„คํŠธ์›Œํฌ ๋Š๊น€์ผ ๊ฒฝ์šฐ
    } else if (exception is SocketTimeoutException) {
        // ์‘๋‹ต์ด ๋„ˆ๋ฌด ๋Šฆ๋Š” ๊ฒฝ์šฐ
    }
}

2.2.4 sandwich๋Š” ํด๋ฆฐ์•„ํ‚คํ…์ฒ˜ domain ๊ณ„์ธต์— ์˜์กด์„ฑ ์ƒ๊ธธ๊นŒ?

ApiResponse๋Š” Sandwich ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ํƒ€์ž…์œผ๋กœ, data๊ณ„์ธต์— ์†ํ•จ
domain ๊ณ„์ธต์€ ์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ์˜์กดํ•˜๋ฉด ์•ˆ๋จ.
APIResponse ๋กœ ๋ฐ›์€๊ฑธ ๋ณ„๋„์˜ Result๋กœ ๋งŒ๋“ค๊ฑฐ๋‚˜ ์ˆœ์ˆ˜ ๊ฐ์ฒด๋กœ ๋„˜๊ฒจ์ค˜์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ํด๋ฆฐ์•„ํ‚คํ…์ฒ˜์—์„œ๋Š” ์ž˜ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค. ํด๋ฆฐ์•„ํ‚คํ…์ฒ˜๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ๋น ๋ฅด๊ฒŒ ์•ฑ์„ ๊ฐœ๋ฐœํ•˜๊ธฐ์—๋Š” ์ข‹์„ ์ˆ˜ ์žˆ๋‹ค.

2. Core:DesignSystem

2.1 ๋””์ž์ธ ์‹œ์Šคํ…œ

Jetpack Compose ๊ธฐ๋ฐ˜ ์•ฑ์—์„œ ๊ณตํ†ต ๋””์ž์ธ ๋ฌถ์Œ(์ƒ‰์ƒ, ๋ฐฐ๊ฒฝ, ํ…Œ๋งˆ)์„ ์ „์—ญ์ ์œผ๋กœ ๊ด€๋ฆฌํ•˜๊ณ ,
๋‹คํฌ/๋ผ์ดํŠธ ๋ชจ๋“œ ๋Œ€์‘ ๋ฐ ๋ฏธ๋ฆฌ๋ณด๊ธฐ, ํ…Œ์ŠคํŠธ, ์žฌ์‚ฌ์šฉ์„ฑ์„ ํ–ฅ์ƒ์‹œํ‚ค๊ธฐ ์œ„ํ•œ ๋””์ž์ธ ์‹œ์Šคํ…œ ๋ชจ๋“ˆ

2.1.1 ArticleColors

์•ฑ ์ „๋ฐ˜์— ์‚ฌ์šฉ๋˜๋Š” ์ƒ‰์ƒ๊ฐ’์„ ์ •์˜ํ•œ ๋ฐ์ดํ„ฐ ํด๋ž˜์Šค์ž…๋‹ˆ๋‹ค. defaultLightColors(), defaultDarkColors()๋ฅผ ํ†ตํ•ด ๋‹คํฌ/๋ผ์ดํŠธ ํ…Œ๋งˆ๋ฅผ ์ž๋™ ๋ถ„๊ธฐ

2.1.2 ArticlesBackground

๋ฐฐ๊ฒฝ ์ƒ‰์ƒ๊ณผ elevation ์ •๋ณด๋ฅผ ๋ฌถ์€ ํ…Œ๋งˆ ์ „์šฉ ํด๋ž˜์Šค์ž…๋‹ˆ๋‹ค. ๋ผ์ดํŠธ/๋‹คํฌ ๋ชจ๋“œ์— ๋”ฐ๋ผ ๋ฐฐ๊ฒฝ์„ ์ž๋™ ์„ค์ •

2.1.3 ArticleTheme

์•ฑ ์ „์ฒด ๋˜๋Š” ํŠน์ • ํ™”๋ฉด์— ํ…Œ๋งˆ๋ฅผ ์ ์šฉํ•˜๋Š” ๋ž˜ํผ ํ•จ์ˆ˜ CompositionLocalProvider ๋ฅผ ์‚ฌ์šฉํ•ด ์ƒ‰์ƒ/๋ฐฐ๊ฒฝ์„ ํ•˜์œ„ Composable ์ฃผ์ž… ์˜ˆ) ArticlesTheme.colors.backgroundLight ๋กœ Composable ์–ด๋””์„œ๋“  ์ „์—ญ์œผ๋กœ ์ ‘๊ทผ ๊ฐ€๋Šฅ

2.1.4 staticCompositionLocalOf, compositionLocalOf

val LocalBackgroundTheme = staticCompositionLocalOf { ArticleBackground() }

ArticleBackground๋Š” ๊ฑฐ์˜ ๊ณ ์ • ๊ฐ’ (์˜ˆ: ํ…Œ๋งˆ์šฉ ๋ฐฐ๊ฒฝ) ๋ณ€๊ฒฝ๋  ์ผ์ด ๊ฑฐ์˜ ์—†๊ณ , ๋ณ€๊ฒฝ๋˜๋”๋ผ๋„ UI๋ฅผ ๋ฆฌ๋ Œ๋”๋งํ•˜์ง€ ์•Š์•„๋„ ๋˜๋Š” ๊ฒฝ์šฐ Recomposition ์ตœ์ ํ™”๋จ โ†’ ์„ฑ๋Šฅ ํ–ฅ์ƒ

val LocalColors = compositionLocalOf<ArticleColors> {
    error("No colors provided")
}

ArticleColors๋Š” ๋‹คํฌ/๋ผ์ดํŠธ ๋“ฑ์œผ๋กœ ๋ฐ”๋€” ์ˆ˜ ์žˆ๋Š” ๋™์  ๊ฐ’ ์ด ๊ฐ’์ด ๋ณ€๊ฒฝ๋˜๋ฉด ํ•ด๋‹น ๊ฐ’์„ ์‚ฌ์šฉํ•œ ์ปดํฌ์ €๋ธ”๋“ค์ด ์ž๋™์œผ๋กœ Recomposition ๋จ UI๊ฐ€ ๋™์ ์œผ๋กœ ๋ฐ˜์‘ํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ ์‚ฌ์šฉ

3. Core:DesignSystem/util

3.1 LocalWindowSizeClass ๋ฅผ CompositionLocal + ๊ณตํ†ต ์œ ํ‹ธ ์ฒ˜๋ฆฌ

// Local ์ •์˜
val LocalWindowSizeClass = staticCompositionLocalOf<WindowSizeClass> {
    error("WindowSizeClass not provided")
}
// CompositionLocalProvider ์„ค์ •
val windowSizeClass = calculateWindowSizeClass(LocalContext.current as Activity)
CompositionLocalProvider(LocalWindowSizeClass provides windowSizeClass) {
    AppContent()
}
// ๋ฐ˜์‘ํ˜• ๊ณ„์‚ฐ
@Composable
fun rememberResponsiveGridCells(): GridCells {
    return when (LocalWindowSizeClass.current.widthSizeClass) {
        WindowWidthSizeClass.Compact -> GridCells.Fixed(2)
        WindowWidthSizeClass.Medium -> GridCells.Adaptive(160.dp)
        WindowWidthSizeClass.Expanded -> GridCells.Adaptive(200.dp)
        else -> GridCells.Fixed(2)
    }
}
// ์‚ฌ์šฉ ์˜ˆ์‹œ
LazyVerticalGrid(columns = rememberResponsiveGridCells()) { ... }

4. Core:Feature

4.1 SharedTransition ์ •์˜ ๋‘๊ฐ€์ง€ ๋ฐฉ๋ฒ• (์ธ์ž ์ „๋‹ฌ , ์ „์—ญ ์Šค์ฝ”ํ”„ ์ •์˜)

4.1.1 ์ธ์ž ์ „๋‹ฌ ๋ฐฉ๋ฒ•

SharedTransitionLayout
โ””โ”€โ”€ NavHost
โ”œโ”€โ”€ composable<Home>
โ”‚   โ””โ”€โ”€ HomeScreen(animatedVisibilityScope, sharedTransitionScope)
โ””โ”€โ”€ composable<Detail>
    โ””โ”€โ”€ DetailScreen(animatedVisibilityScope, sharedTransitionScope)

4.1.2 ์ „์—ญ ์Šค์ฝ”ํ”„ CompositionLocalProvider

core:navigation ์— LocalSharedTransitionScope ๋ฅผ ์ •์˜ํ•˜๊ณ  feature๊ฐ€ ์‚ฌ์šฉ. feature ๋ชจ๋“ˆ ๊ฐ„ SharedTransitionScope, AnimatedVisibilityScope ์ธ์ž๋ฅผ ๋ฐ›์ง€ ์•Š์•„ ๋ถˆํ•„์š”ํ•œ ์˜์กด์„ฑ์„ ์ค„์ธ๋‹ค. (๊ด€์‹ฌ์‚ฌ ๋ถ„๋ฆฌ)

SharedTransitionLayout
โ””โ”€โ”€ CompositionLocalProvider(LocalSharedTransitionScope)
โ””โ”€โ”€ NavHost
โ”œโ”€โ”€ composable<Home>
โ”‚   โ””โ”€โ”€ CompositionLocalProvider(LocalAnimatedVisibilityScope)
โ”‚       โ””โ”€โ”€ HomeScreen() // ๋‚ด๋ถ€์—์„œ Local*.current ๋กœ ์Šค์ฝ”ํ”„ ์ ‘๊ทผ
โ””โ”€โ”€ composable<Detail>
    โ””โ”€โ”€ CompositionLocalProvider(LocalAnimatedVisibilityScope)
        โ””โ”€โ”€ DetailScreen()

Spotless

SpotLess ์ฝ”๋“œ ํฌ๋ฉงํ„ฐ ๋„๊ตฌ Kotlin ๋“ฑ ๋‹ค์–‘ํ•œ ์–ธ์–ด์—์„œ ์ž๋™์œผ๋กœ ์ฝ”๋”ฉ ์Šคํƒ€์ผ์„ ํ†ต์ผํ•œ๋‹ค.

class SpotlessConventionPlugin : Plugin<Project> {
    override fun apply(target: Project) {
        ...
    }
}

Spotless ์„ค์ •์„ ๊ณตํ†ตํ™”ํ•ด์„œ Convention Plugin ์œผ๋กœ ์ •์˜ ๋ชจ๋“  ๋ชจ๋“ˆ์— ์ค‘๋ณต์„ ์ ๋Š” ๋Œ€์‹  plugin ํ•˜๋‚˜๋งŒ apply ํ•˜๋ฉด ๋œ๋‹ค.

์ „์ฒด ๊ฒ€์‚ฌ & ์ˆ˜์ • ./gradlew spotlessCheck ./gradlew spotlessApply

๋ชจ๋“ˆ ๋‹จ์œ„ ๊ฒ€์‚ฌ & ์ˆ˜์ • ./gradlew :feature:home:spotlessCheck ./gradlew :feature:home:spotlessApply

๊ฐœ๋ณ„๋กœ ์ˆ˜์‹œ ํฌ๋ฉง ์ž๋™ ์ ์šฉ or Action Save As ํ™œ์šฉ CI ์—์„œ๋Š” spotlessCheck ์ ์šฉ

5. Core:DataStore

5.1 SOLID ์›์น™ ์ ์šฉ

์ฑ…์ž„ ๋ถ„๋ฆฌ : core:datastore ๋Š” ๊ตฌํ˜„ ๋ฐ DI ์ œ๊ณต์˜ ์ฑ…์ž„, core:data ์ง์ ‘ ๋ฐ”์ธ๋”ฉํ•ด์„œ ์‚ฌ์šฉํ•˜๋Š” ์ฑ…์ž„
์˜์กด์„ฑ ์—ญ์ „ : core:data ๋Š” core:datastore ์˜ ์ถ”์ƒํƒ€์ž…์— ์˜์กดํ•˜๋ฉฐ ์‹ค์ œ ๊ตฌํ˜„์€ datastore์— ์œ„์น˜

5.1.1 DataStoreModule ๋ชจ๋“ˆ ๊ตฌ์„ฑ

DefaultLikedPreferencesDataStore ์—์„œ @ApplicationContext Hilt๊ฐ€ ์ฃผ์ž… ๊ฐ€๋Šฅ ํ•˜์ง€๋งŒ,
Kotlin ์ปดํŒŒ์ผ ํƒ€์ž„์— Context.dataStore์˜ ํ™•์žฅ ํ”„๋กœํผํ‹ฐ๋ฅผ ์ฝ”๋“œ๋กœ ์ƒ์„ฑํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ƒ์„ฑ์ž ๋‚ด๋ถ€์—์„œ ์‚ฌ์šฉ๋ถˆ๊ฐ€.
์ดˆ๊ธฐํ™” ํƒ€์ด๋ฐ, ์ปดํŒŒ์ผํƒ€์ž„ ์•ˆ์ „์„ฑ์„ ๊ณ ๋ คํ•˜์—ฌ DataStore ๋Š” hilt๋ฅผ ํ†ตํ•ด provide ๋กœ ์ฃผ์ž…ํ•˜๋Š”๊ฒŒ ์ข‹๋‹ค.

@Module
@InstallIn(SingletonComponent::class)
object DataStoreModule {

    private const val LIKE_DATA_STORE_NAME = "LIKE_PREFERENCES"
    
    private val Context.likeDataStore by preferencesDataStore(LIKE_DATA_STORE_NAME)  //์ปดํŒŒ์ผ ํƒ€์ž„์— ์ƒ์„ฑ

    @Provides
    @Singleton 
    fun provideLikeDataStore(
        @ApplicationContext context: Context
    ) : DataStore<Preferences> = context.likeDataStore
}

About

Multi-Module, SharedTransition, DesignSystem, Navigation, Responsive UI

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 2

  •  
  •  

Languages