MVP Pattern

๐Ÿ’ก ๋””์ž์ธ ํŒจํ„ด ์ค‘์— ํ•˜๋‚˜์ธ MVP ํŒจํ„ด์— ๋Œ€ํ•˜์—ฌ ํ•™์Šตํ•˜์˜€์Šต๋‹ˆ๋‹ค !

 

MVP

  • MVP๋Š” Model, View, Presenter๋ฅผ ํ•ฉ์นœ ์šฉ์–ด์ด๋ฉฐ, MVC์—์„œ C์— ํ•ด๋‹นํ•˜๋Š” Controller๊ฐ€ Presenter๋กœ ๊ต์ฒด๋œ ํŒจํ„ด์ž…๋‹ˆ๋‹ค.
  • ์ž๋™ํ™”๋œ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ๋ฅผ ์šฉ์ดํ•˜๊ฒŒ ํ•˜๊ณ , Presenter ๋กœ์ง์—์„œ ๊ด€์‹ฌ์‚ฌ๋ฅผ ๋ถ„๋ฆฌํ•˜๋„๋ก ์„ค๊ณ„ ๋œ ์‚ฌ์šฉ์ž ์ธํ„ฐํŽ˜์ด์Šค ์•„ํ‚คํ…์ฒ˜ ํŒจํ„ด์ž…๋‹ˆ๋‹ค.

MVC์™€์˜ ์ฐจ์ด

  • MVC์—์„œ๋Š” Controller๋ฅผ ํ†ตํ•ด์„œ ์ง์ ‘์ ์œผ๋กœ View์— ์ ‘๊ทผํ•˜๊ธฐ ๋•Œ๋ฌธ์—, Controller์™€ ์—ฐ๊ฒฐ ๋œ Model๊ณผ View๋Š” ์„œ๋กœ ์–ด๋Š์ •๋„์˜ ๊ฒฐํ•ฉ๋„๊ฐ€ ์žˆ๋‹ค๊ณ  ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ํ•˜์ง€๋งŒ MVP๋Š” ์˜ค์ง Presenter์™€ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ํ†ตํ•ด ๋ฉ”์‹œ์ง€ ์ „๋‹ฌํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ถ„๋ฆฌ๋œ ์ฑ…์ž„(Separation of Concerns)๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง(Model)๊ณผ ์‚ฌ์šฉ์ž ์ธํ„ฐํŽ˜์ด์Šค(View)๋ฅผ ๋ถ„๋ฆฌํ•˜์—ฌ ๊ด€๋ฆฌํ•˜๋ฏ€๋กœ ์ฝ”๋“œ์˜ ๊ฐ€๋…์„ฑ๊ณผ ์œ ์ง€๋ณด์ˆ˜์„ฑ์ด ํ–ฅ์ƒ ๋ฉ๋‹ˆ๋‹ค.

MVP ํŠน์ง•

์žฅ์ 

  • ์ฝ”๋“œ๊ฐ€ ๊น”๋”ํ•ด์ง€๊ณ , ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ ์ถ”๊ฐ€ ๋ฐ ๋ณ€๊ฒฝ์„ ํ•  ๋•Œ๋งˆ๋‹ค ๊ด€๋ จ๋œ ๋ถ€๋ถ„๋งŒ ์ฝ”๋“œ๋ฅผ ์ˆ˜์ •ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ํ™•์žฅ์„ฑ์ด ๊ฐœ์„ ๋ฉ๋‹ˆ๋‹ค.
  • UI, Model ๊ฐ๊ฐ ํŒŒํŠธ๋ฅผ ๋‚˜๋ˆ ์„œ ํ•ด์•ผ ํ•  ์ผ์ด ๋ช…ํ™•ํ•ด์ง€๋ฉฐ ์˜์กด์„ฑ์ด ์ œ๊ฑฐ๋˜๊ณ  ๊ฒฐํ•ฉ๋„๊ฐ€ ๋‚ฎ์•„์ง€๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.
  • ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ํ™œ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋‹จ์œ„ ํ…Œ์ŠคํŠธ๊ฐ€ ์šฉ์ดํ•˜๋‹ค๋Š” ์žฅ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

