๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
๐Ÿ“ฑ| Android/๐Ÿ“˜ | ๊ธฐ๋ก

[Android] MVI ํŒจํ„ด์€ ๋ฌด์—‡์ผ๊นŒ?

by immgga 2023. 8. 21.

 

์š”์ฆ˜์€ compose๋ฅผ ๋งŽ์ด ์‚ฌ์šฉํ•˜๋Š” ์ถ”์„ธ์ด๋‹ค. ํ•„์ž๋„ compose๋ฅผ ๋งŽ์ด ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋‹ค.

ํ•„์ž๊ฐ€ ์ง€์ธ๋“ค์—๊ฒŒ compose์™€ mvi๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ข‹๋‹ค๋Š” ์†Œ์‹์„ ๋“ฃ๊ณ  ๋ฐ”๋กœ mvi ๊ณต๋ถ€๋ฅผ ์‹œ์ž‘ํ–ˆ๋‹ค.

์ง€๊ธˆ ๋ฐ”๋กœ mvi์— ๋Œ€ํ•ด ์•Œ์•„๋ณด์ž.

 

1. mvi๋ž€?

mvi ์•„ํ‚คํ…์ฒ˜๋Š” mvc, mvp, mvvm์™€ ๊ฐ™์€ ์•ˆ๋“œ๋กœ์ด๋“œ ๊ฐœ๋ฐœ์ž๋“ค์˜ ๊ฐœ๋ฐœ๊ณผ ์œ ์ง€ ๋ณด์ˆ˜๋ฅผ ํŽธ๋ฆฌํ•˜๊ฒŒ ํ•ด์ฃผ๋Š” ์•„ํ‚คํ…์ฒ˜ ํŒจํ„ด์˜ ์ผ์ข…์ด๋ผ ํ•  ์ˆ˜ ์žˆ๋‹ค.

mvi์˜ ๊ตฌ์กฐ๋Š” model - view - intent๋กœ ๊ตฌ์„ฑ๋˜๋ฉฐ ๊ฐ layer๋Š” ์„œ๋กœ ์™„์ „ํžˆ ๊ด€์‹ฌ์‚ฌ๊ฐ€ ๋ถ„๋ฆฌ๋˜์–ด ์žˆ๋‹ค.

sideEffects๋Š” ์‹œ๊ฐ„์ด ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๋Š” ์ž‘์—…(api ํ†ต์‹ , ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์Šค๋ ˆ๋“œ์—์„œ ์ฒ˜๋ฆฌํ•  ๋กœ์ง)๊ณผ ์ธ์Šคํ„ด์Šค ๋ทฐ(screen ๊ฐ„ ์ด๋™, toast์™€ dialog)๋“ฑ์„ ์ฒ˜๋ฆฌํ•œ๋‹ค.

sideEffects๊ฐ€ ์žˆ์œผ๋ฉด ์‚ฌ์šฉ์ž์˜ ํ•„์š”์— ๋”ฐ๋ผ sideEffects์˜ ๋กœ์ง์ด ์ˆ˜ํ–‰๋  ๊ฒƒ์ด๋‹ค.

์ถœ์ฒ˜: https://sungbin.land/%EC%95%84%EC%A7%81%EB%8F%84-mvvm-%EC%9D%B4%EC%A0%A0-mvi-%EC%8B%9C%EB%8C%80-319990c7d60

 

2. mvi๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ด์œ ?

์ด๊ฒŒ ์‚ฌ์‹ค ๋ณธ๋ก ์ด๋‹ค "์ด ํŒจํ„ด์„ ์™œ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๋Š”๊ฐ€?"์ด๋‹ค.

mvi๋ฅผ ์™œ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๋ƒ๋ฉด

์šฐ๋ฆฌ๊ฐ€ ๋งŒ๋“œ๋Š” ์•ˆ๋“œ๋กœ์ด๋“œ ์•ฑ์€ ์‚ฌ์‹ค state(์ƒํƒœ)์˜ ์ง‘ํ•ฉ์ด๋‹ค.

์•ˆ๋“œ๋กœ์ด๋“œ ์•ฑ์—์„œ๋Š” ๋‹ค์–‘ํ•œ ์ด๋ฒคํŠธ๋ฅผ ๋ฐœ์ƒ์‹œ์ผœ state๋ฅผ ์ƒ์„ฑํ•ด ๋‚ธ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด button์„ ํด๋ฆญํ•œ๋‹ค ๋˜๋Š” api ํ˜ธ์ถœ ํ›„ ์„ฑ๊ณต/์‹คํŒจ ์™€ ๊ฐ™์€ ๊ฒฝ์šฐ๊ฐ€ ์žˆ๋‹ค.

์•ˆ๋“œ๋กœ์ด๋“œ ๊ฐœ๋ฐœ์ž๋“ค์€ ๋ชจ๋‘ ์ด state๋“ค์„ ๊ด€๋ฆฌํ•˜๋ฉด์„œ ์•ฑ์„ ๋งŒ๋“ค์–ด์•ผ ํ•œ๋‹ค.

 

