본문 바로가기

Android/Android Lecture

12. Retrofit 서비스와 로그인! 그리고 SharedPref!

안녕하세요~ 효그니에요 >< 너무 늦어버렸어요 ㅠㅠ 사실 회사일이랑 대회가 너무 많아서 블로그 쓰는데에 나태해져 버렸네요 ㅠㅠ...

죄송합니다!

일단 오늘 배워볼것은 Retrofit 서비스인데요.

보통 안드개발 할 때에는 서버연동을 자주하죠! 

기존 네트워크 기능을 위해 HttpUrlConnect등이 쓰여왔어요 그 후 OkHttp나 Volley..등이 쓰여져 왔는데.

그중에서 저같은 경우는 Retrofit을 자주 쓰는것 같아요!

우선 Retrofit Service를 사용하기 위해서는 

    implementation'com.squareup.retrofit2:converter-gson:2.3.0'
    implementation 'com.squareup.okhttp3:logging-interceptor:3.9.1'
    implementation 'com.squareup.retrofit2:retrofit:2.3.0'

이렇게 implementation을 해줘야해요.

그 후  Client

라는 파일을 만들어 줄게요! 

package template.android.hyogeuns.retrofit_template_for_login


import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import java.util.concurrent.TimeUnit

object Client {
    var retrofitService: API

    init {
        val interceptor = HttpLoggingInterceptor()
        interceptor.level = HttpLoggingInterceptor.Level.BODY
        val logger = OkHttpClient.Builder().addInterceptor(interceptor).readTimeout(20, TimeUnit.SECONDS).writeTimeout(20, TimeUnit.SECONDS).build()

        val retrofit = Retrofit.Builder()
                .baseUrl("https://당신의_서버주소")
                .addConverterFactory(GsonConverterFactory.create())
                .client(logger)
                .build()

        retrofitService = retrofit.create(API::class.java)


    }
}

자 소스 리뷰를 해볼까요? 

Retrofit.Builder() 부분은 Retrofit 생성 후 사용할 interface를 통해 서비스를 만드는 부분이에요. (Response를 String 형태로 받고 싶다면 ScalarsConverterFactory 사용)

아하! 근데 에러가 나네요? API? 이게 뭐지 API가 뭘까요?

바로 서버를 보면 

https://효근효근서버.com/로그인

불러오는 중입니다...

이 있다고 하면 /로그인 을 입력해주고 post통신인지 get 통신인지 입력해주고 파라미터값이 무엇인지 입력해주는 파일이에요! 

package template.android.hyogeuns.retrofit_template_for_login


import retrofit2.Call
import retrofit2.http.*


interface API {

    @POST("/user/signin")  //로그인
    @FormUrlEncoded
    fun logIn(@Field("id") id : String, @Field("pw") pw : String) :  Call<Void>

    @POST("/user/signup") //회원가임
    @FormUrlEncoded
    fun logUp(@Field("name") name : String, @Field("id") id : String, @Field("pw") pw : String) :  Call<Void>
}

제 서버 같은경우는 이렇네요!

그 후 이제 제대로 서버랑 통신하는 페이지를 만들어야겠죠?

회원가입과 로그인 페이지를 만들어 줄게요! 

 

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:card_view="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:background="#757575"
    android:orientation="vertical"
    tools:ignore="ButtonStyle,HardcodedText, ContentDescription, SmallSp">

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="match_parent">

        <android.support.v7.widget.CardView
            android:id="@+id/cardview"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_marginBottom="80dp"
            android:layout_marginEnd="30dp"
            android:layout_marginStart="30dp"
            android:layout_marginTop="80dp"
            card_view:cardBackgroundColor="#ffffff"
            card_view:cardCornerRadius="13.3dp">

            <RelativeLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent">

                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_alignParentBottom="true"
                    android:gravity="center"
                    android:orientation="vertical"
                    android:padding="25dp">


                    <android.support.design.widget.TextInputLayout
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:layout_marginTop="40dp">

                        <EditText
                            android:id="@+id/id_tv"
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:hint="ID" />
                    </android.support.design.widget.TextInputLayout>


                    <android.support.design.widget.TextInputLayout
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:layout_marginTop="30dp">
                        <EditText
                            android:id="@+id/pw_tv"
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:hint="PW" />
                    </android.support.design.widget.TextInputLayout>

                    <Button
                        android:id="@+id/login_btn"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:layout_margin="2dp"
                        android:elevation="2dp"
                        android:text="LOG IN"
                        android:textColor="@android:color/white" />

                    <TextView
                        android:id="@+id/signup_go"
                        android:layout_width="wrap_content"
                        android:layout_height="match_parent"
                        android:gravity="top"
                        android:padding="10dp"
                        android:text="회원이 아니신가요?" />

                </LinearLayout>
            </RelativeLayout>
        </android.support.v7.widget.CardView>

    </LinearLayout>



</RelativeLayout>

activity_login.xml 파일이에요! CardView와 TextInputLayout을 사용하였습니다~!

이런 느낌이네요

package template.android.hyogeuns.retrofit_template_for_login


