Retrofit은 앱 개발 시 서버통신에 사용되는 HTTP API를 자바, 코틀린의 인터페이스 형태로 변환해 안드로이드 개발 시 API를 쉽게 호출할 수 있도록 지원하는 라이브러리입니다.
HTTP API가 존재한다고 가정했을 때 Retrofit 라이브러리를 사용하면 HTTP API를 아래 코드와 같은 인터페이스 형태로 변환할 수 있음.
https://~~/user/{user}/repos 얘를
interface GitHubService {
@GET("users/{user}/repos")
fun getRepoList(@Path("user") user: String): Call<List<Repo>>
}
이렇게!
🐶
Retrofit을 이용해 서버를 연동하려면 실제 서버를 연동을 실행하는 Call 객체가 필요하다. 하지만 이 Call 객체는 개발자가 직접 만드는 것이 아니고 개발자는 인터페이스를 통해 설계도만 만들고 Retrofit이 자동으로 만들어 준다.
인터페이스를 이용하여 Service 객체를 획득한 후 네트워킹이 필요할 때 Call 객체를 획득하여 이용하면 되는 구조이다.
대부분의 안드로이드 개발자가 사용하는 Retrofit이 생기기 이전에도 사용하던 라이브러리들이 있었습니다.
바로 HttpClient, HttpUrlConnection, Volley, OKhttp 라이브러리인데요. 현재는 모두 Deprecated 되어 대부분 Retrofit을 사용한다.
(gradle)
dependencies {
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'com.squareup.okhttp3:okhttp:4.8.0'
implementation 'com.squareup.okhttp3:logging-interceptor:4.8.0'
}
🐶
(manifest)
// 권한 설정
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
// 안드로이드 보안 설정
android:usesCleartextTraffic="true"
android:hardwareAccelerated="true"
🐶 우선 api가 명세되어 있어야 한다.
=> 이에 맞춰서 data class를 만든다.
🐵 (@SerializedName("status") val status: Int
=> 코드상 변수명과 api에 정의된 이름을 다르게 할 때 사용)
🐸 data class 내의 data class, model 도 가능하다
//백엔드로 전송할 데이터 클래스
data class UserModel(
var id : String ?= null,
var pw : String ?= null
)
//백엔드에서 받는 데이터 클래스
data class LoginBackendResponse(
val code : String,
//200: 성공, 300,400: 에러
val message : String,
val token : String
)
서버 주소로부터 데이터를 전달하고, 전달 받는다(GET, POST)
@Get("policy"): baseUrl 뒤에 /policy가 붙는다.
@Query("location"): 주소에 들어가는 파라미터인 location을 @Query로 지정한다.
@Header("Authorization"): 서버가 토큰 인증을 사용중이고 헤더에 토큰을 담아 넘겨줘야 하는 경우 @Header("Authorization")을 추가한다.
Call: 서버로부터 데이터를 PolicyResponse 타입으로 받아온다.
baseUrl은 변하지 않는 주소를 지정합니다! ex) http://1.5.7.9:8080
사실 열심히 구글링하다가
인터페이스 내에 companion object를 선언하는 코드와
여기에 하지 않고 다른 파일에 선언하는 코드 둘 다 많이 봤는데,
여기에 해야 액티비티에서 바로 .create()해서 사용할 수 있는 것 같다.
이 부분 밑에 🦁 사자 표시 해놓겠음
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.annotations.SerializedName
import okhttp3.Interceptor
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Call
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import retrofit2.http.Body
import retrofit2.http.Headers
import retrofit2.http.POST
import retrofit2.http.Query
interface Api {
//@Headers("app/json")
@POST("/front")
fun userLogin(
@Body jsonParams : UserModel,
): Call<LoginBackendResponse>
companion object {
private const val BASE_URL = "https:백엔드 서버 uri/"
val gson : Gson = GsonBuilder().setLenient().create();
fun create() : Api{
return Retrofit.Builder()
.baseUrl(BASE_URL)
//.client(client)
.addConverterFactory(GsonConverterFactory.create(gson))
.build()
.create(Api::class.java)
}
}
}
🦁 이 부분이 위에 인터페이스에서 companion object 선언 안했을 때 이런 식으로 따로 파일을 만들어서 작동하게 하는 방법!!!!!🦁
=> 레트로핏 객체를 만들었기 때문에 RetrofitBuilder.api로 어디서든 사용할 수 있습니다.
=> 위에서 해줬으면 없어도 된다는 말임
🐶
RetrofitClient는 싱글톤으로 구현하기 위해 object 키워드를 사용한다.
(코틀린에서의 object는 클래스를 싱글톤으로 구현한다.)
싱글톤: 객체의 인스턴스를 1개만 생성하고 계속 재사용하는 패턴
서버 통신할 액티비티나 Fragment에서 retrofitbuilder(client)를 사용해 바로 사용할 수 있게 싱글톤 패턴으로 object 생성
import okhttp3.Interceptor
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
object RetrofitClient {
val BASE_URL = "https://여기에 백엔드 서버 uri"
fun getApiClient(): Retrofit {
return Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
}
}
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.widget.Toast
import com.example.generallogin.databinding.ActivityMainBinding
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
// ==로그인 버튼 클릭 시==
binding.btnLogin.setOnClickListener {
val id = binding.editTVId.text.toString().trim()//trim : 문자열 공백제거
val pw = binding.editTVPw.text.toString().trim()
saveData(id, pw)//db (shared preference)에 데이터 저장 (자동 로그인 용)
// == 백엔드 통신 부분 ==
val api = Api.create()//🦁🦁🦁🦁🦁
val data = UserModel(id, pw)
api.userLogin(data).enqueue(object : Callback<LoginBackendResponse> {
override fun onResponse(
call: Call<LoginBackendResponse>,
response: Response<LoginBackendResponse>
) {
Log.d("로그인 통신 성공",response.toString())
Log.d("로그인 통신 성공", response.body().toString())
when (response.code()) {
200 -> {
saveData(id, pw)
}
405 -> Toast.makeText(this@MainActivity, "로그인 실패 : 아이디나 비번이 올바르지 않습니다", Toast.LENGTH_LONG).show()
500 -> Toast.makeText(this@MainActivity, "로그인 실패 : 서버 오류", Toast.LENGTH_LONG).show()
}
}
override fun onFailure(call: Call<LoginBackendResponse>, t: Throwable) {
// 실패
Log.d("로그인 통신 실패",t.message.toString())
Log.d("로그인 통신 실패","fail")
}
})
}
}
}
val api = Api.create()//🦁🦁🦁🦁🦁
위에서 말한 부분
fun saveData( id : String, pw : String){
val prefID = getSharedPreferences("userID", MODE_PRIVATE)
val prefPW = getSharedPreferences("userPW", MODE_PRIVATE)
val editID = prefID.edit()
val editPW = prefPW.edit()
editID.putString("id", id)
editPW.putString("pw", pw)
editID.apply()//save
editPW.apply()//save
Log.d("로그인 데이터", "saved")
}
Shared Preference에 아이디와 비밀번호를 저장하는 방법이다.
저장이 잘 되었나? 확인하고 싶으면
안드로이드 스튜디오에 Device File Explorer 에서
data/data/[package 이름]/shared_prefs/
경로로 들어가면 보인다.
요런식으로 잘 저장되었다.
🐶 reference
https://yang-droid.tistory.com/21
https://stickode.tistory.com/43
https://velog.io/@songa29/Kotlin-Android-retrofit2-GET-서버-연동
https://week-year.tistory.com/146