๋งŒ์•ฝ state์˜ ๊ด€๋ฆฌ๊ฐ€ ์ œ๋Œ€๋กœ ์ด๋ฃจ์–ด์ง€์ง€ ์•Š๋Š”๋‹ค๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค.

 

์œ„์˜ ์‚ฌ์ง„์€ api ํ†ต์‹ ์œผ๋กœ ๊ตญ๊ฐ€์˜ ์ •๋ณด์™€ image๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ์ถœ๋ ฅ๋˜์—ˆ๋Š”๋ฐ(success ์ƒํƒœ)

๋กœ๋”ฉ indicator๊ฐ€ ์‚ฌ๋ผ์ง€์ง€ ์•Š๊ณ  ์žˆ๋‹ค.(loading ์ƒํƒœ)

 

์šฐ๋ฆฌ๋“ค์€ ์œ„์™€ ๊ฐ™์€ ๋ฌธ์ œ๋“ค์„ ์‚ฌ์ „์— ๋ง‰์œผ๋ฉด์„œ, ์ˆ˜๋งŽ์€ ์ƒํƒœ๋“ค์„ ํšจ์œจ์ ์œผ๋กœ ๊ด€๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด mvi๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค!

 

3. mvi ์‚ฌ์šฉ๋ฒ•(?)

mvi๊ฐ€ ์™œ ํ•„์š”ํ•œ์ง€ ์•Œ์•˜์œผ๋ฉด ์ด์ œ๋Š” ์‚ฌ์šฉ๋ฒ•(?)์„ ์ตํ˜€๋ณด์ž.

 

1. model

model์—์„œ screen์— ์‚ฌ์šฉํ•  state๋“ค์„ ์ƒ์„ฑํ•œ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด success ์ƒํƒœ, fail ์ƒํƒœ, loading ์ƒํƒœ, ๋ฐ์ดํ„ฐ ์ •๋ณด(api ์ •๋ณด ๋“ฑ๋“ฑ..)๋ฅผ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๋‹ค.

data class MainState(
    val onLoading: Boolean = false,
    val onComplete: Boolean = false,
    val onFail: Boolean = false,
    val apiData: ApiData? = null
)

์˜ˆ์ œ๋ฅผ ๋ณด๋ฉด loading, complete, fail ์ƒํƒœ์˜ state์™€ api data๊ฐ€ ์ถ”๊ฐ€๋œ ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

 

2. intent

intent์—์„œ๋Š” state์— ๋”ฐ๋ฅธ event๋“ค์„ ์ •์˜ํ•œ๋‹ค.

intent๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๊ฒƒ์€ sealed class๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์„ ๊ถŒ์žฅํ•œ๋‹ค๊ณ  ํ•œ๋‹ค.

๊ฐœ๋ฐœ์ž๋Š” ์ž์‹ ์ด ์ƒ์„ฑํ•œ state์— ๋”ฐ๋ฅธ event๋ฅผ ์ƒ์„ฑํ•ด์„œ ํ˜ธ์ถœ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋‹ค.

sealed class MainIntent {
    data class apiData(apiData: ApiData): MainIntent()
    object OnError: MainIntent()
}

์œ„์˜ ์˜ˆ์ œ์—์„œ๋Š” api๊ฐ€ ํ˜ธ์ถœ๋˜์—ˆ์„ ๋•Œ์˜ ์ด๋ฒคํŠธ์™€

error์˜ ์ด๋ฒคํŠธ๋ฅผ ์ƒ์„ฑํ•˜์˜€๋‹ค.

intent๋Š” ํ•œ ํ™”๋ฉด์—์„œ ์ผ์–ด๋‚˜๋Š” state event๋ฅผ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.

 

3. sideEffects

sideEffects์—์„œ๋Š” ์œ„์—์„œ๋„ ์–ธ๊ธ‰ํ–ˆ๋‹ค์‹œํ”ผ, intent์—์„œ ํ•˜๊ธฐ์—๋Š” ์–ด๋ ค์šด ์ž‘์—…๋“ค(api ๋กœ์ง, ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์Šค๋ ˆ๋“œ์—์„œ ํ•  ์ž‘์—…, ์ธ์Šคํ„ด์Šค ๋ทฐ ๋“ฑ)์„ ์ •์˜ํ•œ๋‹ค.

sealed class MainSideEffects {
    data class ApiRequest(requestBoody: RequestBody): MainSideEffects()
}

 

4. view

view๋Š” intent์—์„œ ์ƒ์„ฑํ•œ state(์ƒํƒœ)์— ๋”ฐ๋ฅธ event๋ฅผ ์ด์šฉํ•ด ํ™”๋ฉด์„ ๊ตฌ์„ฑํ•˜๋Š” ์—ญํ• ์„ ํ•œ๋‹ค.

