🎈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
)
}
}
}
takeView
는 View와 Presenter를 연결해주는 역할을 합니다.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()
}
SearchContract.View
를 implements 해주고, Presenter를 이용하여 뷰를 이어주어야 하기 때문에 SearchPresenter()
도 선언해줍니다.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으로 바꾸어보기!