MVC to MVP Migration

๐Ÿ’ก ์šฐ์•„ํ•œํ…Œํฌ์ฝ”์Šค ๊ณผ์ •์„ ์ง„ํ–‰ํ•˜๋ฉด์„œ MVC ๋””์ž์ธํŒจํ„ด์„ MVP๋กœ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์„ ์ง„ํ–‰ํ•œ ์ด์œ ์™€ ๊ณผ์ •์— ๋Œ€ํ•˜์—ฌ ๊ธฐ๋กํ•˜์˜€์Šต๋‹ˆ๋‹ค!

 

๊ฐœ์š”

  • ์ด์ „์— MVC ํŒจํ„ด์— ๋Œ€ํ•˜์—ฌ ํ•™์Šตํ•˜์˜€์Šต๋‹ˆ๋‹ค.

[๋””์ž์ธํŒจํ„ด] MVC

 

[๋””์ž์ธํŒจํ„ด] MVC

๐Ÿ’ก ๋””์ž์ธํŒจํ„ด ์ค‘์— ํ•˜๋‚˜์ธ MVC ํŒจํ„ด์— ๋Œ€ํ•˜์—ฌ ํ•™์Šตํ•˜์˜€์Šต๋‹ˆ๋‹ค ! MVCMVC๋Š” Model, View, Controller์˜ ์•ฝ์ž๋กœ ํ•˜๋‚˜์˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด๋‚˜ ํ”„๋กœ์ ํŠธ๋ฅผ ๊ตฌ์„ฑํ•  ๋•Œ ๊ตฌ์„ฑ์š”์†Œ๋ฅผ ์„ธ๊ฐ€์ง€ ์—ญํ• ๋กœ ๊ตฌ๋ถ„ํ•œ ํŒจํ„ด์ž…

jinudmjournal.tistory.com

  • ํ•˜์ง€๋งŒ MVC ํŒจํ„ด์œผ๋กœ ๊ตฌํ˜„ ํ›„ ํ…Œ์ŠคํŠธ์™€ ์˜์กด์„ฑ ๊ด€๋ จํ•ด์„œ ๋ฌธ์ œ์ ์ด ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ๊ฒŒ ๋˜์—ˆ๊ณ , ์ด๋ฅผ ๋ณด์™„ํ•œ MVP ํŒจํ„ด์— ๋Œ€ํ•ด์„œ ํ•™์Šตํ•˜์˜€์Šต๋‹ˆ๋‹ค.

[๋””์ž์ธํŒจํ„ด] MVP

 

[๋””์ž์ธํŒจํ„ด] MVP

๐Ÿ’ก ๋””์ž์ธ ํŒจํ„ด ์ค‘์— ํ•˜๋‚˜์ธ MVP ํŒจํ„ด์— ๋Œ€ํ•˜์—ฌ ํ•™์Šตํ•˜์˜€์Šต๋‹ˆ๋‹ค ! MVPMVP๋Š” Model, View, Presenter๋ฅผ ํ•ฉ์นœ ์šฉ์–ด์ด๋ฉฐ, MVC์—์„œ C์— ํ•ด๋‹นํ•˜๋Š” Controller๊ฐ€ Presenter๋กœ ๊ต์ฒด๋œ ํŒจํ„ด์ž…๋‹ˆ๋‹ค.์ž๋™ํ™”๋œ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ

jinudmjournal.tistory.com

  • MVCํŒจํ„ด์„ MVP ํŒจํ„ด์œผ๋กœ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ํ–ˆ์„ ๋•Œ ์–ป๋Š” ์ด์ ๊ณผ ๊ณผ์ •์„ ์ •๋ฆฌํ•ด๋ณด๋ ค ํ•ฉ๋‹ˆ๋‹ค.