๋‹จ์ 

  • ํ•˜์ง€๋งŒ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ๋ณต์žกํ•ด์งˆ์ˆ˜๋ก View์™€ Presenter ์‚ฌ์ด์˜ ์˜์กด์„ฑ์ด ๊ฐ•ํ•ด์ง€๋Š” ๋ฌธ์ œ๊ฐ€ ์žˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • 1:1 ๊ด€๊ณ„๋ฅผ ์œ ์ง€ํ•ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— Presenter๋ฅผ ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.
    • ์ด๋Š” View๊ฐ€ ๋Š˜์–ด๋‚  ๋•Œ๋งˆ๋‹ค Presenter๋„ ๊ฐ™์ด ๋Š˜์–ด๋‚˜ ํด๋ž˜์Šค๊ฐ€ ๋งŽ์•„์ง€๊ณ  ๋น„๋Œ€ํ•ด์ง‘๋‹ˆ๋‹ค.

Model

  • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ์˜ ๋ฐ์ดํ„ฐ์ธ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค, ์ƒ์ˆ˜, ๋ณ€์ˆ˜๋ฅผ ๋œปํ•ฉ๋‹ˆ๋‹ค.
  • ๋ทฐ์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์ƒ์„ฑํ•˜๊ฑฐ๋‚˜ ์ˆ˜์ •ํ•˜๋ฉด Presenter๋ฅผ ํ†ตํ•ด ๋ชจ๋ธ์„ ์ƒ์„ฑํ•˜๊ฑฐ๋‚˜ ๊ฐฑ์‹ ํ•ฉ๋‹ˆ๋‹ค.
// ์ฃผ์‚ฌ์œ„๋ฅผ ๋˜์ง€๋Š” ํ–‰์œ„๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” DiceModel์„ ์ƒ์„ฑํ•˜์˜€์Šต๋‹ˆ๋‹ค.
class DiceModel {
		// ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜์ง€๋Š” ์•Š์ง€๋งŒ, ์•„๋ž˜์™€ ๊ฐ™์ด ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.
		val color = Color.Red
		
    fun rollDice(): Int { // ๋‚ด๋ถ€ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค
        return (1..6).random()
    }
}

View

  • ์‚ฌ์šฉ์ž ์ธํ„ฐํŽ˜์ด์Šค ์š”์†Œ๋กœ ๋ชจ๋ธ์„ ๊ธฐ๋ฐ˜์œผ๋กœ ์‚ฌ์šฉ์ž๊ฐ€ ๋ณผ ์ˆ˜ ์žˆ๋Š” ํ™”๋ฉด์„ ๋œปํ•ฉ๋‹ˆ๋‹ค.
  • ๋ชจ๋ธ์ด ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ์ •๋ณด๋ฅผ ๋”ฐ๋กœ ์ €์žฅํ•˜์ง€ ์•Š์•„์•ผ ํ•˜๋ฉฐ ๋‹จ์ˆœํžˆ ํ™”๋ฉด์— ํ‘œ์‹œํ•˜๋Š” ์ •๋ณด๋งŒ์„ ๊ฐ€์ง€๊ณ  ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
class MainActivity : AppCompatActivity(), DiceContract.View {

    private lateinit var presenter: DiceContract.Presenter

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // Model๊ณผ Presenter ์ดˆ๊ธฐํ™”
        val model = DiceModel()
        presenter = DicePresenter(this, model)

        val rollButton = findViewById<Button>(R.id.rollButton)
        rollButton.setOnClickListener {
            presenter.onRollDice()
        }
    }

    override fun showResult(result: Int) {
        val resultTextView = findViewById<TextView>(R.id.resultTextView)
        resultTextView.text = "์ฃผ์‚ฌ์œ„ ๊ฒฐ๊ณผ: $result"
    }

    override fun showError(message: String) {
        Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
    }
}

