삽질도사

[안드로이드] android MVC, MVVM, MVP 차이점 및 특징 본문

안드로이드

[안드로이드] android MVC, MVVM, MVP 차이점 및 특징

삽질도사 2021. 11. 30. 19:31
반응형

프로젝트 처음 진행할 때에 MVC,MVVM,MVP의 개념을 저도 잘 모르고 기능 하는대로 막쓰다가 나중에 알고 갈아엎었는데, 시간이 지나고 다시 보니 갈아엎은 것도 뒤죽박죽 엉망이더라구요! 

 

하지만 그때 당시엔 그런 것을 깨닫고 최선을 다한 결과여서 후회라기보단 영광의 상처같은 느낌이 더 큰데요,

이런 아키텍쳐(?)는 알면 알수록 복잡하고 사실 사람마다 주장하는게 조금씩 달라서 논란의 여지가 되곤 하기 때문에

코드를 직접 치면서 몸으로 느끼고, 시간이 지나서 다시 리뷰함으로써 조금씩 깨닫게 되는 것 같습니다.

 

여튼! 각각 사람마다 구현하는 모습도 다르고 DataBinding에 따라서 조금씩 차이가 있을테니,

아키텍쳐의 차이가 하~나도 이해안가고 복잡해서 헷갈리시는 분들이 쉽게 접근하시고, 저 또한 그랬으니 저도 블로그로 잊지않기 위해 가볍게 쓴 글이니 편하게 봐주시고 수정할 점 있으면 댓글주시면 감사하겠습니다~!~  

 

또한 코드는 Kotlin이지만 Java를 하셨으면 읽을 수 있는 정도의 쉬운 구조이니, 마음으로 읽으시면 됩니다.

 

MVC

 

아키텍쳐에서 이름을 보시면 MV(C), MV(VM), MV(P) 처럼 괄호 친 녀석 외에 M:데이터,V:보여지는 화면 이라고 생각하시면 될 것 같습니다.

 

처음에 정말 헷갈리는게 MVC만 C가 Controller로써 쓰이고 이것은 Activity같은 곳의 코드를 의미하는 데 (controller의 의미로써),

다른 녀석들은 V가 Activity와 같은 내부코드입니다. 헷갈리면 사진과 코드보면서 이해하시면 편합니다!

 

 

Activity 코드 내에서 Model과 View를 모두 쓰는 모습

 

DataBinding을 쓰지 않고 일반적인 경우에 우리가 처음 안드로이드 배울때, 화면에서 버튼클릭하면 데이터가 짜잔 나타나는 그런 코드라고 생각하시면 됩니다.

 

Model를 Data를 가져오는 일반적인 사용자 커스텀 Class이고, View는 아시다시피 layout xml입니다.

 

Model

class MainModel(val context: Context) {

    fun getListDatas(): ArrayList<String> {

        val datas: ArrayList<String> = ArrayList<String>()
        //dbms
        val helper=DBHelper(context)
        val db=helper.readableDatabase
        val cursor = db.rawQuery("select * from tb_test", null)
        while(cursor.moveToNext()){
            datas.add(cursor.getString(1))
        }

        db.close()
        return datas
    }

    fun addItem(item: String){
        val helper=DBHelper(context)
        val db=helper.writableDatabase
        db.execSQL("insert into tb_test (todo) values (?)", arrayOf(item))
        db.close()
    }
}

Controller

그냥 MainActivity 클래스 입니다~

Main안에 Model이 생성된 것을 볼 수 있죠? 그리고 클릭 이벤트에서 화면처리를 하였습니다.

class MVCMainActivity : AppCompatActivity(), View.OnClickListener {

    lateinit var datas: ArrayList<String>
    lateinit var adapter: ArrayAdapter<String>
    val model=MainModel(this)

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

        button.setOnClickListener(this)
        datas=model.getListDatas()
        adapter=ArrayAdapter(this, android.R.layout.simple_list_item_1, datas)
        listView.adapter = adapter
    }

    override fun onClick(v: View?) {
        val data: String = editView.text.toString()
        model.addItem(data)
        datas.add(data)
        adapter.notifyDataSetChanged()
        editView.setText("")
    }
}

말이 필요없습니다! 일반적인 구조이고 Model이라는 클래스가 데이터를 가져다 주는구나~ 생각하시면 되요!

 

MVVM

 

MVVM 안드로이드의 꽃이라고 저는 생각하는데요, 여기서 M은 Model로 동일하고, V는 View == MainActivity입니다.

추가 된 점은 VM == ViewModel 인데요.

 

ViewModel은  View(MainActicity)가 MVC에서 하던 데이터와 화면업데이트를 대신 해주게 됩니다. (MVC와 차이점)

View에서 이벤트가 발생하면 ViewModel이 실행되며, ViewModel에서 Model로부터 데이터를 가져옵니다.

그리고 그 데이터로 화면까지 업데이트하는 것이죠.

 

또한, View는 ViewModel을 관찰(Observe)하며, 데이터의 변화를 알아차리고 lifecycle에 따라 눈치봐서 변화를 적용시킵니다.(일반적으로 liveData를 사용한다면,) 따라서 데이터가 실시간으로 관찰되며 생명주기로부터 안전하므로 메모리 릭을 방지할 수 있습니다. (최고의 특징,장점)

 

Main이 ViewModel을 품고 있고 신호를 주면 ViewModel이 알아서 다 합니다.

View(Activity)

