Android

[Android-java] Retrofit2 적용하기 / 로그인 구현

기시미 2021. 3. 12. 15:42

개발툴 : Android Studio (안드로이드 개발툴) / Postman (서버 데이터를 보기 위한 툴)

 

Volley 라이브러리를 사용하다가 Retrofit2로 변경하였습니다.

 

AndroidManifest.xml

<uses-permission android:name="android.permission.INTERNET" />

Build.gradle(:app)

dependensies {
    //로그를 남기기 위한 라이브러리
    implementation 'com.squareup.okhttp3:logging-interceptor:3.11.0'
    //retrofit 통신을 위한 라이브러리
    implementation 'com.squareup.retrofit2:converter-gson:2.6.2'
    implementation 'com.squareup.retrofit2:retrofit:2.6.2'
    implementation 'com.google.code.gson:gson:2.8.5'
}

'com.squareup.okhttp3:logging-interceptor:3.11.0' 을 사용하면 아래와 같이 통신 로그를 깔끔하게 보실 수 있습니다.

다음은 사용할 retrofit을 구성하기 위한 Activity입니다.

RetrofitClient.java

public class RetrofitClient {
    
    private static RetrofitClient instance = null;
    private static initMyApi initMyApi;
    //사용하고 있는 서버 BASE 주소
    private static String baseUrl = "http://211.229.250.40/";



    private RetrofitClient() {
    	//로그를 보기 위한 Interceptor
        HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
        interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
        OkHttpClient client = new OkHttpClient.Builder()
                .addInterceptor(interceptor)
                .build();

        //retrofit 설정
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(baseUrl)
                .addConverterFactory(GsonConverterFactory.create())
                .client(client) //로그 기능 추가
                .build();

        initMyApi = retrofit.create(initMyApi.class);
    }

    public static RetrofitClient getInstance() {
        if (instance == null) {
            instance = new RetrofitClient();
        }
        return instance;
    }

    public static initMyApi getRetrofitInterface() {
        return initMyApi;
    }
}

다음은 서버에 보내거나 받을 때 Json 형식으로 보내거나 받아야하기 때문에 데이터를 정렬해줄 DTO 클래스를 만들어 줍니다.

※ DTO란 ? Data Transfer Object의 줄임말로 데이터를 오브젝트로 변환하는 객체입니다.

@SerializedName은 서버에서 정의한 변수명과 Android에서 정의할 변수를 연결해주는 역할을 합니다.

getter/setter 함수 또한 만들어줍니다.

LoginRequest.java 

서버에 보낼 데이터이며 입력한 id 와 password를 서버에 보내줍니다.

public class LoginRequest {

    @SerializedName("input_id")
    public String inputId;

    @SerializedName("input_pw")
    public String inputPw;

    public String getInputId() {
        return inputId;
    }

    public String getInputPw() {
        return inputPw;
    }

    public void setInputId(String inputId) {
        this.inputId = inputId;
    }

    public void setInputPw(String inputPw) {
        this.inputPw = inputPw;
    }

    public LoginRequest(String inputId, String inputPw) {
        this.inputId = inputId;
        this.inputPw = inputPw;
    }
}

 

Json 파일 (postman)

LoginResponse.java

서버로부터 받을 데이터들을 정의해줍니다.

public class LoginResponse {

    @SerializedName("result")
    public String resultCode;

    @SerializedName("access_token")
    public String token;

    @SerializedName("staff")
    public String staff;

    public String getResultCode() {
        return resultCode;
    }

    public void setResultCode(String resultCode) {
        this.resultCode = resultCode;
    }

    public String getToken() {
        return token;
    }

    public void setToken(String token) {
        this.token = token;
    }

    public String getStaff() {
        return staff;
    }

    public void setStaff(String staff) {
        this.staff = staff;
    }
}

Json 파일 (postman)

 

initMyApi.java

통신을 정의해주는 interface를 만들어 통신을 위한 함수를 만들어줍니다.

getLoginResponse 함수로 LoginRequest.java에 정의해준 데이터들을 서버 Body에 보낸 후 LoginResponse로 데이터를 받겠다는 의미를 가집니다. 

public interface initMyApi {
    //@통신 방식("통신 API명")
    @POST("/api_init_session")
    Call<LoginResponse> getLoginResponse(@Body LoginRequest loginRequest);
}

LoginActivity.java

public class LoginActivity extends AppCompatActivity {
	 private RetrofitClient retrofitClient;
     private initMyApi initMyApi;
     
