마스터Q&A 안드로이드는 안드로이드 개발자들의 질문과 답변을 위한 지식 커뮤니티 사이트입니다. 안드로이드펍에서 운영하고 있습니다. [사용법, 운영진]

답변을 바탕으로 다시코드 수정해봤는데 이렇게하면 괜찮을까요?

0 추천

LoginInterface

interface LoginInterface {

    interface ViewInterface{
        //메시지 전달
        fun MakeToast(msg:String)


    }
    interface PresenterInterface{
        //성공시 main으로 화면전환
        fun successToLogin()

        fun moveToSignup()
    }

}

 

LoginPresenter

class LoginPresenter() {
    lateinit var userModel: UserModel
    var viewInterface : LoginInterface.ViewInterface?=null
    var presentinterface : LoginInterface.PresenterInterface? =null

    fun Login(email: String, password: String) {
        if (email.isEmpty() || password.isEmpty()
        ) {
            viewInterface?.MakeToast("아이디와 비밀번호를 모두 입력해주세요")
        } else {
            userModel = UserModel(email, password)
            requestLogin(userModel)
        }

    }


    fun requestLogin(userModel:UserModel){
        var auth = FirebaseAuth.getInstance()
        auth?.signInWithEmailAndPassword(userModel.email, userModel.password)
            ?.addOnCompleteListener { task ->
                if (task.isSuccessful) {
                    viewInterface?.MakeToast("로그인 완료")
                    presentinterface?.successToLogin()
                } else {
                    viewInterface?.MakeToast("아이디 또는 비밀번호가 틀렸습니다")
                }
            }
    }
    fun moveToSignup(){
        presentinterface?.moveToSignup()
    }

    fun onStart(viewInterface: LoginInterface.ViewInterface,presenterInterface: LoginInterface.PresenterInterface ){
        this.viewInterface=viewInterface
        this.presentinterface=presenterInterface
    }
    fun onStop(){
        this.viewInterface=null
        this.presentinterface=null
    }

}

LoginActivity

class LoginActivity : AppCompatActivity(), View.OnClickListener, LoginInterface.ViewInterface, LoginInterface.PresenterInterface {
    private lateinit var binding: ActivityLoginBinding
    val TAG: String = "로그"
    var loginPresenter: LoginPresenter? = null


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityLoginBinding.inflate(layoutInflater)
        setContentView(binding.root)
        binding.btnLogin.setOnClickListener(this)
        binding.btnSignup.setOnClickListener(this)


        loginPresenter = LoginPresenter()

    }

    override fun onStart() {
        loginPresenter?.onStart(this,this)
        super.onStart()
    }

    override fun onStop() {
        loginPresenter?.onStop()
        loginPresenter = null
        super.onStop()
    }

    override fun onClick(v: View?) {
        when (v?.id) {
            R.id.btn_login -> {
                loginPresenter?.Login(binding.tvEmail.text.toString(), binding.tvPassword.text.toString())
            }
            R.id.btn_signup -> {
                loginPresenter?.moveToSignup()
            }
        }
    }


    /***
     * 기능함수
     */
    override fun MakeToast(msg: String) {
        Toast.makeText(this, msg, Toast.LENGTH_SHORT).show()
    }

    override fun successToLogin() {
        startActivity(Intent(this, MainActivity::class.java))
    }

    override fun moveToSignup() {
        startActivity(Intent(this, SignupActivity::class.java))
    }


}

 

지금보니까  presenterInterface에있는게 전부다 viewinterface에 들어가야하는것 같기도하고..

원피스를찾은남자신현호 (170 포인트) 님이 2022년 11월 5일 질문
원피스를찾은남자신현호님이 2022년 11월 5일 수정

1개의 답변

0 추천

LoginInterface는 MVP를 사용할 때 개발자들이 가독성을 위해 즐겨사용하던 명명규칙을 사용해 보세요. (다시 한번 강조하지만, naming이 아주 중요합니다. 가능하면 더 좋은 이름을 짓는 연습을 하세요.) 코딩의 상당시간은 코드 작성이 아니라 코드를 읽는데 보내게 됩니다.
View 에는 View에 필요한 동작을, Presneter에는 Presenter에 맞는 동작을 함수로 포함시키세요. 예를 들면, moveToSignUp은 View 에서 필요한 함수이지, Presenter가 필요한 함수가 아닙니다. 이게 잘 이해가 가지 않으신다면 함수의 몸체 없이 함수이름만 가지고 흐름을 먼저 훓어보시기 바랍니다. 다이어그램을 그려봐도 좋구요.

