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

이도현·2023년 8월 25일

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개의 댓글