import android.Manifest
import android.content.Intent
import android.widget.Toast
import kotlinx.android.synthetic.main.activity_login.*
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response

class LoginActivity : BaseActivity() {

    override var viewId: Int = R.layout.activity_login
    val PREFERENCE = "template.android.hyogeuns"

    override fun onCreate() {

        SharedPref.openSharedPrep(this)
        login_btn.setOnClickListener {
            Client.retrofitService.logIn(id_tv.text.toString(), pw_tv.text.toString()).enqueue(object : Callback<Void> {
                override fun onResponse(call: Call<Void>?, response: Response<Void>?) {
                    when (response!!.code()) {
                        200 -> {
                            val pref = getSharedPreferences(PREFERENCE, MODE_PRIVATE)
                            val editor = pref.edit()
                            editor.putString("username", id_tv.text.toString())
                            editor.commit()
                            finish()
                            startActivity(Intent(this@LoginActivity, MainActivity::class.java))
                        }
                        405 -> Toast.makeText(this@LoginActivity, "로그인 실패 : 아이디나 비번이 올바르지 않습니다", Toast.LENGTH_LONG).show()
                        500 -> Toast.makeText(this@LoginActivity, "로그인 실패 : 서버 오류", Toast.LENGTH_LONG).show()
                    }
                }

                override fun onFailure(call: Call<Void>?, t: Throwable?) {

                }


            })
        }

        signup_go.setOnClickListener { startActivity(Intent(this@LoginActivity, SignUpActivity::class.java)) }
    }
}

자 이 부분 같은경우는 LoginActivity에요!

일단 Client.RetrofitSevice.LogIn안에 activity_login 내의 id_tv와 pw_tv를 불러와요!  그리고 .text를 통해 텍스트를 추출하고 서버로 보내줍니다!

그러면 onResponse함수에서 서버 리스폰스코드를 받아와서 when구문으로 200일때는 로그인 성공 405일때는 로그인실패 500일때는 서버 오류를 출력하게 합니다! 

그리고 SharePreference 공유 저장소인데요!

이게 뭘까요? 

앱에서 데이터 저장은 sqlite(DataBase)를 이용하는 방법과 쉐어드 프리퍼런스(Shared Preference)를 이용하는 방법이 있어요!

쉐어드 프레퍼런스는 sqlite(DataBase)를 사용하지 않아도 데이터 저장이 가능하다는점과!. 또 sqlite의 사용보다 좀 더 쉽게 사용할 수있다는 장점이 있구요!

저는 이 SharedPrefrence를 사용하여서 간단하게 로그인을 하면 id를 화면에 SharedPref를 통하여 출력하게 할거에요!

우선 

SharedPref라는 오브젝트를 만들어줄게요!

package template.android.hyogeuns.retrofit_template_for_login


import android.content.Context
import android.content.SharedPreferences
import android.util.Log

object SharedPref {
    val LOGIN_SESSION = "login.session"

    private var sharedPref: SharedPreferences? = null

    fun openSharedPrep(context: Context) {
        this.sharedPref = context.getSharedPreferences(LOGIN_SESSION, Context.MODE_PRIVATE)
    }
    fun writeLoginSession(data: String) {
        if(this.sharedPref == null) {
            Log.e("DSMAD", "Plz start openSahredPrep() !")
        } else {
            sharedPref?.edit()?.putString("session", data)?.apply()
        }
    }
    
    fun readLoginSession() : String? {
        return if(this.sharedPref == null) {
            Log.e("DSMAD", "Plz start openSahredPrep() !")
            null
        } else sharedPref?.getString("session", null)
    }
}

 

간단하게 만들어 졌죠?    그 후

val PREFERENCE = "template.android.hyogeuns"


로 저장할 프리퍼런스를 지정해놔요.

그리고  

  val pref = getSharedPreferences(PREFERENCE, MODE_PRIVATE)
                                val editor = pref.edit()

를 통하여 프리퍼런스 에디터를 열어줍니다!

마지막으로 

 editor.putString("username", id_tv.text.toString())
                                editor.commit()

putString("키값", 저장할 값)으로 프리퍼런스에 저장하고 에디터에 커밋을 해줄게요!

id_tv는 아이디죠? 그럼 id가 쉐어드 프리퍼런스에 저장될거에요!

그럼 로그인 액티비티 생성이 끝났습니다~!! 는 

그럼 회원가입은 어떻게하지? 큰일났다....

그럼 SignUpActivity를 만들면 되겠다!

바로 SignUpActivity를 만들어 줄게요!