interface LoginContract {
 
    interface View{
        ...
        fun moveToSignup()
    }

    interface Presenter{
        fun login(emai: Sring, password: String)
    }

}

 

LoginPresenter는 LoginContract.Presenter를 구현하셔야 하고, LoginConract.View를 멤버변수로 기지셔야 합니다.
 

class LoginPresenter(
    var view: LoginContract.View? = null
): LoginContract.View {

  private val auth by lazy { FirebaseAuth.getInstance() }
    

   fun login(email: String, password: String) {
        if (email.isEmpty() || password.isEmpty()
        ) {
            viewInterface?.MakeToast("아이디와 비밀번호를 모두 입력해주세요")
            return
        }   

          
         // userModel = UserModel(email, password)  - 붚필요한 객체. 필요시 Firebsse에서 축축할 수 있음.
         requestLogin(email, password)
    }
 

    // private 사용
    private fun requestLogin(email: String, pasword: Stringl){
        auth.signInWithEmailAndPassword(email, password)
            .addOnCompleteListener { task ->
                if (task.isSuccessful) {
                    view?.MakeToast("로그인 완료")
                    view?.moveToSignup()
                } else {
                    view?.MakeToast("아이디 또는 비밀번호가 틀렸습니다")
                }
            }
    }
 
     // 님의 경우 onStart, onStop은 불필요.
}

 

 

LoginActivity는 LoginView만 구현하시면 되고 LoginPresenteer는 non-null타입을 사용하시면 됩니다.

class LoginActivity : AppCompatActivity(), View.OnClickListener, LoginContract.View {
    ...
    
    private lateinit var presenter: LoginContact.Presenter 
 
    override fun onCreate(savedInstanceState: Bundle?) {
        ...
         
        presenter = LoginPresenter()
 
    }
 
    override fun onStart() {
        presenter.view = this
        ...
    }
 
    override fun onStop() {
        ...
        presenter.view = null
        ...
    }
 
    
   ...
}


그리고 View 인터페이스와  Presenter인터페이스는 꼭 필요한 동작만 외부에 노출시키면 되고, 함수명을 가능하면 너무 구체적인 것 보다는 좀 더 일반적이 이름을 사용하세요. 예를 들면,

MakeToToast 보다는 showMessage가 더 적합합니다. Presenter는 View가 Toast를 사용할지 Snackbar를 사용하지, Dialog를 사용할지 알 필요가 없습니다. 그리고 이 보다 좀 더 유연한 접근방법은 성공이나 에러메세지를 Presneter에서 구체적으로 지정하지 말고 이벤트형태로 View에 던져주고 거기에 필요한 처리는 View에서 알아서 하도록 하는게 추후에 변경사항같은 것을 적ㅇ요하기에 더 좋습니다. 예를 들면,

enum class LoginUiMessage(@StringRes val resId) {
      INVALID_ID_OR_PASSWORD(R.string.invalid_id_or_password),
       LOGIN_FAILED(R.string.login_failed)
}

view?.onLoginMessage(LoginUiMessage.INVALID_ID_OR_PASSWORD)

// or
sealed class LoginUiEvent {
    object LoginScueess: LoginUiEvent()
    object LoginFailure: LoginUiEvent()
}
view?.onLoginEvent(LoginUiEvent.LoginScueess)

 

그리고 가능하면 메세지 같은 경우는 문자열을 하드코딩하지 마시고 안드로이드 플랫폼의 장점을 활용하기 위해 resource를 이용해서 처리하시는 습관을 들이시기 바랍니다.

spark (227,530 포인트) 님이 2022년 11월 5일 답변
덕분에 각각의 역할이뭔지 이제야 이해됬네요..
다시 정리하고 알려주신내용으로 작성하도록 습관드리겠습니다!
해당내용 참고해서 수정후 블로그에작성해두고싶은데 괜찮을까요혹시??
그럼요, 대신 제거는 예시이니까 그대로 사용하지는 마시고 님이 좀 더 적절하게 다듬으셔서 올리시면 더 좋을 것 같네요  더 나아가서 unit test도 작성해 보심 더 좋을 거 같구요.
...