[Android] MVP에 대해

dada·2022년 4월 29일
0

Android

목록 보기
4/4
post-custom-banner

MVP란

  • MVP 패턴이란 Model, View, Presenter의 첫 글자를 따서 이름이 지어졌습니다. MVP의 핵심 설계는 MVC와는 다르게 UI(View)와 비즈니스 로직(Model)을 분리하고, 서로 간에 상호작용을 다른 객체(Presenter)에 그 역할을 줌으로써 서로의 영향(의존성)을 최소화하는 것에 있습니다.

🎈Model - DB, REST API 등 비즈니스 로직 및 데이터와 관련된 처리역할을 하는 부분입니다.

🎈View - Activity 와 Fragment 같이 View를 담당하는 사용자 인터페이스 역할을 하는 부분입니다.

🎈Presenter - View에서 이벤트를 받아서 데이터를 가공하여 Model 에 전달하는 역할을 하는 부분입니다. View와 Model의 다리 역할을 해줍니다.


🐥구현해보기

저는 Github Api를 이용해서 연습해보도록 하겠습니다.
간단히 repository를 검색할 수 있는 searchApi를 이용하여 검색 어플리케이션을 만들어봅시다.

1. Contract 구현하기
우선 View와 Presenter가 구현해야할 인터페이스를 정의 하는 Contract를 구현하여 전체적인 틀을 잡아줍니다.

//SearchContract.kt

interface SearchContract {
    interface View {
        fun progressbarVisible(visible: Boolean)
        fun showResult(result: List<ResultDetail>)
    }

    interface Presenter {
        fun takeView(view: View)
        fun getSearchResult(query: String)

        interface Model {
            fun getSearchResult(
                query: String,
                onSuccess: (List<ResultDetail>) -> Unit,
                onFailure: (String) -> Unit
            )
        }
    }
}
  • View에는 결과를 보여주는 함수와 로딩 프로그레스바와 관련된 함수를 정의해줍니다.
  • Presenter에 있는 takeView는 View와 Presenter를 연결해주는 역할을 합니다.
  • Model은 Api로부터 결과를 얻어오는 함수를 정의합니다.

2. Model로 부터 Api 결과 받아오기

//DataListModel.kt

class DataListModel : SearchContract.Presenter.Model {
    override fun getSearchResult(
        query: String,
        onSuccess: (List<ResultDetail>) -> Unit,
        onFailure: (String) -> Unit
    ) {
        val searchApiCall = Retrofit.githubApi.getSearchList(query)
        searchApiCall.enqueue(object : Callback<Result> {
            override fun onResponse(call: Call<Result>, response: Response<Result>) {
                if (response.isSuccessful) {
                    response.body()?.let { onSuccess(it.items) }
                }
                onFailure("error")
            }

            override fun onFailure(call: Call<Result>, t: Throwable) {
                Log.d("fail", t.message.toString())
                onFailure("error${t.message}")
            }

        })
    }
}
  • SearchContract.Presenter.Model를 implements하고 getSearchResult 함수에 Api로부터 데이터를 받아오는 코드를 작성합니다.

3. Presenter 구현하기

//SearchPresenter.kt

class SearchPresenter : SearchContract.Presenter {

    private lateinit var searchView: SearchContract.View

    override fun takeView(view: SearchContract.View) {
        searchView = view
    }

    override fun getSearchResult(query: String) {
        DataListModel().getSearchResult(query,
            onSuccess = { result -> searchView.showResult(result) },
            onFailure = { error -> Log.d("error", error) })
    }
}

Presenter에서는 View와 Model를 연결해주는 역할을 합니다. 그렇기 때문에 takeView() 함수를 통해 view를 연결해주고, getSearchResult 함수는 Model로 부터 얻어온 데이터를 전달해줍니다.

4. View 구현하기

//MainActivity.kt

class MainActivity : AppCompatActivity(), SearchContract.View {
    private lateinit var searchPresenter: SearchPresenter
    private lateinit var binding: ActivityMainBinding
    private val adapter: SearchResultAdapter by lazy {
        SearchResultAdapter()
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        searchPresenter = SearchPresenter()
        searchPresenter.takeView(this)

        setAdapter()
    }
  • MainActivity에 SearchContract.View를 implements 해주고, Presenter를 이용하여 뷰를 이어주어야 하기 때문에 SearchPresenter()도 선언해줍니다.
  • 검색 Query가 들어오면 SearchPresenter에 getSearchResult를 이용하여 결과값을 가져오기 때문에 SearchView를 사용하여 제출되었을 때 호출하도록 작성하였습니다.
searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
            override fun onQueryTextSubmit(query: String?): Boolean {
                if (query != null) {
                    progressbarVisible(true)
                    searchPresenter.getSearchResult(query)
                }
                return false
            }

            override fun onQueryTextChange(newText: String?): Boolean {
                return false
            }
        })
        return super.onCreateOptionsMenu(menu)
    }

그 후 View안에 정의되었던 두 함수의 내용을 작성해줍니다.

    override fun progressbarVisible(visible: Boolean) {
        if (visible) binding.progressBar.visibility = View.VISIBLE
        else binding.progressBar.visibility = View.INVISIBLE
    }

    override fun showResult(result: List<ResultDetail>) {
        adapter.submitList(result)
        progressbarVisible(false)
    }

🧐🧐🧐 회고
MVP가 아직 익숙하지 않아서 어렵고 서툰점도 아직 많지만..개념만 익힐때와는 달리 머리에 잘 들어와서 의미있는 실습이었다. 제가 작성한 코드가 틀릴 수도 있으니 댓글로 피드백도 환영입니다... 다음은 MVVM으로 바꾸어보기!

profile
기록하기
post-custom-banner

0개의 댓글