DTO
'Data Transfer Object' 형태의 모델(Model)/JSON 타입 변환에 사용
Interface
사용할 HTTP CRUD 동작(메서드)을 정의해놓은 인터페이스
CRUD(Create/Read/Update/Delete) -> HTTP Method(POST/GET/PUT/DELETE)
Retrofit.Builder 클래스
Interface를 사용할 인스턴스로 baseUrl/Converter 설정
Gradle 의존성 추가
implementation 'com.squareup.retrofit2:retrofit:(insert latest version)'
implementation 'com.squareup.retrofit2:converter-gson:(insert latest version)'
인터넷 권한 설정
Manifest 파일에 인터넷 권한 추가 - 네트워크 통신에 필요
<uses-permission android:name="android.permission.INTERNET"/>
Dto 모델 클래스 생성
data class Model(
val id: Long,
val title: String,
val image: String,
val content: String,
)
data class Dto(
val list: List<Model>
)
interface 정의
interface Service {
@GET("Url을 제외한 End Point")
fun getList(): Call<Dto>
}
Retrofit 인스턴스 생성
private fun getAPI() {
val retrofit = Retrofit.Builder()
.baseUrl(baseUrl 등록 '/' 까지 붙이기)
// JSON을 변환해줄 Gson 변환기
.addConverterFactory(GsonConverterFactory.create())
.build()
// Retrofit 인스턴스로 인터페이스 객체 구현
retrofit.create(Service::class.java).also {
it.getList()
// enqueue로 비동기 통신 실행, 통신 완료 후 이벤트 처리 위한 Callback 리스너 등록
.enqueue(object : Callback<Dto> {
override fun onResponse(call: Call<Dto>, response: Response<Dto>) {
if (response.isSuccessful.not()) {
Log.d("MainActivity", "response fail")
return
}
response.body()?.let { dto ->
// 통신 성공한 경우
}
}
override fun onFailure(call: Call<Dto>, t: Throwable) {
// 통신 실패에 대한 처리
// 인터넷 끊김, 예외 발생 등 시스템적인 이유의 실패
}
})
}
}
Response Code
클라이언트(모바일, 웹브라우저) -> 요청(Request) -> 서버 -> 응답(Response) -> 클라이언트
Response Class
Retrofit 사용하거나, 네트워크 작업시 나타나는 HTTP Status Code Error
100번대 응답 : 정보 제공(조건부 응답)
200번대 응답 : 성공(Success)
300번대 응답 : 리디렉션(Redirection)
300번대는 요청 처리 중 URL과 관련도니 문제 때문에 발생한 응답코드
예외적으로 304 코드의 경우 URL과는 관계 없는 응답코드
400번대 응답 : 클라이언트 에러(Client Error)
500번대 응답 : 서버 에러(Server Error)
완성 화면
Mock api 구성
HouseDto
data class HouseDto(
val code: Int,
val data: List<Data>
)
data class Data(
val total: Int,
val items: List<Items>
)
data class Items(
val id: Int,
val title: String,
val price: String,
val imgUrl: String
)
HouseService
interface HouseService {
@GET("v3/60ffdd33-f77c-4f60-b43c-7e8b6b9d0566")
fun getHouseList(): Call<HouseDto>
}
HouseAdapter
class HouseAdapter : ListAdapter<Items, HouseAdapter.ViewHolder>(diffUtil) {
inner class ViewHolder(var binding: ItemHouseBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(item: Items) = with(binding) {
titleTextView.text = item.title
priceTextView.text = item.price
Glide
.with(thumbnailImageView.context)
.load(item.imgUrl)
.transform(CenterCrop(), RoundedCorners(dpToPx(thumbnailImageView.context, 12)))
.into(thumbnailImageView)
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(ItemHouseBinding.inflate(LayoutInflater.from(parent.context), parent, false))
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(currentList[position])
}
companion object {
val diffUtil = object : DiffUtil.ItemCallback<Items>() {
override fun areItemsTheSame(oldItem: Items, newItem: Items): Boolean {
return oldItem.id == newItem.id
}
override fun areContentsTheSame(oldItem: Items, newItem: Items): Boolean {
return oldItem == newItem
}
}
}
private fun dpToPx(context: Context, dp: Int): Int {
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp.toFloat(), context.resources.displayMetrics).toInt()
}
}
HouseActivity
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private lateinit var adapter: HouseAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
adapter = HouseAdapter()
binding.recyclerView.adapter = adapter
getHouseListFromAPI()
}
private fun getHouseListFromAPI() {
val retrofit = Retrofit.Builder()
.baseUrl("https://run.mocky.io/")
.addConverterFactory(GsonConverterFactory.create())
.build()
retrofit.create(HouseService::class.java).also {
it.getHouseList()
.enqueue(object : Callback<HouseDto> {
override fun onResponse(call: Call<HouseDto>, response: Response<HouseDto>) {
if (response.isSuccessful.not()) {
Log.d("TAG", "response fail!!")
return
}
response.body()?.let {
Log.d("TAG", it.data.toString())
val result = response.body()?.data?.get(0)?.items
adapter.submitList(result)
}
}
override fun onFailure(call: Call<HouseDto>, t: Throwable) {
Log.d("TAG", t.message.toString())
}
})
}
}
}