안드로이드에서 API 서버와 통신하기 위한 방법으로 HttpUrlConnection, Volley, OkHttp, Retrofit2 등이 존재합니다. 그중에서 Retrofit2는 구현하기 쉽고 성능과 가독성이 좋다는 장점을 가지고 있습니다.
implementation 'com.squareup.retrofit2:retrofit:버전정보'
implementation 'com.squareup.retrofit2:converter-gson:버전정보'
build.gradle 파일에 retrofit2 라이브러리를 추가해줍니다.
data class ResponseCertiTab(
val `data`: List<Data>,
val message: String,
val status: Int,
val success: Boolean
)
{
data class Data(
val id: Int,
val image: String,
val user_id: Int,
val user_img: String,
val user_name: String
)
}
JSON 데이터를 받아올 ResponseCertiTab라는 Data Class를 생성해줍니다. 여기서는 GET /certi/:userName?date로 api를 가져오며 Request Query는 date, Response의 Data는 list 형식입니다.
interface CertifRetrofitService {
@GET("/certi/{userName}")
fun requestList(
@Path("userName") userName: String,
@Query("date") date: String
) : Call<ResponseCertiTab>
}
API 인터페이스를 생성 후 어노테이션을 이용하여 HTTP Method인 POST, GET, PUT, DELETE를 설정해줍니다.
object GroupRetrofitServiceImpl {
private const val BASE_URL = "http://15.164.186.213:3000/"
private val retrofit: Retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
//인증탭 - 메인
val service_ct_tab : CertifRetrofitService = retrofit.create(CertifRetrofitService::class.java)
}
코틀린의 object 키워드를 이용하여 싱글톤으로 생성해줍니다. baseUrl에는 연동할 서버 url을 넣어주며, addConverterFactory에는 GsonConverter를 추가하여 JSON 형식을 Data Class 형식으로 자동변환해줍니다.
class CertifTabFragment : Fragment() {
@RequiresApi(Build.VERSION_CODES.O)
@SuppressLint("ResourceType", "SetTextI18n")
var data: ResponseCertiTab? = null
var certiList: List<ResponseCertiTab.Data>? = null
private lateinit var recyclerView: RecyclerView
private lateinit var viewAdapter: RecyclerView.Adapter<*>
private lateinit var viewManager: RecyclerView.LayoutManager
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
val view = inflater.inflate(R.layout.fragment_certif_tab, container, false)
load(serverDate)
return view
}
ResponseCertiTab의 타입인 data와 certiList 변수를 선언해줍니다.
private fun load(serverDate: String){
//Callback 등록하여 통신 요청
val call: Call<ResponseCertiTab> =
GroupRetrofitServiceImpl.service_ct_tab.requestList(
userName = "김지현",
date = serverDate //처음 date에 오늘 날짜
)
Log.d("changeServerDate", serverDate)
call.enqueue(object : Callback<ResponseCertiTab> {
override fun onFailure(call: Call<ResponseCertiTab>, t: Throwable) {
// 통신 실패 로직
}
@SuppressLint("SetTextI18n")
override fun onResponse(
call: Call<ResponseCertiTab>,
response: Response<ResponseCertiTab>
) {
response.takeIf { it.isSuccessful }
?.body()
?.let { it ->
// do something
data = response.body()
Log.d("CertifTabFragment", data.toString())
//인증한 adapter에 Member 데이터 넣기
setCertifAdapter(it.data)
} ?: showError(response.errorBody())
}
})
}
private fun showError(error: ResponseBody?) {
val e = error ?: return
val ob = JSONObject(e.string())
Toast.makeText(context, ob.getString("message"), Toast.LENGTH_SHORT).show()
}
private fun setCertifAdapter(certiList: List<ResponseCertiTab.Data>) {
val mAdapter = CertifDateAdapter(certiList, context)
certifRecycler.adapter = mAdapter
mAdapter.notifyDataSetChanged()
certifRecycler.setHasFixedSize(true)
}
loadData 메소드를 만들어 서버와 통신을 요청하며, 카드뷰가 들어 있는 CertifDateAdapter에 서버와 통신하여 얻은 data를 adapter에 넣어줍니다.
class CertifDateAdapter(
private val certifList: List<ResponseCertiTab.Data>?,
val context: Context?
) :
RecyclerView.Adapter<CertifDateAdapter.ViewHolder>() {
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { //
var personName: TextView = itemView.findViewById(R.id.personName)
var certifImg: ImageView = itemView.findViewById(R.id.certifImg)
var userImg: ImageView = itemView.findViewById(R.id.my_iv_profile)
}
// 아이템 하나가 들어갈 뷰를 만들고 뷰 홀더에 넣어줌
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): CertifDateAdapter.ViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.item_certif_tab, parent, false)
return ViewHolder(view)
}
//뷰를 그리는 부분
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val item: ResponseCertiTab.Data = certifList!!.get(position)
val imgUrl: String = certifList[position].image
val userImgUrl: String = certifList[position].user_img
if (imgUrl.length > 0) {
Glide.with(holder.certifImg.context)
.load(imgUrl)
.error(android.R.drawable.stat_notify_error)
.into(holder.certifImg)
} else {
Glide.with(holder.certifImg.context)
.load(R.drawable.certif_un)
.error(android.R.drawable.stat_notify_error)
.into(holder.certifImg)
}
holder.personName.setText(certifList?.get(position)?.user_name)
if (userImgUrl.length > 0) {
Glide.with(holder.userImg.context)
.load(userImgUrl)
.error(android.R.drawable.stat_notify_error)
.into(holder.userImg)
} else {
Glide.with(holder.userImg.context)
.load(R.drawable.gr_img_profile_basic)
.error(android.R.drawable.stat_notify_error)
.into(holder.userImg)
}
}
//리스트의 전체 개수
override fun getItemCount(): Int {
var size: Int = 0
if (certifList != null) {
size = certifList.size
}
return size
}
}
카드뷰와 연결된 Adapter의 뷰를 그려주는 메소드인 onBindViewHolder 안에 서버에서 받아온 데이터와 연결해줍니다. img의 경우에는 url을 String으로 받아와 Glide 라이브러리를 사용하여 연결해줍니다.
(cf Glide 라이브러리 추가는 build.gradle 파일에서 해주셔야 합니다.)