MVC

  • ์šฐ์•„ํ•œํ…Œํฌ์ฝ”์Šค ๊ณผ์ • ์ดˆ๊ธฐ์— MVC ํŒจํ„ด์„ ์ฃผ๋กœ ํ™œ์šฉํ•˜์˜€์Šต๋‹ˆ๋‹ค.
  • MVC ํŒจํ„ด์—์„œ ์ปจํŠธ๋กค๋Ÿฌ๋Š” ๋ทฐ๋กœ๋ถ€ํ„ฐ ์œ ์ €์˜ ์ด๋ฒคํŠธ๋กœ ๋ชจ๋ธ์„ ๋ณ€๊ฒฝํ•˜์—ฌ ๋‹ค์‹œ ์ปจํŠธ๋กค๋Ÿฌ์— ์ „๋‹ฌํ•ด ๋ทฐ๋ฅผ ์—…๋ฐ์ดํŠธ ํ•ฉ๋‹ˆ๋‹ค.
  • ์ด๋Ÿฌํ•œ ๊ณผ์ •์—์„œ ๋ชจ๋ธ๊ณผ ๋ทฐ๋ฅผ ๋ถ„๋ฆฌํ•˜๊ธด ํ•˜์˜€์ง€๋งŒ, ์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ ์ง€๋‚˜์น˜๊ฒŒ ๋น„๋Œ€ํ•ด์ง€๋Š” ๊ณผ์ •์ด ๋ฐœ์ƒํ•˜์˜€์Šต๋‹ˆ๋‹ค.
  • ๋˜ํ•œ ์ดˆ๊ธฐ์—๋Š” ์ฃผ๋กœ ์ฝ˜์†”์„ ํ™œ์šฉํ•œ I/O ํ”„๋กœ๊ทธ๋žจ์„ ๊ตฌํ˜„ํ•˜์˜€์ง€๋งŒ, Activity๋ฅผ ํ™œ์šฉํ•˜๋Š” ์•ˆ๋“œ๋กœ์ด๋“œ ํ”„๋ ˆ์ž„์›Œํฌ๋กœ ๋„˜์–ด๊ฐ€๋ฉด์„œ ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธฐ๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

MVC with Android

View์™€ Controller์˜ ๊ฐ•ํ•œ ๊ฒฐํ•ฉ

  • ์•ˆ๋“œ๋กœ์ด๋“œ์—์„œ๋Š” xml ํŒŒ์ผ์„ ํ†ตํ•ด์„œ View๋ฅผ ๋ณด์—ฌ์ฃผ๊ณ , ํ•ด๋‹น View์˜ ์ด๋ฒคํŠธ๋‚˜ ๋ณ€ํ™”๋ฅผ ๋ฐ›์•„ ๋™์ž‘์„ ์ˆ˜ํ–‰ํ•˜๋Š” ์•กํ‹ฐ๋น„ํ‹ฐ๊ฐ€ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.
  • ์ผ๋ฐ˜์ ์œผ๋กœ xml๊ณผ activity๋Š” 1:1 ๊ด€๊ณ„๋กœ ๊ตฌ์„ฑ๋˜๋Š”๋ฐ, ์ด๋กœ์ธํ•ด Controller๊ฐ€ activity์˜ ๋กœ์ง๊ณผ ๋™์ผํ•ด์ง€๊ณ  Activity์—์„œ๋Š” ๋ทฐ ๋กœ์ง๊ณผ Controller์˜ ์—ญํ• ์„ ๋™์‹œ์— ๋‹ด๋‹นํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

Activity or Fragment์˜ ๊ณผ๋„ํ•œ ์ฑ…์ž„

  • God Object ๋ฌธ์ œ ๋ฐœ์ƒ
    • Activity, Fragment๊ฐ€ View์™€ Controller์˜ ์—ญํ• ์„ ๋™์‹œ์— ์ˆ˜ํ–‰ํ•˜๋ฉด์„œ ์ฝ”๋“œ์˜ ์–‘์ด ๊ธ‰๊ฒฉํ•˜๊ฒŒ ์ฆ๊ฐ€ํ•˜๊ณ  ๋ณต์žกํ•ด์ง‘๋‹ˆ๋‹ค.
    • ์ด๋ฅผ Got Object ๋ฌธ์ œ๋ผ๊ณ  ํ•˜๋ฉฐ, ํ•œ ํด๋ž˜์Šค์— ๋„ˆ๋ฌด ๋งŽ์€ ์ฑ…์ž„์ด ์ง‘์ค‘๋˜๋Š” ํ˜„์ƒ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.
  • ์ด๋กœ ์ธํ•ด ์ž‘์€ ๋ณ€๊ฒฝ์—๋„ ํฐ ์˜ํ–ฅ์„ ๋ฏธ์น  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ๋ฒ„๊ทธ ๋ฐœ์ƒ ๊ฐ€๋Šฅ์„ฑ๊ณผ ์ฝ”๋“œ ๊ฐ€๋…์„ฑ์ด ์ €ํ•˜๋ฉ๋‹ˆ๋‹ค.

