
💡 MVP란?
view와 model 완전 분리 특징이 가장 두드러짐 [뷰와 프레젠터가 해야할 작업을 이미 인터페이스로 규격화 한 것이 특징. 모듈화된 작업 템플릿을 만들때 용이한 구조임
1) Model : MVC패턴의 모델과 같은 역할 [데이터 취급 : Item, Person 등]
2) View : 사용자가 볼 화면 및 이벤트 처리 [액티비티, 프래그먼트]
3) Presenter : 뷰와 모델의 중계 역할. 컨트롤러가 비슷하지만 인터페이스로 역할을 정해놓는 특징이 있음

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp"
tools:context=".view.MainActivity">
<EditText
android:id="@+id/et_name"
android:hint="이름"
android:inputType="text"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<EditText
android:id="@+id/et_email"
android:hint="이메일"
android:inputType="textEmailAddress"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<Button
android:id="@+id/btn_save"
android:text="save Data"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<Button
android:id="@+id/btn_load"
android:text="load Data"
android:backgroundTint="@color/teal_700"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="80dp"/>
<TextView
android:id="@+id/tv_result"
android:text="result"
android:textStyle="bold"
android:textColor="@color/black"
android:padding="8dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
package com.bsj0420.mvp.model
//이름 이메일 데이터 저장하는 역할의 데이터 클래스
data class UserVO(var name:String, var email:String)
package com.bsj0420.mvp.model
import android.content.Context
import androidx.core.content.edit
// 유저 정보(데이터)를 제어하는 기능 클래스
class UserModel constructor(val context: Context) { //데이터를 제어하기 위해서 콘텍스트 능력이 필요한 경우가 있다면
// 주 생성자로 전달 - 생성자 DI
// 1) 데이터를 전달 받아서 SharedPreferences에 데이터 저장하는 기능
fun saveData(name:String, email:String){
val pref = context.getSharedPreferences("data", Context.MODE_PRIVATE)
pref.edit {
putString("name", name)
putString("email", email)
}
}
// 2) SharedPreferences에서 데이터를 읽어와서 내보내는(return) 기능
fun loadData(): UserVO {
val pref = context.getSharedPreferences("data", Context.MODE_PRIVATE)
val name : String = pref.getString("name", "") as String //스트링으로 형변환
val email : String = pref.getString("email", "") as String
return UserVO(name, email) //코틀린은 리턴 하나밖에 못함 그래서 모델을 사용하여 리턴
}
}

package com.bsj0420.mvp.view
import android.content.Context
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import com.bsj0420.mvp.databinding.ActivityMainBinding
import com.bsj0420.mvp.model.UserVO
import com.bsj0420.mvp.presenter.MainContract
import com.bsj0420.mvp.presenter.MainPresenter
class MainActivity : AppCompatActivity(), MainContract.View {
// 2. MVP 패턴 [Model - View - Presenter]
// view와 model 완전 분리 특징이 가장 두드러짐 [뷰와 프레젠터가 해야할 작업을 이미 인터페이스로 규격화 한 것이 특징. 모듈화된 작업 템플릿을 만들때 용이한 구조임]
// 1) Model : MVC패턴의 모델과 같은 역할 [데이터 취급 : Item, Person 등]
// 2) View : 사용자가 볼 화면 및 이벤트 처리 [액티비티, 프래그먼트]
// 3) Presenter : 뷰와 모델의 중계 역할. 컨트롤러가 비슷하지만 인터페이스로 역할을 정해놓는 특징이 있음
//주요 특징
// view와 프레젠터가 해야 할 작업들을 미리 interface를 통해서 규격화
// # view 참조 변수 만들기
lateinit var binding : ActivityMainBinding
// # Presenter 참조변수
lateinit var presenter : MainPresenter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// # 프레젠터 객체 생성 및 초기화
presenter = MainPresenter()
presenter.initial(this)
// # view 로서의 역할
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
// # view로서 사용자 이벤트 처리 - 프레젠터를 통해서
binding.btnSave.setOnClickListener { presenter.clickSave(binding.etName.text.toString(), binding.etEmail.text.toString()) }
binding.btnLoad.setOnClickListener { presenter.clickLoad() }
}
// 기능 채우기
override fun showData(userVO: UserVO) {
binding.tvResult.text = "${userVO.name} : ${userVO.email}"
}
override fun getContext(): Context {
return this
}
}
-> 인터페이스로 기술해 둔다
package com.bsj0420.mvp.presenter
import android.content.Context
import com.bsj0420.mvp.model.UserVO
//View와 presenter 역할을 하는 클래스들이 가지고 있어야할 기능을 정하는 인터페이스
interface MainContract {
// # view 역할을 하는 클래스가 가져야할 기능을 기술한 인터페이스
interface View {
//반드시 있어야할 기능 (추상메소드)
//1) 모델의 데이터를 화면에 보여주는 기능
fun showData(userVO: UserVO)
//2) presenter에서 사용할 수 있는 콘텍스트를 리턴해주는 기능 필요!
fun getContext() : Context
}
// # presenter 역할을 하는 클래스가 가져야할 기능을 기술한 인터페이스
interface Presenter {
//사용자의 이벤트에 따라 처리할 기능들을 만든다 (2개 - 버튼 2개니까)
// view 역할 클래스의 요청에 의해 실행 될 메소드
// 1) save 버튼 클릭 했을 때
fun clickSave(name : String, email : String)
// 2) load 버튼 눌렀을 때
fun clickLoad()
}
}