     @Override
     protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);
        
        //뒤로 가기 버튼 2번 클릭시 종료
        backPressCloseHandler = new BackPressCloseHandler(this);
        
        idText= findViewById(R.id.insert_id);
        passwordText = findViewById(R.id.insert_password);
        btn_login = findViewById(R.id.btn_login);
        checkBox = findViewById(R.id.autoLogin);
        
        //자동 로그인을 선택한 유저
        if (!getPreferenceString(autoLoginId).equals("") && !getPreferenceString(autoLoginPw).equals("")) {
            checkBox.setChecked(true);
            checkAutoLogin(getPreferenceString(autoLoginId));
        }
        
        btn_login.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View view) {

                String id = idText.getText().toString();
                String pw = passwordText.getText().toString();
                hideKeyboard();
               
               //로그인 정보 미입력 시
                if (id.trim().length() == 0 || pw.trim().length() == 0 || id == null || pw == null) {

                    AlertDialog.Builder builder = new AlertDialog.Builder(LoginActivity.this);
                    builder.setTitle("알림")
                            .setMessage("로그인 정보를 입력바랍니다.")
                            .setPositiveButton("확인", null)
                            .create()
                            .show();
                    AlertDialog alertDialog = builder.create();
                    alertDialog.show();

                } else {
                    //로그인 통신
                    LoginResponse();
                }
            }
        });
    }
    
    public void LoginResponse() {
        String userID = idText.getText().toString().trim();
        String userPassword = passwordText.getText().toString().trim();
        
        //loginRequest에 사용자가 입력한 id와 pw를 저장
        LoginRequest loginRequest = new LoginRequest(userID, userPassword);
        
        //retrofit 생성
        retrofitClient = RetrofitClient.getInstance();
        initMyApi = RetrofitClient.getRetrofitInterface();
        
        //loginRequest에 저장된 데이터와 함께 init에서 정의한 getLoginResponse 함수를 실행한 후 응답을 받음
        initMyApi.getLoginResponse(loginRequest).enqueue(new Callback<LoginResponse>() {
            @Override
            public void onResponse(Call<LoginResponse> call, Response<LoginResponse> response) {

                Log.d("retrofit", "Data fetch success");
                
                //통신 성공
                if (response.isSuccessful() && response.body() != null) {
                	
                    //response.body()를 result에 저장
                    LoginResponse result = response.body();
                    
                    //받은 코드 저장
                    String resultCode = result.getResultCode();
                    
                    //받은 토큰 저장
                    String token = result.getToken();

                    String success = "200"; //로그인 성공
                    String errorId = "300"; //아이디 일치x
                    String errorPw = "400"; //비밀번호 일치x


                    if (resultCode.equals(success)) {
                        String userID = idText.getText().toString();
                        String userPassword = passwordText.getText().toString();
                        
                        //다른 통신을 하기 위해 token 저장
                        setPreference(token,token);
                        
                        //자동 로그인 여부
                        if (checkBox.isChecked()) {
                            setPreference(autoLoginId, userID);
                            setPreference(autoLoginPw, userPassword);
                        } else {
                            setPreference(autoLoginId, "");
                            setPreference(autoLoginPw, "");
                        }

                        Toast.makeText(LoginActivity.this, userID + "님 환영합니다.", Toast.LENGTH_LONG).show();
                        Intent intent = new Intent(LoginActivity.this, MainActivity.class);
                        intent.putExtra("userId", userID);
                        startActivity(intent);
                        LoginActivity.this.finish();

                    } else if (resultCode.equals(errorId)) {

                        AlertDialog.Builder builder = new AlertDialog.Builder(LoginActivity.this);
                        builder.setTitle("알림")
                                .setMessage("아이디가 존재하지 않습니다.\n 고객센터에 문의바랍니다.")
                                .setPositiveButton("확인", null)
                                .create()
                                .show();
                        AlertDialog alertDialog = builder.create();
                        alertDialog.show();

                    } else if (resultCode.equals(errorPw)) {
                        AlertDialog.Builder builder = new AlertDialog.Builder(LoginActivity.this);
                        builder.setTitle("알림")
                                .setMessage("비밀번호가 일치하지 않습니다.\n 고객" +
                                        "센터에 문의바랍니다.")
                                .setPositiveButton("확인", null)
                                .create()
                                .show();
                    } else {

                        AlertDialog.Builder builder = new AlertDialog.Builder(LoginActivity.this);
                        builder.setTitle("알림")
                                .setMessage("예기치 못한 오류가 발생하였습니다.\n 고객센터에 문의바랍니다.")
                                .setPositiveButton("확인", null)
                                .create()
                                .show();

                    }
                }
            }
            
            //통신 실패
            @Override
            public void onFailure(Call<LoginResponse> call, Throwable t) {
            	AlertDialog.Builder builder = new AlertDialog.Builder(LoginActivity.this);
                        	builder.setTitle("알림")
                                	.setMessage("예기치 못한 오류가 발생하였습니다.\n 고객센터에 문의바랍니다.")
                                	.setPositiveButton("확인", null)
                                	.create()
                                	.show();
            }
        });
    }
    
    //데이터를 내부 저장소에 저장하기
    public void setPreference(String key, String value){
        SharedPreferences pref = getSharedPreferences(DATA_STORE, MODE_PRIVATE);
        SharedPreferences.Editor editor = pref.edit();
        editor.putString(key, value);
        editor.apply();
    }

	//내부 저장소에 저장된 데이터 가져오기
    public String getPreferenceString(String key) {
        SharedPreferences pref = getSharedPreferences(DATA_STORE, MODE_PRIVATE);
        return pref.getString(key, "");
    }


    //키보드 숨기기
    private void hideKeyboard()
    {
        InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
        imm.hideSoftInputFromWindow(idText.getWindowToken(), 0);
        imm.hideSoftInputFromWindow(passwordText.getWindowToken(), 0);
    }

    //화면 터치 시 키보드 내려감
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        View focusView = getCurrentFocus();
        if (focusView != null) {
            Rect rect = new Rect();
            focusView.getGlobalVisibleRect(rect);
            int x = (int) ev.getX(), y = (int) ev.getY();
            if (!rect.contains(x, y)) {
                InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
                if (imm != null)
                    imm.hideSoftInputFromWindow(focusView.getWindowToken(), 0);
                focusView.clearFocus();
            }
        }
        return super.dispatchTouchEvent(ev);
    }

    //자동 로그인 유저
    public void checkAutoLogin(String id){

        //Toast.makeText(this, id + "님 환영합니다.", Toast.LENGTH_LONG).show();
        Intent intent = new Intent(this, MainActivity.class);
        startActivity(intent);
        finish();

    }
    
    //뒤로 가기 버튼 2번 클릭시 종료
    @Override public void onBackPressed() {
        //super.onBackPressed();
        backPressCloseHandler.onBackPressed();
    }
     
     