class MVVMMainActivity : AppCompatActivity(), View.OnClickListener {

    val viewModel: MainViewModel= MainViewModel(this)

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

        button.setOnClickListener(this)
        viewModel.onCreate()
    }

    override fun onClick(v: View?) {
        val data: String = editView.text.toString()
        editView.setText("")
        viewModel.onClick(data)
    }
}

라이프사이클 혹은 이벤트에 의한 Model수행을 ViewModel에게 위임하는 것을 볼 수 있습니다.

 

ViewModel

 class MainViewModel(val activity: Activity){

    lateinit var datas: ArrayList<String>
    lateinit var adapter: ArrayAdapter<String>
    val model=MainModel(activity)

    fun onCreate(){
        datas=model.getListDatas()
        adapter= ArrayAdapter(activity, R.layout.simple_list_item_1, datas)
        activity.listView.adapter = adapter
    }

    fun onClick(data: String){
    
        model.addItem(data)
        datas.add(data)
        adapter.notifyDataSetChanged()
    }
}

MainActivity가 왕이면 ViewModel이 행동대장입니다.

 

MVVM과 MVC의 차이점.

 

처음에 제일 헷갈리는 부분이 MVC와 MVVM의 차이점이고, 도대체 MVVM을 왜 써야하나? 라는 생각이 들면 아래 그림을 보시면 됩니다.

 

저는 MVC는 데이터 로직이 쉽고 처리가 단조로우면 사용하고,

MVVM은 실시간 업데이트가 필요할 때, 데이터 로직이 복잡할 때 또는 다양하게 Class가 재활용되면 사용합니다.

MVC처럼 한 곳에서 뷰와 통신,내부로직을 전부 관리하기보다는 viewModel에서 독립적으로 로직을 처리하고 그 결과를 보내주면

뷰를 관리하는 View에서는 그 결과를 뿌리기만 하면 되고, 그렇게 모두 독립적인 성질을 가지게 되면, 재활용이 쉽고 가독성이 좋아집니다.

(*구조가 복잡해져서 처음엔 이해하기 힘든데 익숙해지면 한 곳에 다 때려박은 코드가 제일 읽기 힘듬)

MVC와 MVVM 코드의 차이점

 

MVP

 

MVP의 P는 Presenter입니다.

 

Presenter는 View에서 발생한 이벤트 신호에 Model을 실행합니다. (View->Presenter->Model)

Model의 실행결과를 Presenter에게 주고 Presenter가 View에 데이터를 넘겨줍니다. (Model->Presenter->View)

 

결국 P는 특별한 역할을 담당한다기 보다는 Model과 View의 징검다리입니다.

ViewModel과 가장 큰 차이는 P는 직접 View를 핸들링 하지는 않는다는 점입니다.

물론 작성방법에 차이는 있지만 P는 그저 Interface이고 이를 활용하는 것은 코더의 몫입니다.
개인적으론 몇몇 경우를 제외하곤 메인구조로 사용하지 않습니다. 

 

Presenter

 

interface IMainPresenter {

    fun getListView(): ArrayList<String>
    fun addItem(item: String)
    
    interface IView {
        fun updateListView(item: String)
    }
}

위의 Presenter의 interface를 작성하기위해 MainPresenter를 하나 만들고 내부에 생성된 interfaceI인 IView는 View(MainActivity)에서 오버라이드할 것입니다.

 

MainPresenter

 class MainPresenter(val context: Context): IMainPresenter {

    var model: MainModel= MainModel(context)
    lateinit var view: IMainPresenter.IView

    override fun getListView(): ArrayList<String> {
        return model.getListDatas()
    }

    override fun addItem(item: String) {
        model.addItem(item)
        view.updateListView(item)
    }
}

 

View(Activity)

class MVPMainActivity : AppCompatActivity(), View.OnClickListener, IMainPresenter.IView{

    val presenter: MainPresenter = MainPresenter(this)
    lateinit var datas: ArrayList<String>
    lateinit var adapter: ArrayAdapter<String>

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
        button.setOnClickListener(this)
        presenter.view=this
        datas=presenter.getListView()
        adapter= ArrayAdapter(this, android.R.layout.simple_list_item_1, datas)
        listView.adapter = adapter
    }

    override fun onClick(v: View?) {
        val data: String = editView.text.toString()
        presenter.addItem(data)
    }

    override fun updateListView(item: String) {
        datas.add(item)
        adapter.notifyDataSetChanged()
    }
}

 

View에서 결국 P를 호출하고 P가 데이터를 받으면 View에게 전달하며 View에서 혹은 P코드 내에서 화면을 업데이트 할 수 있는 구조입니다.

 

중요한 점은 P는 interface이고, 그저 징검다리의 역할을 한다는 것이죠.

 

 

좀 더 디테일한 내용은 밑에 두 출처를 순서대로 읽으시면 쉽게 이해할 수 있습니다. 정말 이해하기 쉽게 되어있어요

 

출처:

https://kkangsnote.tistory.com/m/9

 

Android MVC, MVVM, MVP

MVVM, MVP를 적용한 Android App 개발의 코드적인 디테일은 작성자에 따라 다르다. 특정 Framework을 이용한 적용이 아님으로 구현 방법에 차이가 날수 밖에 없을것 같다. 원래 예전부터 있었던 개념인데

kkangsnote.tistory.com

https://blog.yena.io/studynote/2019/03/16/Android-MVVM-AAC-1.html

반응형