본문 바로가기
Android

Android [메모앱] Retrofit2 기본설정, 회원가입, 로그인

by leopard4 2023. 2. 9.

 

 

 

 

gradle

dependencies {

    implementation 'com.squareup.retrofit2:retrofit:2.9.0'
    implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
    implementation("com.squareup.okhttp3:logging-interceptor:4.9.0")


}

 

UserApi (인터페이스)

package com.leopard4.memoapp.api;

import com.leopard4.memoapp.model.User;
import com.leopard4.memoapp.model.UserRes;

import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.POST;

// 유저 관련 API 들을 모아놓은 인터페이스
public interface UserApi {

    // 회원가입 API 함수 작성
    @POST("/user/register") // API 명세서에 있는 경로를 작성한다.
    Call<UserRes> register(@Body User user); // Body 에 Json 으로 데이터를 보낸다. // Call<UserRes> 는 응답을 받을 클래스를 지정한다.

    // 로그인 API 함수 작성
    @POST("/user/login")// @의 의미는 레트로핏 라이브러리가 이 함수를 레트로핏 API 로 사용한다는 의미이다.
    Call<UserRes> login(@Body User user);

}

MemoApi (인터페이스)

package com.leopard4.memoapp.api;

import com.leopard4.memoapp.model.MemoList;

import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Header;
import retrofit2.http.Query;

public interface MemoApi {

    // 내 메모 가져오는 API
    @GET("/memos")
    Call<MemoList> getMemoList(@Header("Authorization") String token, @Query("offset") int offset, @Query("limit") int limit);
}

Config

package com.leopard4.memoapp.config;

public class Config {

    // 서버 주소
    public static final String DOMAIN = "개인적인 엔드포인트주소";
    public static final String DOMAIN_TEST = "http://localhost:5000";

    public static final String PREFERENCE_NAME = "memo_app";
    public static final String ACCESS_TOKEN = "access_token";

}

NetworkClient

package com.leopard4.memoapp.api;

import android.content.Context;

import com.leopard4.memoapp.config.Config;

import java.util.concurrent.TimeUnit;

import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

public class NetworkClient {

    public static Retrofit retrofit;

    public static Retrofit getRetrofitClient(Context context){
        if(retrofit == null){
            // 통신 로그 확인할때 필요한 코드
            HttpLoggingInterceptor loggingInterceptor =
                    new HttpLoggingInterceptor();
            loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY); // 실배포시는 NONE으로 설정 (해킹문제)

            // 네트워크 연결관련 코드
            OkHttpClient httpClient = new OkHttpClient.Builder()
                    .connectTimeout(1, TimeUnit.MINUTES)
                    .readTimeout(1, TimeUnit.MINUTES)
                    .writeTimeout(1, TimeUnit.MINUTES)
                    .addInterceptor(loggingInterceptor)
                    .build();
            // 네트워크로 데이터를 보내고 받는
            // 레트로핏 라이브러리 관련 코드
            retrofit = new Retrofit.Builder()
                    .baseUrl(Config.DOMAIN)
                    .client(httpClient)
                    .addConverterFactory(GsonConverterFactory.create())
                    .build();
        }
        return retrofit;
    }


}

user모델

package com.leopard4.memoapp.model;

import java.io.Serializable;

public class User implements Serializable {
    // 레트로핏 라이브러리를 통해서, Body 에 Json으로 데이터를 보낼 클래스
    // 1. Serializable 인터페이스를 구현한다.
    // 2. API 명세서를 보고, 멤버변수를 만든다.
    // 3. 멤버변수는 private 으로 선언한다.
    // 4. 멤버변수에 대한 Getter, Setter 를 만든다.
    private String email;
    private String password;
    private String nickname;

    public User() {
    }

    public User(String email, String password, String nickname) {
        this.email = email;
        this.password = password;
        this.nickname = nickname;
    }

    public User(String email, String password) {
        this.email = email;
        this.password = password;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getNickname() {
        return nickname;
    }

    public void setNickname(String nickname) {
        this.nickname = nickname;
    }
}

UserRes

package com.leopard4.memoapp.model;

import java.io.Serializable;

// 레트로핏 라이브러리를 통해서 응답받는 클래스
public class UserRes implements Serializable {
    private String access_token;

    public String getAccess_token() {
        return access_token;
    }

    public void setAccess_token(String access_token) {
        this.access_token = access_token;
    }
}

RegisterActivity

package com.leopard4.memoapp;

import androidx.appcompat.app.AppCompatActivity;

import android.app.ProgressDialog;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.util.Log;
import android.util.Patterns;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import com.leopard4.memoapp.api.NetworkClient;
import com.leopard4.memoapp.api.UserApi;
import com.leopard4.memoapp.config.Config;
import com.leopard4.memoapp.model.User;
import com.leopard4.memoapp.model.UserRes;

import java.util.regex.Pattern;

import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;

public class RegisterActivity extends AppCompatActivity {

