Retrofit2 Api 호출 함수 가독성 높이기

이도현·2023년 8월 25일
0

0. 개요

기존의 Retrofit 호출 코드

package com.example.data.api

import android.graphics.Color
import android.util.Log
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory


class Retrofit(){
    private lateinit var apiService: ApiService
    fun call롯데월드(){
        val retrofit = Retrofit.Builder()
            .baseUrl("http://115.21.135.45:8000/")
            .addConverterFactory(GsonConverterFactory.create())
            .build()
        apiService = retrofit.create(com.example.data.api.ApiService::class.java)

        val call = apiService.getData()
        call.enqueue(object : Callback<ApiResponse> {
            override fun onResponse(call: Call<ApiResponse>, response: Response<ApiResponse>) {
                if (response.isSuccessful) {
                    val apiResponse = response.body()
                    if(apiResponse != null){
                        val area = apiResponse.롯데월드
                        val congestionLevel = area.congestionLevel
                        val datetime = area.datetime

// ... custom
                    }
                } else{
                    Log.e("API Error", "Request failed with code: ${response.code()}")
                }
            }

            override fun onFailure(call: Call<com.example.data.api.ApiResponse>, t: Throwable) {
                // API 요청 실패 처리
                t.printStackTrace()
            }
        })

    }

....(지역 5개 더있음)
  • 위의 코드가 같은작업을 하는게 5개 겹쳐져있다. 가독성도 떨어지고 무엇보다 시간 낭비이다.
  • 당시에는 시간이 촉박해 마감을 목표로 하느라 하나의 함수를 구현하는게 어떤 오류를 불러올지 몰라서 복붙으로 여러개 배치했지만 코드가 클린하지 못하다.
  • 하나의 함수에 매개변수로 지역이름을 받는다면 얼마나 간편하고 좋을까 해서 시작한다.

1. 리플랙션 vs 직접 매핑

1) 리플랙션은 코드가 실행 중인 프로그램의 메타 데이터(즉, 그 자체의 구조와 특성)에 액세스하고 수정할 수 있게 함.

  • 프레임워크 라이브러리, 플로그인 시스템, 직렬화 및 역 직렬화에 주로 사용
  • 장점: 유연성, 코드 양 감소
  • 단점: 성능, 안정성, 가독성

2) 직접 매핑

  • 특정 값을 또는 객체를 명시적으로 다른 값 또는 객체에 매핑하는 것, 고정된 구조에 변경이나 확장이 그리 빈번하지 않을 때 유용
  • 고정된 매핑, 성능 중심의 애플리케이션, 간단한 구조에 자주사용
  • 장점: 성능, 안정성, 가독성
  • 단점: 유연성, 코드양 증가

3)리플랙션 기반으로 수정

  • 지금은 잠실의 정보만 가져오지만 서울 전체의 정보를 가져올 업데이트가 고려되고 있는점
  • 을 고려하여 리플랙션 기반 방법으로 수정
class Retrofit(){
    private val retrofit: Retrofit = Retrofit.Builder()
        .baseUrl("http://115.21.135.45:8000/")
        .addConverterFactory(GsonConverterFactory.create())
        .build()
    private val apiService: ApiService = retrofit.create(com.example.data.api.ApiService::class.java)

    fun callApi(areaName: String) {
        val call = apiService.getData()
        call.enqueue(object : Callback<ApiResponse> {
            override fun onResponse(call: Call<ApiResponse>, response: Response<ApiResponse>) {
                if (response.isSuccessful) {
                    val apiResponse = response.body()
                    if(apiResponse != null){
                        val area = apiResponse.getClassField(areaName) // Reflectively get the field
                        val congestionLevel = area.congestionLevel
                        val datetime = area.datetime


// ... custome
                    }
                } else {
                    Log.e("API Error", "Request failed with code: ${response.code()}")
                }
            }

            override fun onFailure(call: Call<ApiResponse>, t: Throwable) {
                // API 요청 실패 처리
                t.printStackTrace()
            }
        })
    }

    // This is a helper function to get the value of the class field using reflection
    private fun ApiResponse.getClassField(fieldName: String): Hotspot {
        val field = this::class.java.getDeclaredField(fieldName)
        return field.get(this) as Hotspot
    }
}

4) 직접 매핑을 했다면

  • when절을 활용하여 직접 매핑을 한 것이다.
  • 리플랙션에선 Hotspot이란 데이터 클래스가 있어 수정을 할 때 Hotspot데이터 클래스만 수정하면 되지만
  • 아래와 같이 직접 매핑할경우 다른 곳에서도 같은 데이터를 사용할 때(만약 매핑 구간이 3 구간이 있다면) 3 구간 모두 수정해야 될 것이다ㅣ
  • 성능과 가독성은 직접매핑이 좀 더 좋으니 성능과 가독성을 고려할 경우 아래 방법과 같이 when구절을 사용하는 것도 좋은 방법이다.
// Retrofit 클래스 이름 변경
class ApiServiceManager(){
    private val retrofit: Retrofit = Retrofit.Builder()
        // ... 기존 코드
        .build()
    private val apiService: ApiService = retrofit.create(com.example.data.api.ApiService::class.java)

    fun callApi(areaName: String) {
        val call = apiService.getData()
        call.enqueue(object : Callback<ApiResponse> {
            override fun onResponse(call: Call<ApiResponse>, response: Response<ApiResponse>) {
                // ... 기존 코드
            }

            // ... 기존 코드
        })
    }

    // 리플렉션 대신 사용할 수 있는 매핑 로직
    private fun ApiResponse.getAreaByName(areaName: String): Hotspot? {
        return when(areaName) {
            "롯데월드" -> 롯데월드
            "방이동먹자골목" -> 방이동먹자골목
            "에비뉴엘월드타워점" -> 에비뉴엘월드타워점
            "롯데월드몰" -> 롯데월드몰
            "올림픽공원" -> 올림픽공원
            else -> null
        }
    }
}
profile
좋은 지식 나누어요

0개의 댓글