์–ด๋ ค์šด ํ…Œ์ŠคํŠธ

  • Controller์™€ View์˜ ๊ฐ•ํ•œ ๊ฒฐํ•ฉ์œผ๋กœ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ๋…๋ฆฝ์ ์œผ๋กœ ํ…Œ์ŠคํŠธํ•˜๊ธฐ ์–ด๋ ต์Šต๋‹ˆ๋‹ค.
  • Activity๋‚˜ Fragment๋Š” Android ํ”„๋ ˆ์ž„์›Œํฌ์— ๊ฐ•ํ•˜๊ฒŒ ์˜์กดํ•˜๋ฏ€๋กœ, ๋‹จ์œ„ ํ…Œ์ŠคํŠธ๋ฅผ ์œ„ํ•ด์„œ ๋ณต์žกํ•œ Mocking์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.
  • ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๊ฐ€ ๋ณต์žกํ•ด์ง€๊ณ , ํ…Œ์ŠคํŠธ ์ปค๋ฒ„๋ฆฌ์ง€๊ฐ€ ๋‚ฎ์•„์ง€๋Š” ๋‹จ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค.
  • [ํ…Œ์ŠคํŠธ ์ปค๋ฒ„๋ฆฌ์ง€] : ์‹œ์Šคํ…œ ์†Œํ”„ํŠธ์›จ์–ด์—์„œ ํ…Œ์ŠคํŠธ๊ฐ€ ์–ผ๋งˆ๋‚˜ ์ถฉ๋ถ„ํ•œ๊ฐ€๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ์ง€ํ‘œ๋กœ, ์ˆ˜ํ–‰ํ•œ ํ…Œ์ŠคํŠธ๊ฐ€ ๋Œ€์ƒ์„ ์–ด๋งˆ๋‚˜ ์ปค๋ฒ„ํ–ˆ๋Š”์ง€ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค.

๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง๊ณผ UI ๋กœ์ง์˜ ํ˜ผํ•ฉ

  • MVC ํŒจํ„ด์—์„œ ๋กœ์ง์ด ๋ณต์žกํ•ด์งˆ ๊ฒฝ์šฐ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง๊ณผ UI ๋กœ์ง์ด ํ˜ผํ•ฉ๋˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์Šต๋‹ˆ๋‹ค.
  • ์ด๋กœ ์ธํ•ด ์ฝ”๋“œ์˜ ๋ชจ๋“ˆํ™”๊ฐ€ ๋–จ์–ด์ง€๊ณ , ์ฝ”๋“œ ๋ณ€๊ฒฝ์œผ๋กœ ์ธํ•œ ๋ฒ„๊ทธ ๋ฐœ์ƒ ๊ฐ€๋Šฅ์„ฑ์ด ๋†’์•„์ง‘๋‹ˆ๋‹ค.

MVP๋กœ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ํ•˜๋Š” ์ด์œ 

ํ…Œ์ŠคํŠธ ๊ฐ€๋Šฅ์„ฑ ํ–ฅ์ƒ

  • MVC์—์„œ๋Š” Controller๊ฐ€ View์— ๊ฐ•ํ•˜๊ฒŒ ๊ฒฐํ•ฉ๋˜์–ด ์žˆ์–ด ํ…Œ์ŠคํŠธ๊ฐ€ ์–ด๋ ต์ง€๋งŒ, MVP๋Š” Presenter๊ฐ€ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ํ†ตํ•ด View์™€ ์ƒํ˜ธ์ž‘์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋‹จ์œ„ ํ…Œ์ŠคํŠธ๊ฐ€ ์šฉ์ดํ•ฉ๋‹ˆ๋‹ค.

์œ ์—ฐ์„ฑ ์ฆ๊ฐ€

  • View Presenter์˜ ๋ช…ํ™•ํ•œ ๋ถ„๋ฆฌ๋กœ ์ธํ•ด UI ๋ณ€๊ฒฝ ์‹œ Presenter๋ฅผ ์ˆ˜์ •ํ•  ํ•„์š”๊ฐ€ ์—†์œผ๋ฏ€๋กœ ๋” ์œ ์—ฐํ•œ ๊ตฌ์กฐ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