    EditText editEmail;
    EditText editPassword;
    EditText editNickname;
    Button btnRegister;
    TextView txtLogin;

    // 네트워크를 통해서 로직처리를 할때 보여주는
    // 프로그레스 다이얼로그
    ProgressDialog dialog;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_register);

        editEmail = findViewById(R.id.editEmail);
        editPassword = findViewById(R.id.editPassword);
        editNickname = findViewById(R.id.editNickname);
        btnRegister = findViewById(R.id.btnLogin);
        txtLogin = findViewById(R.id.txtRegister);

        btnRegister.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // 이메일 가져와서 형식 체크
                String email = editEmail.getText().toString().trim();

                Pattern pattern = Patterns.EMAIL_ADDRESS;
                if(pattern.matcher(email).matches() == false){
                    Toast.makeText(RegisterActivity.this, "이메일 형식이 올바르지 않습니다.", Toast.LENGTH_SHORT).show();
                    return;
                }

                // 비밀번호 체크
                String password = editPassword.getText().toString().trim();
                // 우리 기획에는 비번길이가 4~12 만 허용
                if(password.length() < 4 || password.length() > 12){
                    Toast.makeText(RegisterActivity.this, "비번 길이를 확인하세요.", Toast.LENGTH_SHORT).show();
                    return;
                }

                // 닉네임 가져온다.
                String nickname = editNickname.getText().toString().trim();
                if(nickname.isEmpty()){
                    Toast.makeText(RegisterActivity.this, "닉네임은 필수입니다.", Toast.LENGTH_SHORT).show();
                    return;
                }

                // 회원가입 API 를 호출!

                // 1. 다이얼로그를 화면에 보여준다.
                showProgress("회원가입 중입니다...");

                // 2. 서버로 데이터를 보낸다.
                // 2-1. 레트로핏 변수 생성
                Retrofit retrofit =
                        NetworkClient.getRetrofitClient(RegisterActivity.this);
                // 2-2. api 패키지에 있는 인터페이스 생성
                UserApi api = retrofit.create(UserApi.class);

                // 2-3. 보낼 데이터 만들기 => 클래스의 객체 생성
                User user = new User(email, password, nickname);

                // 2-4. api 호출
                Call<UserRes> call = api.register(user);
                // 2.5. 서버로부터 받아온 응답을 처리하는 코드 작성
                call.enqueue(new Callback<UserRes>() {
                    @Override
                    public void onResponse(Call<UserRes> call, Response<UserRes> response) {
                        // 프로그레스 다이얼로그가 있으면, 나타나지 않게해준다.
                        dismissProgress();

                        // 서버에서 보낸 응답이 200 OK 일때 처리하는 코드
                        if(response.isSuccessful()){
                            Log.i("MEMO_APP", response.toString());

                            // 서버가 보낸 데이터를 받는 방법!!!!!!!
                            UserRes res = response.body();

                            Log.i("MEMO_APP", res.getAccess_token());

                            // 억세스토큰은, api 할때마다 헤더에서 사용하므로
                            // 회원가입이나 로그인이 끝나면, 파일로 꼭 저장해 놔야 한다.
                            SharedPreferences sp =
                                    getApplication().getSharedPreferences(Config.PREFERENCE_NAME, MODE_PRIVATE);
                            SharedPreferences.Editor editor = sp.edit();
                            editor.putString(Config.ACCESS_TOKEN, res.getAccess_token() );
                            editor.apply();

                            // 3. 데이터를 이상없이 처리하면,
                            //    메인액티비티를 화면에 나오게 한다.
                            Intent intent = new Intent(RegisterActivity.this, MainActivity.class);
                            startActivity(intent);
                            finish();

                        }else{
                            Log.i("MEMO_APP", response.toString());
                        }
                    }

                    @Override
                    public void onFailure(Call<UserRes> call, Throwable t) {
                        // 프로그레스 다이얼로그가 있으면, 나타나지 않게해준다.
                        dismissProgress();
                    }
                });



            }
        });

        txtLogin.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(RegisterActivity.this, LoginActivity.class);
                startActivity(intent);

                finish();
            }
        });
    }

    // 네트워크 로직 처리시에 화면에 보여주는 함수
    void showProgress(String message){
        dialog = new ProgressDialog(this);
        dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
        dialog.setMessage(message);
        dialog.show();
    }

    // 로직처리가 끝나면 화면에서 사라지는 함수
    void dismissProgress(){
        dialog.dismiss();
    }

}

loginActivity

package com.leopard4.memoapp;

import androidx.appcompat.app.AppCompatActivity;

import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.util.Patterns;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import com.leopard4.memoapp.api.NetworkClient;
import com.leopard4.memoapp.api.UserApi;
import com.leopard4.memoapp.config.Config;
import com.leopard4.memoapp.model.User;
import com.leopard4.memoapp.model.UserRes;