signup_go라는 id를 activity_login을 사용해서 SignUpActivity로 넘겨줘요!

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:card_view="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:background="#757575"
    android:orientation="vertical"
    tools:ignore="ButtonStyle,HardcodedText, ContentDescription, SmallSp">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <android.support.v7.widget.CardView
            android:id="@+id/cardview"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_marginEnd="30dp"
            android:layout_marginBottom="20dp"
            android:layout_marginStart="30dp"
            android:layout_marginTop="80dp"
            card_view:cardBackgroundColor="#ffffff"
            card_view:cardCornerRadius="13.3dp">

            <RelativeLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent">

                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:layout_alignParentBottom="true"
                    android:gravity="center"
                    android:orientation="vertical"
                    android:padding="25dp">


                    <android.support.design.widget.TextInputLayout
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:layout_marginTop="30dp">

                        <EditText
                            android:id="@+id/name_tv"
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:hint="Name" />
                    </android.support.design.widget.TextInputLayout>

                    <android.support.design.widget.TextInputLayout
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:layout_marginTop="15dp">

                        <EditText
                            android:id="@+id/id_tv"
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:hint="ID" />
                    </android.support.design.widget.TextInputLayout>


                    <android.support.design.widget.TextInputLayout
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:layout_marginBottom="45dp"
                        android:layout_marginTop="15dp">

                        <EditText
                            android:id="@+id/pw_tv"
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:hint="PW" />
                    </android.support.design.widget.TextInputLayout>

                    <Button
                        android:id="@+id/sign_btn"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:layout_margin="2dp"
                        android:elevation="2dp"
                        android:text="SIGN UP"
                        android:textColor="@android:color/white" />

                </LinearLayout>
            </RelativeLayout>
        </android.support.v7.widget.CardView>

    </LinearLayout>


</RelativeLayout>

회원가입의 xml activity_signup이에요!

그 후

package template.android.hyogeuns.retrofit_template_for_login


import android.widget.Button
import android.widget.Toast
import kotlinx.android.synthetic.main.activity_signup.*
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response

class SignUpActivity : BaseActivity() {

    override var viewId: Int = R.layout.activity_signup
    override fun onCreate() {


        findViewById<Button>(R.id.sign_btn).setOnClickListener {
            Client.retrofitService.logUp(name_tv.text.toString(), id_tv.text.toString(), pw_tv.text.toString()).enqueue(object : Callback<Void> {
                override fun onResponse(call: Call<Void>?, response: Response<Void>?) {
                    when (response!!.code()) {
                        200 -> {
                            Toast.makeText(this@SignUpActivity, "회원가입 성공", Toast.LENGTH_LONG).show()
                            finish ()
                        }
                        405 -> Toast.makeText(this@SignUpActivity, "회원가입 실패 : 아이디나 비번이 올바르지 않습니다", Toast.LENGTH_LONG).show()
                        500 -> Toast.makeText(this@SignUpActivity, "회원가입 실패 : 서버 오류", Toast.LENGTH_LONG).show()
                    }
                }

                override fun onFailure(call: Call<Void>?, t: Throwable?) {

                }


            })
        }
    }

}

SignUpActivity를 만들어주도록 할게요!

LoginActivity와 마찬가지로 name_tv, id_tv, pw_tv를 서버에 보내줄게요!

그 후 서버에서 리스폰스코드를 받아 200일때 성공, 405일때 실패, 500일때 서버 에러로 올려둘게요!

그렇게되면 회원가입도 간단하게 완성이 됩니다! 

이제 MainActivity는 텅 비여있으니 MainActivity작성도 해볼까요? 

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/hello"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="로그인 성공"
        android:textSize="30dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintHorizontal_bias="0.498"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.324" />

</android.support.constraint.ConstraintLayout>

간단하게 텍스트뷰 하나만 띄워줄게요! 

그 후

package template.android.hyogeuns.retrofit_template_for_login

import android.content.SharedPreferences
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import kotlinx.android.synthetic.main.activity_login.*
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {

    val PREFERENCE = "template.android.hyogeuns"

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        var pref = getSharedPreferences(PREFERENCE, MODE_PRIVATE)
        var users = pref.getString("username", "")
        hello.text = "환영합니다" + users + "님"
    }
}

MainActivity에 pref를 열어줄게요!

pref.getString을 users라는 변수에 넣어줘요! username이 아까 제가 지정한 키값이였으니, 그대로 불러오도록 할게요!

그 후 TextView의 Id인 hello에 그 변수를 넣어줄게요 그럼! 이제 진짜로 전부 소스가 짜졌네요 이제 실행을 해볼까요?

 

{저같은 경우는 개인적으로 테스트서버가 있어서 node.js를 사용해 서버를 올렸습니다!}

 

실행이 잘되는 화면

음~ 잘뜨네요! 

우선 회원이 아니니 회원가입을 해볼까요? 

블로그 홍보를 하는 주인장

이렇게 해볼게요!

정말 잘 되네요! 

그렇게 되면 

로그켓에선 200 OK라고 뜨네요! 참고로 제 서버주소가 나와있어서 저부분만 가렸습니다 ㅎ,ㅎ

그럼 이제 로그인을 해볼까요?

음음~  king 을 하나 붙여볼게요~ 되겠죠? 저는 왕 할레요~ 

앗...아앗.... 전 왕이 아닌걸로~ ㅎㅎ

로그켓에선 405 에러가 뜨네요~! 

그럼 다시 제대로! 

LOGIN!

정상적으로 되네요!

로그켓에서도

문제 없이 실행이 잘 되네용~ 

이상 오늘의 블로그 여기까지였습니다~!