์ฝ”๋“œ ์œ ์ง€๋ณด์ˆ˜์„ฑ๊ณผ ์žฌ์‚ฌ์šฉ์„ฑ

  • ๊ฐ ์ปดํฌ๋„ŒํŠธ์˜ ์—ญํ• ์ด ๋” ๋ช…ํ™•ํ•˜๊ฒŒ ๋ถ„๋ฆฌ๋˜์–ด ์žˆ์–ด ์œ ์ง€๋ณด์ˆ˜์™€ ํ™•์žฅ์ด ์šฉ์ดํ•ฉ๋‹ˆ๋‹ค.
  • Presenter๊ฐ€ ๋กœ์ง์„ ๋‹ด๋‹นํ•˜๋ฏ€๋กœ, ๋‹ค๋ฅธ View์—์„œ๋„ ๋™์ผํ•œ Presenter๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด ์ฝ”๋“œ ์žฌ์‚ฌ์šฉ์„ฑ์„ ๋†’์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์ง„ํ–‰ํ•˜๊ธฐ

  • ์‹ค์ œ๋กœ๋Š” Contract Interface๋ฅผ ํ™œ์šฉํ•˜์—ฌ View ์ธํ„ฐํŽ˜์ด์Šค์™€ Presenter ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ํ•œ ๊ณณ์—์„œ ๊ด€๋ฆฌํ•˜์˜€์ง€๋งŒ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ํ•˜๋Š” ๊ณผ์ •์„ ์œ„ํ•ด์„œ ๋ถ„๋ฆฌํ•˜์˜€์Šต๋‹ˆ๋‹ค.

View Interface ์ƒ์„ฑ

  • MVC ๊ตฌ์กฐ์˜ View์—์„œ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ถ”์ถœํ•˜์˜€์Šต๋‹ˆ๋‹ค.
interface MainView {
    fun showLoading()
    fun hideLoading()
    fun showData(data: List<String>)
    fun showError(error: String)
}

Presenter ํด๋ž˜์Šค ์ƒ์„ฑ

  • MVC์˜ Controller ์—ญํ• ์„ ํ–ˆ๋˜ ๋กœ์ง์„ Presenter๋กœ ์ด๋™ํ•˜์˜€์Šต๋‹ˆ๋‹ค.
  • Presenter๋Š” View ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์˜์กด์„ฑ์œผ๋กœ ๊ฐ€์ง€๋ฉฐ, View์™€ Model ๊ฐ„์˜ ์ƒํ˜ธ์ž‘์šฉ์„ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
class MainPresenter(private val view: MainView, private val model: DataModel) {

    fun loadData() {
        view.showLoading()
        val data = model.fetchData()
        if (data.isNotEmpty()) {
            view.showData(data)
        } else {
            view.showError("No data found")
        }
        view.hideLoading()
    }
}

๊ธฐ์กด์˜ View ํด๋ž˜์Šค์—์„œ Presenter ์‚ฌ์šฉ

  • ๊ธฐ์กด์˜ Activity๋‚˜ Fragment๋ฅผ Presenter์™€ ์ƒํ˜ธ์ž‘์šฉํ•˜๋„๋ก ๋ณ€๊ฒฝํ•ฉ๋‹ˆ๋‹ค.
  • View ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜์—ฌ, MainView์˜ ํ•จ์ˆ˜๋“ค์„ ์˜ค๋ฒ„๋ผ์ด๋”ฉ ํ•˜์˜€์Šต๋‹ˆ๋‹ค.
class MainActivity : AppCompatActivity(), MainView {

    private lateinit var presenter: MainPresenter

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

        // Model ์ดˆ๊ธฐํ™”
        val model = DataModel()

        // Presenter ์ดˆ๊ธฐํ™”
        presenter = MainPresenter(this, model)