import java.util.regex.Pattern;

import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;

public class LoginActivity extends AppCompatActivity {

    EditText editEmail;
    EditText editPassword;
    Button btnLogin;
    TextView txtRegister;
    ProgressDialog dialog;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);

        editEmail = findViewById(R.id.editEmail);
        editPassword = findViewById(R.id.editPassword);
        btnLogin = findViewById(R.id.btnLogin);
        txtRegister = findViewById(R.id.txtRegister);

        btnLogin.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String email = editEmail.getText().toString().trim();
                Pattern pattern = Patterns.EMAIL_ADDRESS;

                if(pattern.matcher(email).matches() == false){
                    Toast.makeText(LoginActivity.this, "이메일 형식이 올바르지 않습니다.", Toast.LENGTH_SHORT).show();
                    return;
                }

                String password = editPassword.getText().toString().trim();
                // 우리 기획에는 비번길이가 4~12 만 허용
                if(password.length() < 4 || password.length() > 12){
                    Toast.makeText(LoginActivity.this, "비번 길이를 확인하세요.", Toast.LENGTH_SHORT).show();
                    return;
                }

                showProgress("로그인 중입니다.");

                Retrofit retrofit = NetworkClient.getRetrofitClient(LoginActivity.this);
                UserApi api = retrofit.create(UserApi.class);

                User user = new User(email, password);

                Call<UserRes> call = api.login(user);
                call.enqueue(new Callback<UserRes>() {
                    @Override
                    public void onResponse(Call<UserRes> call, Response<UserRes> response) {
                        dismissProgress();

                        if(response.isSuccessful() == false){


                        UserRes res = response.body();

                        SharedPreferences sp =
                                getApplication().getSharedPreferences(Config.PREFERENCE_NAME, MODE_PRIVATE);
                        SharedPreferences.Editor editor = sp.edit();
                        editor.putString(Config.ACCESS_TOKEN, res.getAccess_token() );
                        editor.apply();

                        Intent intent = new Intent(LoginActivity.this, MainActivity.class);
                        startActivity(intent);
                        finish();

                    } else if (response.code() == 400) {
                        Toast.makeText(LoginActivity.this, "회원가입이 되어있지 않거나 비번이 틀렸습니다.", Toast.LENGTH_SHORT).show();
                        return;
                    } else {
                        Toast.makeText(LoginActivity.this, "정상적으로 처리되지 않았습니다.", Toast.LENGTH_SHORT).show();
                    }

                }

                    @Override
                    public void onFailure(Call<UserRes> call, Throwable t) {
                        dismissProgress();
                        Toast.makeText(LoginActivity.this, "정상적으로 처리되지 않았습니다.", Toast.LENGTH_SHORT).show();

                    }
                });
            }






        });

        txtRegister.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent(LoginActivity.this, RegisterActivity.class);
                startActivity(intent);
                finish();
            }
        });
    }
    // 네트워크 로직 처리시에 화면에 보여주는 함수
    void showProgress(String message){
        dialog = new ProgressDialog(this);
        dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
        dialog.setMessage(message);
        dialog.show();
    }

    // 로직처리가 끝나면 화면에서 사라지는 함수
    void dismissProgress(){
        dialog.dismiss();
    }

}

memoList

package com.leopard4.memoapp.model;

import java.io.Serializable;
import java.util.List;

public class MemoList implements Serializable {

    private String result;
    private List<Memo> items;
    private int count;

    public String getResult() {
        return result;
    }

    public void setResult(String result) {
        this.result = result;
    }

    public List<Memo> getItems() {
        return items;
    }

    public void setItems(List<Memo> items) {
        this.items = items;
    }

    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
    }
}

memo

package com.leopard4.memoapp.model;

import java.io.Serializable;

public class Memo implements Serializable {

    private int id;
    private String title;
    private String datetime;
    private String content;
    private String createdAt;
    private String updatedAt;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getDatetime() {
        return datetime;
    }

    public void setDatetime(String datetime) {
        this.datetime = datetime;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public String getCreatedAt() {
        return createdAt;
    }

    public void setCreatedAt(String createdAt) {
        this.createdAt = createdAt;
    }

    public String getUpdatedAt() {
        return updatedAt;
    }

    public void setUpdatedAt(String updatedAt) {
        this.updatedAt = updatedAt;
    }
}

memoApi

package com.leopard4.memoapp.api;

import com.leopard4.memoapp.model.MemoList;

import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Header;
import retrofit2.http.Query;

public interface MemoApi {

    // 내 메모 가져오는 API
    @GET("/memos")
    Call<MemoList> getMemoList(@Header("Authorization") String token, @Query("offset") int offset, @Query("limit") int limit);
}