Presenter

  • Model๊ณผ View ์‚ฌ์ด์˜ ๋งค๊ฐœ์ฒด ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.
  • MVC์˜ Controller์™€ ์œ ์‚ฌํ•˜์ง€๋งŒ, View์— ์ง์ ‘ ์—ฐ๊ฒฐ๋˜๋Š” ๋Œ€์‹  ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ํ†ตํ•ด ์ƒํ˜ธ์ž‘์šฉํ•œ๋‹ค๋Š” ํฐ ์ฐจ์ด๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์ด๋ฅผ ํ†ตํ•ด MVC๊ฐ€ ๊ฐ€์ง„ ํ…Œ์ŠคํŠธ ๋ฌธ์ œ์™€ ํ•จ๊ป˜ ๋ชจ๋“ˆํ™”, ์œ ์—ฐ์„ฑ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • View์—๊ฒŒ ํ‘œ์‹œํ•  ๋‚ด์šฉ(Data)๋งŒ ์ „๋‹ฌํ•˜๋ฉฐ ์–ด๋–ป๊ฒŒ ๋ณด์—ฌ์ค„ ์ง€๋Š” View๊ฐ€ ๋‹ด๋‹นํ•ฉ๋‹ˆ๋‹ค.
class DicePresenter(
    private val view: DiceContract.View,
    private val model: DiceModel
) : DiceContract.Presenter {

    override fun onRollDice() {
        try {
            val result = model.rollDice()
            view.showResult(result)
        } catch (e: Exception) {
            view.showError("์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.")
        }
    }
}

MVP Logic

  • MVP ํŒจํ„ด์€ ์•„๋ž˜์™€ ๊ฐ™์€ ๋กœ์ง์œผ๋กœ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค.

  1. View์—์„œ ์‚ฌ์šฉ์ž ์ด๋ฒคํŠธ๋ฅผ ์ˆ˜์‹ ํ•ฉ๋‹ˆ๋‹ค.
  2. View → Presenter๋กœ ์ด๋ฒคํŠธ๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.
  3. Presenter → Model๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์š”์ฒญํ•ฉ๋‹ˆ๋‹ค.
  4. Model → Presenter๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค. (์ˆ˜์ •, ์‚ญ์ œ, ์ถ”๊ฐ€ ๋œ ์ •๋ณด)
  5. Presenter → View๋กœ ์ „๋‹ฌ๋ฐ›์€ ๋ฐ์ดํ„ฐ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์—…๋ฐ์ดํŠธ ํ•ฉ๋‹ˆ๋‹ค.
  6. View์—์„œ ํ™”๋ฉด์„ ์—…๋ฐ์ดํŠธ ํ•ฉ๋‹ˆ๋‹ค.

Contract Interface

  • Contract Interface๋Š” View์™€ Presenter ๊ฐ„์˜ ์ด๋ฒคํŠธ๋“ค์„ ์„ ์–ธํ•ฉ๋‹ˆ๋‹ค.
interface DiceContract {
	//Contract.View interface
    interface View {
        fun showResult(result: Int)
        fun showError(message: String)
    }
	//Contract.Prsenter interface
    interface Presenter {
        fun onRollDice()
    }
}

Contract.View interface

  • Presenter์—์„œ View๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๊ธฐ ์œ„ํ•œ ์ด๋ฒคํŠธ๋“ค์„ ์„ ์–ธํ•ฉ๋‹ˆ๋‹ค.
    • ์ด๋Š” 5๋ฒˆ์งธ ๋กœ์ง ์ž‘์—…์ด ๋ฉ๋‹ˆ๋‹ค.

Contract.Prsenter interface

  • View์—์„œ ํ˜ธ์ถœํ•  Presenter ์ด๋ฒคํŠธ๋“ค์„ ์„ ์–ธํ•ฉ๋‹ˆ๋‹ค.
    • ์ด๋Š” 2๋ฒˆ์งธ ๋กœ์ง ์ž‘์—…์ด ๋ฉ๋‹ˆ๋‹ค.

์ฐธ๊ณ 

https://velog.io/@kyeun95/๋””์ž์ธ-ํŒจํ„ด-MVP-ํŒจํ„ด์ด๋ž€

https://brunch.co.kr/@oemilk/75

https://github.com/android/architecture-samples/tree/master