[Android] Resource Provider

๐Ÿ’ก์•ˆ๋“œ๋กœ์ด๋“œ์—์„œ ๋ฆฌ์†Œ์Šค๋ฅผ ์‰ฝ๊ฒŒ ๊ด€๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•˜๋Š” Resource Provider์— ๋Œ€ํ•˜์—ฌ ๊ธฐ๋กํ•˜์˜€์Šต๋‹ˆ๋‹ค.

 

Resource Provider

  • Resource Provider๋Š” ์•ˆ๋“œ๋กœ์ด๋“œ์—์„œ ๋ฆฌ์†Œ์Šค๋ฅผ ๋” ์‰ฝ๊ฒŒ ๊ด€๋ฆฌํ•˜๊ณ  ํ…Œ์ŠคํŠธ ๊ฐ€๋Šฅ์„ฑ์„ ๋†’์ด๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•˜๋Š” ํŒจํ„ด์ž…๋‹ˆ๋‹ค.
  • XML๋ฆฌ์†Œ์Šค(๋ฌธ์ž์—ด, ์ƒ‰์ƒ, ๋ฐฐ์—ด ๋“ฑ)์™€ ๊ฐ™์€ ๋ฆฌ์†Œ์Šค๋ฅผ ์ฝ”๋“œ ๋‚ด์—์„œ ์ฃผ์ž…ํ•˜๊ณ  ViewModel์ด๋‚˜ ๋‹ค๋ฅธ ํด๋ž˜์Šค์—์„œ ์‚ฌ์šฉํ•˜๋ฉด์„œ๋„ ๋ฆฌ์†Œ์Šค์— ์ง์ ‘ ์ ‘๊ทผํ•˜์ง€ ์•Š๋„๋ก ๋งŒ๋“ค์–ด์ค๋‹ˆ๋‹ค.

ViewModel์ด๋‚˜ Repository์—์„œ๋Š” Context์— ์ง์ ‘ ์ ‘๊ทผํ•˜๋Š” ๊ฒƒ์ด ๊ถŒ์žฅ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. Resource Provider ํŒจํ„ด์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ฆฌ์†Œ์Šค์— ๋Œ€ํ•œ ์ ‘๊ทผ์„ ์ธํ„ฐํŽ˜์ด์Šค๋กœ ์ถ”์ƒํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Resource Provider์˜ ์—ญํ• 

  • Context์— ๋Œ€ํ•œ ์˜์กด์„ฑ์„ ์ค„์ž…๋‹ˆ๋‹ค.
    • ๋ฆฌ์†Œ์Šค๋ฅผ ์ง์ ‘ Context์—์„œ ๋ถˆ๋Ÿฌ์˜ค๋Š” ๋Œ€์‹ , Resource Provider๋ฅผ ํ†ตํ•ด ๋ฆฌ์†Œ์Šค๋ฅผ ๊ฐ€์ ธ์˜ด์œผ๋กœ์จ Context์— ๋Œ€ํ•œ ์˜์กด์„ฑ์„ ์ค„์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ํ…Œ์ŠคํŠธ ์šฉ์ด์„ฑ์„ ์ฆ๊ฐ€์‹œํ‚ต๋‹ˆ๋‹ค.
    • Resource Provider๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด Mock ๋ฆฌ์†Œ์Šค๋ฅผ ์ฃผ์ž…ํ•  ์ˆ˜ ์žˆ์–ด, ๋‹จ์œ„ ํ…Œ์ŠคํŠธ๊ฐ€ ์‰ฌ์›Œ์ง‘๋‹ˆ๋‹ค.

Resource Provider ๊ตฌํ˜„

ResourceProvider Interface

  • ๋ฆฌ์†Œ์Šค๋ฅผ ์ œ๊ณตํ•˜๋Š” ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ •์˜ํ•˜์—ฌ, ๋‹ค์–‘ํ•ญ ๋ฆฌ์†Œ์Šค๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ์—ญํ• ์„ ๋ถ€์—ฌํ•ฉ๋‹ˆ๋‹ค.
interface ResourceProvider {
    fun getString(resId: Int): String
    fun getString(resId: Int, vararg formatArgs: Any): String
    fun getColor(resId: Int): Int
    fun getDrawable(resId: Int): Drawable?
}

ResourceProviderImpl

  • ์‹ค์ œ ๊ตฌํ˜„์ฒด์—์„œ Context๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฆฌ์†Œ์Šค๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ์ฝ”๋“œ๋ฅผ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค.
class ResourceProviderImpl(private val context: Context) : ResourceProvider {

    override fun getString(resId: Int): String {
        return context.getString(resId)
    }

    override fun getString(resId: Int, vararg formatArgs: Any): String {
        return context.getString(resId, *formatArgs)
    }

    override fun getColor(resId: Int): Int {
        return ContextCompat.getColor(context, resId)
    }

    override fun getDrawable(resId: Int): Drawable? {
        return ContextCompat.getDrawable(context, resId)
    }
}

ViewModel์—์„œ ํ™œ์šฉ

  • ViewModel์—์„œ๋Š” Context์— ์ง์ ‘ ์ ‘๊ทผํ•˜์ง€ ์•Š๊ณ , ResourceProvider๋ฅผ ์ฃผ์ž… ๋ฐ›์•„ ๋ฆฌ์†Œ์Šค๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
class MyViewModel(private val resourceProvider: ResourceProvider) : ViewModel() {

    fun getWelcomeMessage(): String {
        return resourceProvider.getString(R.string.welcome_message)
    }