package com.bsj0420.mvp.presenter
import com.bsj0420.mvp.model.UserModel
// 라면 가져야할 기능을 기술한 인터페이스를 구현하려 실제 기능을 작성
class MainPresenter : MainContract.Presenter{
//프레젠터는 뷰와 모델을 연결해야하기 때문에
//각각을 참조변수를 멤버로 보유한다
// var view : MainActivity //강한 결합은 x
var view : MainContract.View?= null //1. 뷰 역할을 수행하는 클래스는 반드시 MainContract.View 인터페이스를 구현하고 있기에
// 상속을 느슨하게 하기 위해 메인을 직접 부르지 않음
var model : UserModel ?= null //2. 모델 역할을 수행하는 클래스 참조변수
override fun clickSave(name: String, email: String) {
TODO("Not yet implemented")
}
override fun clickLoad() {
TODO("Not yet implemented")
}
}
package com.bsj0420.mvp.presenter
import com.bsj0420.mvp.model.UserModel
// 라면 가져야할 기능을 기술한 인터페이스를 구현하려 실제 기능을 작성
class MainPresenter : MainContract.Presenter{
//프레젠터는 뷰와 모델을 연결해야하기 때문에
//각각을 참조변수를 멤버로 보유한다
// var view : MainActivity //강한 결합은 x
var view : MainContract.View?= null //1. 뷰 역할을 수행하는 클래스는 반드시 MainContract.View 인터페이스를 구현하고 있기에
// 상속을 느슨하게 하기 위해 메인을 직접 부르지 않음
var model : UserModel ?= null //2. 모델 역할을 수행하는 클래스 참조변수
//Presenter가 연결한 2개의 참조변수를 생성 및 전달받는 메소드 정의
//생성자 생성!
fun initial (view : MainContract.View){
this.view = view
model = UserModel(view.getContext())
}
override fun clickSave(name: String, email: String) {
TODO("Not yet implemented")
}
override fun clickLoad() {
TODO("Not yet implemented")
}
}
기능 처리 후 view에게 다시 돌려줘야함
package com.bsj0420.mvp.presenter
import com.bsj0420.mvp.model.UserModel
import com.bsj0420.mvp.model.UserVO
//뷰한테 어떤 동작을 하는지 얘기 듣고 처리 후 뷰에게 다시 요청
// 라면 가져야할 기능을 기술한 인터페이스를 구현하려 실제 기능을 작성
class MainPresenter : MainContract.Presenter{
//프레젠터는 뷰와 모델을 연결해야하기 때문에
//각각을 참조변수를 멤버로 보유한다
// var view : MainActivity //강한 결합은 x
var view : MainContract.View?= null //1. 뷰 역할을 수행하는 클래스는 반드시 MainContract.View 인터페이스를 구현하고 있기에
// 상속을 느슨하게 하기 위해 메인을 직접 부르지 않음
var model : UserModel ?= null //2. 모델 역할을 수행하는 클래스 참조변수
//Presenter가 연결한 2개의 참조변수를 생성 및 전달받는 메소드 정의
//생성자 생성!
fun initial (view : MainContract.View){
this.view = view
model = UserModel(view.getContext())
}
// 뷰의 save 버튼 클릭 이벤트를 대신 처리해 주는 기능 메소드
override fun clickSave(name: String, email: String) {
//모델에서 저장 요청
model?.saveData(name, email)
}
override fun clickLoad() {
//모델에게 데이터 요청
var user : UserVO? = model?.loadData()
//view 에게 데이터 출력 요청
user?.let {
view?.showData(it)
}
}
}