compose์˜ ๊ฒฝ์šฐ์—๋Š” ์•„์ง ์ž˜ ๋ชจ๋ฅด๊ฒ ์ง€๋งŒ xml์˜ ๊ฒฝ์šฐ์—๋Š” databinding์ด ์‚ฌ์šฉ๋œ๋‹ค๊ณ  ์•Œ๊ณ  ์žˆ๋‹ค.

 

๊ฐ„๋‹จ ์ •๋ฆฌ

  • mvi๋Š” ์•ˆ๋“œ๋กœ์ด๋“œ ์•ฑ์˜ state(์ƒํƒœ) ๊ด€๋ฆฌ์— ์œ ์šฉํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์•„ํ‚คํ…์ฒ˜ ํŒจํ„ด์ด๋‹ค.
  • mvi๋ฅผ ๋ฐฐ์šฐ๊ธฐ ์œ„ํ•ด ์•Œ์•„์•ผ ํ•  ์‚ฌ์ „ ์ง€์‹์ด ๋งŽ์„ ๊ฒƒ ๊ฐ™๋‹ค.
  • ๋˜ํ•œ ํ•œ ํ™”๋ฉด์˜ ๋ชจ๋“  ์ƒํƒœ๋ฅผ ์ƒ์„ฑํ•˜์—ฌ์•ผ ํ•˜๊ธฐ์— ๊ฐ„๋‹จํ•œ state ๋ณ€ํ™”๋„ ๋ชจ๋‘ ์ฝ”๋“œ๋กœ ์ ์–ด ์ฃผ์–ด์•ผ ํ•œ๋‹ค.

 

์ •๋ฆฌ

  • mvi์— ๋Œ€ํ•ด ๊ณต๋ถ€ํ•ด ๋ณด์•˜๋Š”๋ฐ ์•„์ง ๋ถ€์กฑํ•œ ์ ์ด ๋งŽ์•˜๋‹ค.
  • mvi๋Š” ๋‹จ์ ๋„ ์žˆ์ง€๋งŒ state ๊ด€๋ฆฌ๊ฐ€ ์ค‘์š”ํ•œ compose์™€ ๊ฐ™์ด ์‚ฌ์šฉํ•˜๋ฉด ์ตœ๊ณ ์˜ ์‹œ๋„ˆ์ง€๊ฐ€ ๋‚  ๊ฒƒ ๊ฐ™๋‹ค.
  • ์ด์ œ ์ง์ ‘ ์‹ค์ „์—์„œ ์‚ฌ์šฉํ•˜๋ฉด์„œ ๊ณต๋ถ€ํ•œ ๋‚ด์šฉ์„ ์ ์šฉํ•˜๊ณ  ์‹ถ์–ด์กŒ๋‹ค.
  • ๋‹ค๋ฅธ mv ์‹œ๋ฆฌ์ฆˆ์™€๋Š” ๋‹ค๋ฅด๊ฒŒ ๋ช…ํ™•ํ•œ ์žฅ์ ์ด ์žˆ๋Š” ์•„ํ‚คํ…์ฒ˜ ํŒจํ„ด ๊ฐ™์•˜๋‹ค(๊ธฐ๋ถ„ ํƒ“์ผ ์ˆ˜๋„ ์žˆ๋‹ค.)

 

์ œ ์†Œ์Šค ์ฝ”๋“œ๋Š” ์•„๋ž˜์˜ ์ตœ์žฌํ˜ธ ๊ฐ•์‚ฌ๋‹˜์˜ ๊ฐ•์˜๋ฅผ ์‚ฌ์šฉํ•ด ์ œ ์ž…๋ง›๋Œ€๋กœ(?) ์กฐ๊ธˆ์”ฉ ๋ฐ”๊พธ์—ˆ์Šต๋‹ˆ๋‹ค. ์˜ค๋ฅ˜๊ฐ€ ์žˆ์„ ์ˆ˜๋„ ์žˆ์œผ๋‹ˆ ์–‘ํ•ด ๋ฐ”๋ž๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ ๊ฐ•์‚ฌ๋‹˜์˜ ๊ฐ•์˜๋ฅผ ์ฐธ๊ณ ํ•ด ๋ณด์‹œ๋ฉด ๋„์›€์ด ๋  ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค :0

 

์ฐธ๊ณ 

https://youtu.be/_ePUpzECd8c

https://sungbin.land/%EC%95%84%EC%A7%81%EB%8F%84-mvvm-%EC%9D%B4%EC%A0%A0-mvi-%EC%8B%9C%EB%8C%80-319990c7d60

 

์•„์ง๋„ MVVM? ์ด์   MVI ์‹œ๋Œ€!

Orbit์„ ์ด์šฉํ•œ MVI ๋””์ž์ธํŒจํ„ด ์•Œ์•„๋ณด๊ธฐ

sungbin.land

728x90

๋Œ“๊ธ€