    fun getFormattedMessage(name: String): String {
        return resourceProvider.getString(R.string.hello_message, name)
    }
}
  • ์ด๋ฅผ ํ™œ์šฉํ•˜๋ฉด Context์— ์˜์กดํ•˜์ง€ ์•Š๊ณ  ๋ฆฌ์†Œ์Šค์— ์ ‘๊ทผ์ด ๊ฐ€๋Šฅํ•ด์ง‘๋‹ˆ๋‹ค.

์˜์กด์„ฑ ์ฃผ์ž… ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ํ™œ์šฉ

  • Hilt๋‚˜ Dagger๋ฅผ ํ™œ์šฉํ•œ ์˜์กด์„ฑ ์ฃผ์ž…๋„ ์œ ์šฉํ•˜๊ฒŒ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
@HiltViewModel
class MyViewModel @Inject constructor(
    private val resourceProvider: ResourceProvider
) : ViewModel() {
    // ViewModel ์ฝ”๋“œ...
}

ํ…Œ์ŠคํŠธ ๋ฐฉ์‹์€?

  • ํ…Œ์ŠคํŠธ์—์„œ ResourceProvider์˜ Mock์„ ์ƒ์„ฑํ•˜์—ฌ ํ…Œ์ŠคํŠธ๋ฅผ ์ง„ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
class FakeResourceProvider : ResourceProvider {

    override fun getString(resId: Int): String {
        return when (resId) {
            R.string.welcome_message -> "Welcome!"
            else -> "Unknown"
        }
    }

    override fun getString(resId: Int, vararg formatArgs: Any): String {
        return "Formatted string"
    }

    override fun getColor(resId: Int): Int {
        return 0xFFFFFF
    }

    override fun getDrawable(resId: Int): Drawable? {
        return null
    }
}

  • ์ด๋ ‡๊ฒŒ ๊ตฌํ˜„ํ•˜๋ฉด ViewModel์—์„œ ๋ฆฌ์†Œ์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋”๋ผ๋„ ํ…Œ์ŠคํŠธํ•  ๋•Œ ์‹ค์ œ Context๋‚˜ ๋ฆฌ์†Œ์Šค ํŒŒ์ผ์— ์˜์กดํ•˜์ง€ ์•Š๊ณ , Fake ๋˜๋Š” Mock ๋ฆฌ์†Œ์Šค๋ฅผ ์ฃผ์ž…ํ•˜์—ฌ ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ •๋ฆฌ

ResourceProvider์˜ ์žฅ์ 

  • Context์˜ ์˜์กด์„ฑ์„ ์ œ๊ฑฐํ•˜์—ฌ, ๋‹ค๋ฅธ ๊ณ„์ธต์—์„œ Context๋ฅผ ์ฐธ์กฐํ•˜์ง€ ์•Š๊ณ  MVVM ํŒจํ„ด์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • Mock์„ ํ™œ์šฉํ•œ ํ…Œ์ŠคํŠธ์— ์šฉ์ดํ•˜๋ฉฐ, UI ๋ฆฌ์†Œ์Šค์— ์˜์กดํ•˜์ง€ ์•Š๊ณ  ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ์‰ฝ๊ฒŒ ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๋ฆฌ์†Œ์Šค์— ์ ‘๊ทผํ•˜๋Š” ๋กœ์ง์„ ๋ถ„๋ฆฌํ•˜์—ฌ ์œ ์ง€ ๋ณด์ˆ˜์„ฑ์ด ํ–ฅ์ƒ๋˜๊ณ , ๋ฆฌ์†Œ์Šค์— ๋Œ€ํ•œ ์ ‘๊ทผ ๋ฐฉ์‹์ด ๋ณ€๊ฒฝ๋˜๋”๋ผ๋„ ResourceProvider๋งŒ ์ˆ˜์ •ํ•˜์—ฌ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ResourceProvider๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค๋ฉด?

  • ViewModel์—์„œ ์ง์ ‘ Context๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์•ˆ๋“œ๋กœ์ด๋“œ์˜ ์ƒ๋ช… ์ฃผ๊ธฐ์™€ ์—ฎ์ด๋Š” ๋ณต์žกํ•œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๋˜ํ•œ Mock์„ ํ™œ์šฉํ•œ ํ…Œ์ŠคํŠธ๊ฐ€ ์–ด๋ ค์šฐ๋ฉฐ, ๋‹จ์œ„ ํ…Œ์ŠคํŠธ์— ์‹ค์ œ ๋ฆฌ์†Œ์Šค์˜ ์˜์กดํ•˜์—ฌ ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ ๊ตฌ์ถ•์— ์–ด๋ ค์›€์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ResourceProvider๋Š” ์•ˆ๋“œ๋กœ์ด๋“œ ์•ฑ์—์„œ ๋ฆฌ์†Œ์Šค ์ ‘๊ทผ์„ ์ถ”์ƒํ™”ํ•˜๊ณ , Context์— ๋Œ€ํ•œ ์˜์กด์„ฑ์„ ์ค„์ด๋ฉฐ ํ…Œ์ŠคํŠธ ๊ฐ€๋Šฅ์„ฑ์„ ๋†’์—ฌ์ฃผ๋Š” ํŒจํ„ด์ž…๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ViewModel์ด๋‚˜ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์—์„œ ๋ฆฌ์†Œ์Šค๋ฅผ ์‰ฝ๊ฒŒ ์ฃผ์ž…๋ฐ›์•„ ์‚ฌ์šฉํ•˜๊ณ , ๋‹ค์–‘ํ•œ ๋ฆฌ์†Œ์Šค์— ๋Œ€ํ•œ ๋ณ€๊ฒฝ์„ ์œ ์—ฐํ•˜๊ฒŒ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ฐธ๊ณ 

https://arc.net/l/quote/ijnxuuku