        // ๋ฐ์ดํ„ฐ ๋กœ๋“œ
        presenter.loadData()
    }

    override fun showLoading() {
        // ๋กœ๋”ฉ ํ‘œ์‹œ
    }

    override fun hideLoading() {
        // ๋กœ๋”ฉ ์ˆจ๊น€
    }

    override fun showData(data: List<String>) {
        // ๋ฐ์ดํ„ฐ๋ฅผ UI์— ํ‘œ์‹œ
    }

    override fun showError(error: String) {
        // ์—๋Ÿฌ ๋ฉ”์‹œ์ง€ ํ‘œ์‹œ
    }
}

ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ž‘์„ฑ

  • MVP ํŒจํ„ด์ด ํ…Œ์ŠคํŠธ ์ž‘์„ฑ์— ์šฉ์ดํ•œ์ง€ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด์„œ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด๋ณด์•˜์Šต๋‹ˆ๋‹ค.
  • Mocking ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ(Mockito)๋ฅผ ํ™œ์šฉํ•˜์—ฌ View๋ฅผ ๋ชจ์˜ ๊ฐ์ฒด๋กœ ๋งŒ๋“ค๊ณ , Presenter์˜ ๋™์ž‘์„ ๊ฒ€์ฆํ•˜์˜€์Šต๋‹ˆ๋‹ค.
class MainPresenterTest {

    private val mockView = mock(MainView::class.java)
    private val mockModel = mock(DataModel::class.java)
    private lateinit var presenter: MainPresenter

    @Before
    fun setup() {
        presenter = MainPresenter(mockView, mockModel)
    }

    @Test
    fun testLoadData() {
        val dummyData = listOf("Data1", "Data2")
        `when`(mockModel.fetchData()).thenReturn(dummyData)

        presenter.loadData()
        
        // verify๋ฅผ ํ†ตํ•ด์„œ ๋™์ž‘ ํ™•์ธ
        verify(mockView).showLoading()
        verify(mockView).showData(dummyData)
        verify(mockView).hideLoading()
    }
}

๊ฒฐ๋ก 

  • MVC ํŒจํ„ด์€ MVP ํŒจํ„ด์œผ๋กœ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ํ•˜์˜€์Šต๋‹ˆ๋‹ค.
  • ์ด๋กœ ์ธํ•ด์„œ ์•„๋ž˜์™€ ๊ฐ™์€ ์ด์ ์„ ์–ป์„ ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

์œ ์—ฐ์„ฑ ์ฆ๊ฐ€

  • MVP๋กœ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ํ•จ์œผ๋กœ์จ View์™€ Presenter์˜ ๋ถ„๋ฆฌ๊ฐ€ ๋ช…ํ™•ํ•ด์กŒ์Šต๋‹ˆ๋‹ค.
  • ์ด๋กœ ์ธํ•ด ์œ ์ง€๋ณด์ˆ˜์™€ ์ฝ”๋“œ ๊ด€๋ฆฌ๊ฐ€ ์‰ฌ์›Œ์กŒ์Šต๋‹ˆ๋‹ค.

ํ…Œ์ŠคํŠธ ๊ฐ€๋Šฅ์„ฑ

  • ๊ธฐ์กด์— Controller๋ฅผ ํ…Œ์ŠคํŠธํ•˜๊ธฐ ์–ด๋ ค์› ์ง€๋งŒ, Presenter๋กœ ์ „ํ™˜ํ•˜์—ฌ View์™€์˜ ์˜์กด์„ฑ์„ ๊ฐ์†Œ์‹œ์ผฐ์Šต๋‹ˆ๋‹ค.
  • ์ด๋กœ ์ธํ•ด์„œ UI ๋กœ์ง์„ Presenter์—์„œ ๋…๋ฆฝ์ ์œผ๋กœ ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ์–ด์„œ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ ์ž‘์„ฑ์ด ์šฉ์ดํ•ด์กŒ์Šต๋‹ˆ๋‹ค.

๊ฐœ๋ฐœ ์ƒ์‚ฐ์„ฑ

  • Presenter์™€ View๊ฐ€ ๋…๋ฆฝ์ ์ด๊ธฐ ๋•Œ๋ฌธ์— UI ๋ณ€๊ฒฝ ์‹œ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์˜ ๋ณ€๊ฒฝ์ด ํ•„์š” ์—†์–ด ๊ฐœ๋ฐœ ์†๋„๊ฐ€ ๋นจ๋ผ์ง‘๋‹ˆ๋‹ค.