MVP 패턴을 사용해보자

김흰돌·2023년 3월 8일
0

아키텍처 패턴

목록 보기
1/2

MVP에 들어가기 전 MVC 패턴이란?

MVC 패턴은 Model-View-Controller의 약어이며, 애플리케이션을 세 가지 주요 컴포넌트로 분리한다.

  • Model: 애플리케이션에서 사용되는 데이터와 비즈니스 로직을 담당한다. Model은 데이터를 가져오고 저장하며, View와 Controller의 변경 사항에 따라 업데이트된다.

  • View: 사용자 인터페이스를 나타내며, 사용자와 상호작용한다. View는 사용자 입력을 Controller로 전달하고, Controller가 처리한 결과를 화면에 표시한다.

  • Controller: View와 Model 사이에서 중개자 역할을 한다. Controller는 View로부터 사용자 입력을 받아 Model을 업데이트하고, Model의 변경 사항을 View에 전달한다.


MVC 패턴의 특징

  • Controller는 여러 개의 View를 선택할 수 있는 1:n 구조이다.
  • Controller는 Model에 직접적인 영향을 끼칠 수 있다.

단순한 패턴이기 때문에 사용하기 쉽다
하지만 View와 Model 사이의 의존성이 높아 유지보수가 어렵다
의존성이 높아 테스트 코드 작성에 불리하다


MVP 패턴이란?

MVP 패턴은 세 가지 주요 컴포넌트로 구성됨

  • Model: 애플리케이션에서 사용되는 데이터와 비즈니스 로직을 담당. 데이터를 가져오고 저장하며, View와 Presenter의 변경 사항에 따라 업데이트 된다.

  • View: 사용자 인터페이스를 나타내며, 사용자와 상호작용함, View는 사용자 입력을 Presenter로 전달하고, Presenter가 처리한 결과를 화면에 표시한다.

  • Presenter: View와 Model 사이에서 중개를 하는 역할을 함. Presenter는 View로부터 사용자 입력을 받아 Model을 업데이트하고, Model의 변경 사항을 View에 전달함. Presenter는 View와 Model의 독립성을 유지하면서 서로 연결하는 역할을 한다.

MVP 패턴의 특징

  • Presenter와 View가 1:1 관계이다.
  • View와 Modle은 서로를 알 필요가 없다.

Presenter가 중개를 해주기 때문에 View와 Model의 의존성이 없다.
View와 Presenter가 1:1로 강한 의존성을 가지게 된다.
각각의 View마다 Presenter가 존재하게 되어 코드량이 많아질 수 있다.

MVP 패턴 적용

이전에 올렸던 Retrofit으로 LOL API를 다뤄보자 프로젝트에 MVP 패턴을 적용해 볼 생각이다.

MainActivity에서 사용하는 getAPI() 함수를 분리하면 된다.

위에서 설명했듯 View는 사용자의 입력을 Presenter로 전달하고 Presenter가 처리한 결과를 화면에 표시하는 역할만 하면 되기 때문에 HTTP 통신을 통해 데이터를 가져오는 코드는 적절하지 못 하기 때문이다.

MainPresenter
View와 Model 사이를 중재하는 MainPresenter.kt 파일과 View와 Presenter 간의 통신을 위한 MainContract.kt 파일을 생성한다.

그리고 MainContract를 아래와 같이 작성해준다.

MainContract.kt

interface MainContract {
    interface View {
        fun getItem(response: Response<List<LOLResponse.LOLResponseItem>>)
    }

    interface Presenter {
        fun getAPI()
    }
}

Contract를 생성한 이유는 하나의 interface에 View와 Presenter를 정의하고, 이를 각각의 View와 Presenter에서 정의하는 목적으로 사용한다.

다음은 생성한 Presenter에 API 통신을 하는 코드를 옮겨 준다.

class MainPresenter(private val view: MainContract.View) : MainContract.Presenter {
    override fun getAPI() {
        val retrofitAPI = RetrofitConnection.getInstance().create(LOLService::class.java)
        retrofitAPI.getInformation(
            "API 키"
        ).enqueue(object : Callback<List<LOLResponse.LOLResponseItem>> {
            override fun onResponse(
                call: Call<List<LOLResponse.LOLResponseItem>>,
                response: Response<List<LOLResponse.LOLResponseItem>>
            ) {
                view.getItem(response)
            }

            override fun onFailure(call: Call<List<LOLResponse.LOLResponseItem>>, t: Throwable) {
                t.printStackTrace()
            }
        })
    }
}

MainContract의 Presenter를 상속받고 파라미터로 넘어온 MainContract.View를 가진다.

override fun getAPI()를 실행하고 API 호출이 잘 되었을 시 view.getItem으로
받아온 데이터들을 넘겨준다.

MainActivity

class MainActivity : AppCompatActivity(), MainContract.View {

    private val binding by lazy { ActivityMainBinding.inflate(layoutInflater) }
    private val adapter by lazy { RecyclerViewAdapter() }
    private val presenter by lazy { MainPresenter(this) }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(binding.root)
        binding.recyclerView.adapter = adapter
        binding.recyclerView.layoutManager = LinearLayoutManager(this)

        presenter.getAPI()
    }

    override fun getItem(response: Response<List<LOLResponseItem>>) {
        if (response.isSuccessful) {
            Toast.makeText(this@MainActivity, "데이터 가져오기 성공!", Toast.LENGTH_SHORT).show()
            response.body()?.let {
                setUI(it as ArrayList<LOLResponseItem>)
            }
        } else {
            Toast.makeText(this@MainActivity, "데이터 가져오기 실패...", Toast.LENGTH_SHORT).show()
        }
    }

    private fun setUI(lolData: ArrayList<LOLResponseItem>) {
        adapter.updateList(lolData)
        adapter.notifyDataSetChanged()
    }
}

MainContract.View를 상속받고 presenter 객체를 생성하여 getAPI 함수를 실행시킨다.

위에서 작성한 Presenter에서 데이터를 보내주면
상속받은 MainContract.View의 getItem 함수로 값을 받은 뒤 그 값을 화면에 뿌려준다.

0개의 댓글