공상하는 개발자

[아키텍쳐 패턴] 2탄 : MVP 패턴 적용하기 본문

개발/안드로이드

[아키텍쳐 패턴] 2탄 : MVP 패턴 적용하기

공상과학소설 2020. 1. 19. 22:56
반응형

1탄 : Model 분리하기 에서는...

지난 시간에는 모델과 뷰가 서로 상호작용을 했다. 하지만 그러한 과정 또한 세분화해서 나누고 싶어졌다.

그래서 나오게 된 MVP 패턴에 대해 공부해보도록 하겠다.

 


MVP 패턴이란?

MVP 패턴은 Model + View + Presenter 를 합친 패턴이다.

Model 과 View가 서로 직접적인 상호작용을 하는 것이 아니라 Presenter 라는 중간 다리를 놓아서 상호작용을 한다.

 

1. 구조

  • Model : 어플리케이션에서 사용되는 데이터와 그 데이터를 처리하는 부분.
  • VIew : 사용자에게 보여지는 UI
  • Presenter : View 에서 요청한 정보로 Model을 가공하여 View에 전달해 주는 부분이다. (예를 들어 view에서 함수를 통해 프레젠터에게 어떤 요청을 하면 presenter 내부에서 가공한 데이터를 view로 보내주는 역할을 한다.)

 

2. 특징

  • Presenter 를 통해 Model과 View의 의존성을 없앨 수 있다.
  • 하나의 presenter 당 하나의 View를 사용하는게 국룰이다.
  • View 와 Presenter 사이의 의존성이 높아진다.

3. 동작

  1. View 에서 사용자 이벤트 수신 (버튼 클릭)
  2. View 에서 Presenter 이벤트 호출 (클릭 할때 함수 호출)
  3. Presenter 에서 Model에 데이터 요청 (presenter 함수에서 레트로핏 데이터 요청)
  4. Model에서 Presenter로 데이터 전달 (데이터 가공 후 전달)
  5. Presenter 에서 전달받은 데이터를 기반으로 View 업데이트
  6. View 에서 화면 업데이트

구현

1. Contract interface 구현 (View 와 Presenter 에서 필요한 함수 모음)

interface MainContract {
    interface View {
        fun showNoResult()
        fun showNotAvailableKeyword()
        fun showResult(data: NaverApi)
        fun showNotExistKeyword()
        fun showDataNum(num:Int)
    }

    interface Presenter {
        fun getMovieData(keyword: String)
        fun getMovieDataNum(data:NaverApi)
    }
}

 

 

2. MainPresenter 구현

class MainPresenter(val view: MainContract.View) :
    MainContract.Presenter {
    private var naverRepository: NaverRepository = NaverRepository()

    override fun getMovieData(keyword: String) {
        if (keyword.contains("@")) {
            view.showNotAvailableKeyword()
            return
        }
        if (keyword.equals("")) {
            view.showNotExistKeyword()
            return
        }
        naverRepository.getMovieData(keyword, success = {
            if (it.item.isEmpty()) {
                view.showNoResult()
            } else {
                getMovieDataNum(it)
                view.showResult(it)

            }
        },
            fail = {
                Log.e("error is :", it.toString())
            })
    }

    override fun getMovieDataNum(data: NaverApi) {
        view.showDataNum(data.total)
    }
}

 

3. View 에서 Presenter 에서 받은 데이터 뿌려주기

class MainActivity : AppCompatActivity(),
    MainContract.View {

    private lateinit var movieAdapter: MovieAdapter
    val presenter: MainContract.Presenter = MainPresenter(this)
    lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)

        binding.mainActivity = this@MainActivity
        movieAdapter = MovieAdapter(object : MovieAdapter.MovieViewHolder.ItemClickListener {
            override fun onItemClick(position: Int) {
                val goWebView = Intent(this@MainActivity, WebviewActivity::class.java)
                goWebView.putExtra(URL, movieAdapter.getMovieLink(position))
                startActivity(goWebView)
            }
        })
        binding.rvMovie.adapter = movieAdapter


    }

    fun callMovie() {   //수정완료
        presenter.getMovieData(binding.etSearch.text.toString())
    }

    override fun showNoResult() {
        Toast.makeText(this, "검색결과가 없습니다.", Toast.LENGTH_SHORT).show()
    }

    override fun showNotExistKeyword() {
        Toast.makeText(this, "검색어를 입력하세요.", Toast.LENGTH_SHORT).show()
    }

    override fun showNotAvailableKeyword() {
        Toast.makeText(this, "유효하지 않는 키워드입니다.", Toast.LENGTH_SHORT).show()
    }

    override fun showResult(data: NaverApi) {
        movieAdapter.setMovieItemList(data.item)
    }

    override fun showDataNum(num: Int) {
        Toast.makeText(this, "총 " + num + "개가 검색되었습니다.", Toast.LENGTH_SHORT).show()
    }
}

 

 

 

반응형
Comments