개발툴 : 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;
}
}
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;
}
}
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>
결과 화면
'Android' 카테고리의 다른 글
[Android-java] ScrollView 사용법 및 에러 해결 (0) | 2021.08.26 |
---|---|
[Android-java] 서버 통신 시 SocketTimeoutException 에러 (2) | 2021.08.25 |
[Android-Java] Spinner를 이용해 드롭다운 리스트 구현하기 (0) | 2021.08.25 |
[Android-Java] ViewPager와 TabLayout을 이용해 Custom 탭 만들기 (0) | 2021.03.18 |
[Android-Java] TabLayout 배경색 다르게 설정하기 (1) | 2021.03.18 |