Kotlin - 로그인, 로그아웃 이게 맞을까?
Mobile/Kotlin

Kotlin - 로그인, 로그아웃 이게 맞을까?

뉴비뉴 2022. 4. 28.

안녕하세요,

 

오늘은 로그인, 로그아웃, Shared Preference, visibility,Interceptor, GONE,

Retrofit, OkHttpClient 에 대해서 정리해보겠습니다.

간단히 제 코드만 올린 상태이고, 추가 설명은 주석처리 해놨습니다.

 

MainActivity에 관한 내용입니다. 

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val btnSignUp = findViewById<Button>(R.id.btnSignUp) as Button // 첫 번째 회원가입을 누르면 JoinActivity

        val email = MyApplication.prefs.getString("email", "") // 로그인이 되어 있다면 값을 가져오고, 안되어 있다면 ""를 설정한다.
        val password = MyApplication.prefs.getString("password", "")

        if (email == "" && password == ""){ // 로그인이 안되어 있다고 과정하고 로그인 페이지를 띄운다.
            val btnLogin = findViewById<Button>(R.id.btnLogin) as Button
            btnLogout.visibility = View.GONE // GONE-사라지다. 로그인이 안되어 있으면 로그아웃을 볼 필요가 없으므로 GONE 한다.
            btnLogin.setOnClickListener{
                val intent = Intent(this, LoginActivity::class.java)
                startActivity(intent)
            }
        } else {
            val btnLogout = findViewById<Button>(R.id.btnLogout) as Button
            btnLogin.visibility = View.GONE // 반대로 로그인이 되어 있는 상태에서 로그인을 볼 필요가 없기에 GONE 한다.
            btnLogout.setOnClickListener{
                val intent = Intent(this, LogoutActivity::class.java)
                startActivity(intent)
            }
        }
        btnSignUp.setOnClickListener{ // 요건 회원가입
            val intent = Intent(this, JoinActivity::class.java)
            startActivity(intent)
        }

    }
}

다음 LoginActivity

class LoginActivity : AppCompatActivity() {
    // Login 에서 Shared Preferences
    // Shared Preferences 는 간단한 값을 저장할 때 주로 사용한다. 초기 설정 값이나 **자동 로그인 여부** 등 간단한 값을
    // 저장할 때 DB를 사용하면 복잡하기 때문에 Shared Preferences 를 사용하면 적합하다.
    // 로그아웃 할 때 Shared Preferences 데이터를 가져와 세션을 같으 삭제하면 완벽한 로그아웃이 완성?!
    
    lateinit var email: EditText
    lateinit var password: EditText
    lateinit var loginBtn: Button

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_login)

        email = findViewById(R.id.login_email)
        password = findViewById(R.id.login_password)
        loginBtn = findViewById(R.id.login_btnLogin)

        loginBtn.setOnClickListener {
            val email = email.text.toString().trim()
            val password1 = password.text.toString().trim()
            var state: Boolean?

            val api = Api.create() // 여기에 보이는 Api가 headerInterceptor, OkHttpClient, Retrofit 을 갖고 있다.
            val data = LoginResponse(email, password1) // 로그인 data class를 가져온다.

            api.userLogin(data).enqueue(object: Callback<LoginBackendResponse> { 
            // userLogin @Body에는 request 값을 넣어주고, 
            // ): Call<> 에는 리스폰스 값을 넣어준다.
            // data class LoginResponse(val email: String, val password: String)
			// data class LoginBackendResponse(val code: String, val message: String, val token: String? = null)
            
                override fun onResponse(call: retrofit2.Call<LoginBackendResponse>, response: Response<LoginBackendResponse>) { // 성공한 경우
                    state = if(response.body()?.code == "200"){ // Node.js 에서 200값이 response로 오게되면 if
                        MyApplication.prefs.setString("email", email) // Shared Preferences setString
                        MyApplication.prefs.setString("password", password1)

                        val intent = Intent(this@LoginActivity, HomeActivity::class.java) // 화면을 이동한다.
                        startActivity(intent)

                        Toast.makeText(this@LoginActivity, "로그인에 성공하였습니다.", Toast.LENGTH_SHORT).show() // 알림 발생

                        true
                    } else {
                        Toast.makeText(this@LoginActivity, "로그인에 실패하였습니다.", Toast.LENGTH_SHORT).show()

                        false
                    }
                }
                override fun onFailure(call: retrofit2.Call<LoginBackendResponse>, t: Throwable) { // 실패한 경우
                    false.also { state = it }
                }
            })
        }
    }
}

