ABOUT ME

-

  • Android) Retrofit2 작동 원리 알아보기
    Android 2021. 4. 9. 15:12


    대부분의 안드로이드 개발자는 통신 라이브러리로 Retrofit을 사용하고 있습니다.

    오늘은 Retrofit의 특징 및 내부 작동방식에 대해 이해해보려고 합니다.

     

    Retrofit

    • REST API 통신을 위해 구현된 통신 라이브러리.
    • AsyncTask 없이 Background Thread에서 실행되면 callback을 통해 Main Thread에서 UI 업데이트를 간단하게 할 수 있도록 제공.
    • 다른 통신 라이브러리도 존재하지만 Retrofit의 성능과 구현 방법이 쉽다. 속도에 대한 내용은 참조

     

    Retrofit을 사용하기 위한 3가지 과정

    1. JSON 형식에 따른 POJO 클래스 만들기.

    • 클라이언트에서 서버에 request를 보내면 서버에서 JSON response를 줍니다.
    • 해당 JSON 형식을 안드로이드 스튜디오의 JSON To Kotlin Class 플러그인을 사용하면 쉽게 POJO 클래스를 만들 수 있습니다.

     

     

     

    2. HTTP 작업을 정의하는 API Interface 만들기.

    • @GET, @POST, @DELETE, @UPDATE 어노테이션을 사용하여 인터페이스를 생성합니다.
    // kotlin (Retrofit + RxJava)
    interface DaumService {
        @GET("v2/search/image")
        fun loadImages(
            @Query("query") query: String?,
            @Query("page") page: Int,
            @Query("size") size: Int
        ): Single<ImageResponse>
    }

     

    3. Retrofit.Builder 클래스 만들기.

    • 통신을 쉽게 하기 위해 OkHttp와 로그를 보기 위한 HttpLoggingInterceptor를 사용하여 크래스를 생성합니다.
    // kotlin (Retrofit + OkHttp)
    fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit =
            Retrofit.Builder()
                .baseUrl(BASE_URL)
                .client(okHttpClient)
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .build()

     

     

    위의 3가지 과정을 통해 API Interface의 메서드 객체를 선언하고 동기/비동기로 실행하여 서버에서 Response를 받아온 뒤 작업을 수행합니다.

      var retrofit: Retrofit = retrofitBuilder.build()
      var apiService: APIService = retrofit.create(ApiService::class.java)

     

    오늘의 주제는 "어떻게 API Interface를 구현해서 request를 날릴 수 있을까?"에 대해 알아보는 것입니다.

     

    var apiService: APIService = retrofit.create(ApiService::class.java)

     

    위의 코드에서 .create 메서드를 살펴보겠습니다.

     

    API Service의 객체를 만들 때 내부에서는 다음과 같이 동작합니다.

    주석의 내용을 보면 인터페이스에 정의된 API 엔드포인트들의 구현체를 만든다라고 적혀있습니다.

    엔드포인트란 커뮤니케이션을 가능하게, API가 서버에서 리소스에 접근할 수 있도록 가능하게 하는 URL입니다.

    예를 들어 https://example.com/endpoint 이런 식입니다. BASE URL인 https://example.com 뒤에 /로 시작하는 부분입니다.

    // Create an implementation of the API endpoints defined by the {@code service} interface
    public <T> T create(final Class<T> service) {
        validateServiceInterface(service);
        return (T)
            Proxy.newProxyInstance(
                service.getClassLoader(),
                new Class<?>[] {service},
                new InvocationHandler() {
                  private final Platform platform = Platform.get();
                  private final Object[] emptyArgs = new Object[0];
    
                  @Override
                  public @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args)
                      throws Throwable {
                    // If the method is a method from Object then defer to normal invocation.
                    if (method.getDeclaringClass() == Object.class) {
                      return method.invoke(this, args);
                    }
                    args = args != null ? args : emptyArgs;
                    return platform.isDefaultMethod(method)
                        ? platform.invokeDefaultMethod(method, service, proxy, args)
                        : loadServiceMethod(method).invoke(args);
                  }
                });
      }

     

    validateServiceInterface() 메서드를 호출하여 해당 인터페이스가 유효한 것인지 판단합니다. 

    그다음 validateEagerly로 판단하여 Platform 유형을 얻습니다.

    다음 인터페이스에 선언된 모든 메서드 객체를 포함하는 배열을 리턴합니다.

    그리고 메서드의 어노테이션이나 매개변수 타입 등을 파싱 하여 함께 저장합니다.

     

    전체적인 흐름은 위와 같습니다.

     

    그럼 자바에서는 인터페이스 자체를 객체화해서 사용하는 게 원칙적으로 허용하지 않는데, 어떻게 하는 걸까요?

     

    Retrofit은 내부적으로 Dynamic Proxy를 사용하고 런타임에 어노테이션을 파싱 합니다. Interface를 런타임에 읽어서 그 Interface에 선언한 메서드 및 어노테이션 등을 Reflection을 사용해서 처리하여 Proxy Class를 만들어 그 구현체를 사용하는 방식입니다.

     

    Proxy를 사용하여 동적으로 런타임에 객체를 만드는 것입니다.

    Proxy는 보안상의 문제로 직접 통신을 주고받을 수 없는 사이에서 중계 기능을 하는 것입니다.

    Proxy를 클라이언트와 서버 사이에 존재하며 대리로 통신을 수행하는 것을 의미합니다.

     

     

    Retrofit은 한번 만들어진 인스턴스를 내부적으로 캐시 하고 있습니다.

    즉 두 번째 호출부터는 어노테이션에 있는 정보가 캐시 되지만 첫 번째 호출은 길어질 수 있습니다. 

     


    Reference

    Android의 HTTP 클라이언트 라이브러리

    [Android Studio] Retrofit2 기본 사용법 / Retrofit 의문점 풀어헤치기 (스압)

    github.com/HwangEunmi/Retrofit-Sample

    반응형

    댓글

Designed by Me.