Retrofit이란?

jeunguri·2023년 2월 5일
0

android

목록 보기
13/13


Retrofit 이란?

  • REST API 통신을 위해 구현
  • OkHttp 라이브러리의 상위 구현체


Retrofit 구성요소

  • 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 설정


Retrofit 사용 방법

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>
)

  • JSON 데이터의 속성명과 변수명 +타입(ex. String, Int, Boolean) 일치 필수
  • JSON = @SerializedName("속성명") : 변수명 다르게 선언 가능

interface 정의

  • RetrofitAPI 인터페이스 생성
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) {
                        // 통신 실패에 대한 처리
                        // 인터넷 끊김, 예외 발생 등 시스템적인 이유의 실패   
                    }
                })
        }
    }


HTTP Status Code

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)



Retrofit 예제


완성 화면


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())
                    }
                })
        }
    }
}

0개의 댓글