다음 LogoutActivity

class LogoutActivity : AppCompatActivity() { // 로그아웃 페이지
    // TODO(problem): Logout two click why? No Screen / dialog

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_logout)

        val btnLogout = findViewById<Button>(R.id.logout_btnLogout) as Button
        btnLogout.setOnClickListener{
            var dialog = AlertDialog.Builder(this)
            dialog.setTitle("로그아웃을 하시겠습니까?")
            dialog.setMessage("서비스를 이용해주셔서 감사합니다.")
            // dialog.setIcon 추후에 아이콘 삽입

            fun toast() {
                Toast.makeText(this, "로그아웃 되었습니다.", Toast.LENGTH_SHORT).show()
                MyApplication.prefs.edit.remove("email") // 여기서 Shared Preference 를 remove 한다!
                MyApplication.prefs.edit.remove("password")
                MyApplication.prefs.edit.commit() // SP 삭제되는 것을 확인
                val intent = Intent(this, HomeActivity::class.java)
                intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP
                startActivity(intent)
            }
            var dialogLister = DialogInterface.OnClickListener { p0, p1 ->
                when (p1) {
                    DialogInterface.BUTTON_POSITIVE -> toast()
                }
            }
            dialog.setPositiveButton("YES", dialogLister)
            dialog.setNegativeButton("NO", null)
            dialog.show()
        }
    }
}

다음 Api (Interceptor, OkHttpClient, Retrofit)

interface Api {
    // 내용 정리
    // Retrofit, 송신 시 우리는 값을 디코딩한 상태로 보였지만, OkHttpClient 에서는 인코딩 된 상태로 확인되었고,
    // 그게 백엔드로 전송이되면 당연히 오류가 발생했고, 인코딩 문제를 해결하려고 하였음
    // 최종 정리
    // postman 으로도 원하는 값을 넣어 보내도 오류가 발생하는 문제 확인완료.
    // OkHttpClient 는 @FormUrlEncoded 로 인코딩이 된 걸로 확인(확실하지 않음) 그래서 백엔드에서 인코딩 된 걸 디코딩하여
    // 받아야 되는 걸로 확인 - help 조이스
//    @FormUrlEncoded
//    @POST("/api/auth/signup/")
//    fun createUser(
//        @Field("email") email:String,
//        @Field("password") password1:String
//    ): Call<SignUpResponse>

    @Headers("content-type: application/json")
    @POST("/api/auth/signup/")
    fun createUser(
        @Body jsonParams:SignUpResponse,
    ): Call<SignUpResponse>

    @Headers("content-type: application/json")
    @POST("/api/auth/signin")
    fun userLogin(
        @Body jsonParams: LoginResponse,
    ): Call<LoginBackendResponse>

    companion object {
        private const val BASE_URL = "http://0.0.0.0"

        fun create(): Api {
            val httpLoggingInterceptor = HttpLoggingInterceptor()
            httpLoggingInterceptor.level = HttpLoggingInterceptor.Level.BODY

            val headerInterceptor = Interceptor {
                val request = it.request()
                    .newBuilder()
                    .addHeader("Content-Type", "application/json; charset=utf-8")
                    .build()
                return@Interceptor it.proceed(request)
            }

            val client = OkHttpClient.Builder()
                .addInterceptor(headerInterceptor)
                .addInterceptor(httpLoggingInterceptor)
                .addNetworkInterceptor(headerInterceptor)
                .build()

            return Retrofit.Builder()
                .baseUrl(BASE_URL)
                .client(client)
                .addConverterFactory(GsonConverterFactory.create())
                .build()
                .create(Api::class.java)
        }
    }
}

 

 

'Mobile > Kotlin' 카테고리의 다른 글

Kotlin @Body와 @Field 차이  (0) 2022.04.12

댓글

💲 추천 글