CallAdapter 선택 메커니즘

Jiyoon Chae·2025년 6월 19일
0

안드로이드

목록 보기
22/23

💟 issue

NetworkResponseCallAdapter 에서 Call<T>를 Call<NetworkResponse<T>> 로 변환하는 어댑터를 생성했다.
근데 모든 api호출 응답을 NetworkResponse<T>로 바꾸고 싶은건 아니어서,, 
이렇게 어댑터 추가하면 다른 응답 타입으로 정의된 콜들은 어떻게 되는건지 걱정됨;;

💟 제미나이 답변에 의하면,,,

내가 새로 구현한 NetworkResponseCallAdapter 는
반환타입이 Call<NetworkResponse> 인 Api 호출에만 적용이 된다.
다른 반환 타입을 가진 api들은 NetworkResponseCallAdapter의 영향을 받지 않고, Retrofit에 등록된 다른 CallAdapter (또는 기본 CallAdapter)에 의해 처리된다.

  1. Retrofit의 CallAdapter 선택 메커니즘
  • Retrofit은 여러 CallAdapter.Factory를 가질 수 있음.
    • Retrofit.Builder().addCallAdapterFactory(..)를 통해 여러 팩토리를 추가 가능.
  • API인터페이스의 메소드가 호출될 때, Retrofit은 등록된 CallAdapter.Factory들을 추가된 순서대로 순회함!
  • 각 팩토리의 get()메소드가 호출되며, 이 메소드는 해당 API메소드의 반환타입을 인자로 받음.
  • 팩토리는 자신이 이 반환 타입을 처리할 수 있는지 판단함. 처리할 수 있으면 해당타입에 맞는 CallAdapter인스턴스를 반환, 처리할 수 없다면 null을 반환함.
  • 가장먼저 null이 아닌 CallAdapter를 반환하는 팩토리가 해당 Api 호출을 처리함.
  1. NetworkResponseCallAdapterFactory 의 동작
/ NetworkResponseCallAdapterFactory.kt (간략화된 예시)
        override fun get(
            returnType: Type,
            annotations: Array<Annotation>,
            retrofit: Retrofit
        ): CallAdapter<*, *>? {
            // 1. 반환 타입이 Call<T> 형태인지 확인
            if (getRawType(returnType) != Call::class.java) {
                return null // Call<T>가 아니면 우리 어댑터는 처리 안 함
            }

            // 2. Call<T>의 제네릭 파라미터 T가 NetworkResponse<R> 형태인지 확인
            val callInnerType = getParameterUpperBound(0, returnType as ParameterizedType)
            if (getRawType(callInnerType) != NetworkResponse::class.java) {
                return null // Call 안의 타입이 NetworkResponse<R>이 아니면 우리 어댑터는 처리 안 함
            }

            // 3. NetworkResponse<R>의 제네릭 파라미터 R (실제 응답 데이터 타입) 추출
            val responseType = getParameterUpperBound(0, callInnerType as ParameterizedType)
            return NetworkResponseCallAdapter<Any>(responseType) // Any 대신 실제 R 타입 사용
        }
  1. 다른 타입의 Api 호출
  • 만약 Api 인터페이스에 다음과 같은 메소드들이 있다면 처리 방식이
interface MyApiService {
            // 이 메소드는 NetworkResponseCallAdapter에 의해 처리될 가능성이 높음
            @GET("some/data")
            fun getMyData(): Call<NetworkResponse<MyActualData>>

            // 이 메소드는 NetworkResponseCallAdapter가 null을 반환하고,
            // 다른 CallAdapter (예: RxJava용 또는 기본 CallAdapter)에 의해 처리됨
            @GET("user/{id}")
            fun getUser(@Path("id") id: String): Call<User>

            // suspend 함수와 일반 타입을 사용하는 경우 (내부적으로 Call<User>로 변환 후 처리)
            @GET("user/profile")
            suspend fun getUserProfile(): User // 이 또한 NetworkResponseCallAdapter가 처리하지 않음

            // RxJava Observable을 반환하는 경우
            @GET("items")
            fun getItems(): Observable<List<Item>> // RxJavaCallAdapterFactory가 처리
        }
  • getMyData(): 반환 타입이 Call<NetworkResponse<MyActualData>>이므로 NetworkResponseCallAdapterFactory가 이를 인식하고 NetworkResponseCallAdapter를 제공하여 NetworkResponse로 래핑된 결과를 처리합니다.
  • getUser(): 반환 타입이 Call<User>입니다. NetworkResponseCallAdapterFactoryget() 메소드는 이 타입을 보고 자신이 처리할 수 없다고 판단하여 null을 반환합니다. 그러면 Retrofit은 다음으로 등록된 CallAdapter.Factory에게 처리를 위임합니다. 만약 다른 커스텀 어댑터가 없다면, Retrofit의 기본 CallAdapterCall<User>를 처리하게 됩니다.
  • getUserProfile(): suspend 함수는 내부적으로 Retrofit에 의해 Call<User>와 유사하게 처리됩니다. 이 역시 NetworkResponseCallAdapterFactory의 대상이 아닙니다.
  • getItems(): Observable<List<Item>>을 반환하면, RxJava3CallAdapterFactory (또는 RxJava2 버전)가 등록되어 있다면 해당 팩토리가 이를 처리합니다.

💟 결론

NetworkResponseCallAdapter는 명시적으로 NetworkResponse를 반환 타입으로 사용하는 API 호출에만 선택적으로 적용됨!!
다른 반환 타입을 가진 API들은 해당 타입에 맞는 다른 CallAdapter (또는 Retrofit의 기본 동작)에 의해 정상적으로 처리됨.
따라서 하나의 Retrofit 인스턴스 내에서 NetworkResponse를 사용하는 API와 그렇지 않은 기존 방식의 API들을 함께 사용할 수 있습니다. 이는 Retrofit의 유연한 CallAdapter 아키텍처 덕분 😊

profile
바닐라라떼 좋아☕

0개의 댓글