activity_login.xml

<?xml version="1.0" encoding="utf-8"?>

<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@android:color/white"
        android:focusableInTouchMode="true"
        android:focusable="true">

        <ImageView
            android:id="@+id/logo"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@drawable/ic_logo"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            android:layout_marginTop="150dp"/>

        <TextView
            android:id="@+id/tv_content"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/logo"
            android:layout_marginTop="20dp"
            android:layout_marginStart="50dp"
            android:text="#푸드는 사장님의 번창을 기원합니다"
            android:textSize="15dp"
            android:fontFamily="@font/gmarketsansmedium"
            android:includeFontPadding="false"
            android:textColor="@color/blue_500"/>

        <EditText
            android:id="@+id/insert_id"
            android:layout_width="0dp"
            android:layout_height="40dp"
            android:layout_marginStart="50dp"
            android:layout_marginEnd="50dp"
            android:textSize="15dp"
            android:textColor="@android:color/background_dark"
            android:fontFamily="@font/gmarketsanslight"
            android:includeFontPadding="false"
            android:hint="아이디"
            android:paddingLeft="10dp"
            android:textColorHint="@color/blue_500"
            android:background="@drawable/line_blue"
            android:gravity="center|start"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/tv_content"
            android:layout_marginTop="10dp"
            android:maxLines="1"
            android:imeOptions="actionDone"/>

        <EditText
            android:id="@+id/insert_password"
            android:layout_width="0dp"
            android:layout_height="40dp"
            android:layout_marginTop="10dp"
            android:layout_marginStart="50dp"
            android:layout_marginEnd="50dp"
            android:textSize="15dp"
            android:textColor="@android:color/background_dark"
            android:fontFamily="@font/gmarketsanslight"
            android:includeFontPadding="false"
            android:hint="비밀번호"
            android:textColorHint="@color/blue_500"
            android:inputType="textPassword"
            android:paddingLeft="10dp"
            android:background="@drawable/line_blue"
            android:gravity="center|start"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/insert_id"
            android:maxLines="1"
            android:imeOptions="actionDone"/>

        <CheckBox
            android:id="@+id/autoLogin"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="자동로그인"
            android:textSize="15dp"
            android:textColor="@color/blue_500"
            android:fontFamily="@font/gmarketsansmedium"
            android:includeFontPadding="false"
            android:buttonTint="@color/blue_500"
            app:layout_constraintTop_toBottomOf="@+id/insert_password"
            app:layout_constraintStart_toStartOf="parent"
            android:layout_marginLeft="45dp"/>

        <Button
            android:id="@+id/btn_login"
            android:layout_width="100dp"
            android:layout_height="40dp"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/autoLogin"
            app:layout_constraintEnd_toEndOf="parent"
            android:layout_marginTop="10dp"
            android:text="로그인"
            android:textSize="15dp"
            android:background="@drawable/round_blue"
            android:fontFamily="@font/gmarketsansmedium"
            android:includeFontPadding="false"/>

    </androidx.constraintlayout.widget.ConstraintLayout>

</androidx.appcompat.widget.LinearLayoutCompat>

결과 화면