[안드로이드] Android 통신 라이브러리의 역사

dada·2022년 8월 16일
5

Android

목록 보기
13/16
post-thumbnail
post-custom-banner

참고)
안드로이드의 HTTP 통신 라이브러리 고찰
[Android] OkHttp & Retrofit
OkHttp or Retrofit for Android?

✅공부배경

  • 프로젝트를 하면서 HTTP 통신을 할때 고민없이 Retrofit2, Okhttp3 의존성을 추가해 사용했었습니다.

  • Retrofit2 라이브러리 이전에 HTTP 통신은 어떤 방식으로 처리했고, Retrofit2를 사용하는 이유에 대한 이해가 필요하다고 생각했습니다.

  • 그래서 Android 통신 라이브러리에 대한 역사를 공부하고, Retrofit2에 대해 알아보고자 합니다! 그 전에 비동기처리, HTTP에 대한 사전지식이 필요하여 이부분부터 공부하고 가겠습니다~ 갈길이 살짝 머니 안전벨트 꽉 잡으세요(?)ㅎㅎ

✅사전지식 - Main 스레드와 백그라운드 스레드

✔ 스레드 종류

  • 안드로이드의 스레드는 크게 2종류입니다
    • 메인스레드(UI스레드): 1개만 존재
    • 백그라운드 스레드: 여러개 존재 가능
      • 네트워크 작업, 파일업로드와 다운로드, 이미지 처리, 데이터 로딩같은 일을함
      • 메모리 이외의 다른 곳에서 데이터를 가져오는 작업은 백그라운드에서 실행하길 권장
  • 프로세스가 실행될 때 최초의 실행 시작점이 되는 main()함수로부터 시작되는 스레드가
    메인스레드
    입니다 ("새로운 스레드가 오직 메인 스레드에 의해서만 실행될 수 있다"고 오해하면 안됩니다. 스레드는 "기존에 실행 중인 스레드"에서 실행될 수 있습니다. 단지, 메인 스레드는 프로세스가 가지는 최초의 스레드일 뿐입니다.)

✔ 메인스레드 실행 과정

  • 안드로이드가 메인 스레드를 시작시키는 과정을 보겠습니다
  1. 안드로이드의 애플리케이션을 실행
  2. 안드로이드 프레임워크(Framework)에 이미 구현되어있는 "android.app.ActivityThread" 클래스가 가진 main()함수가 실행
  3. main()함수가 실행되며 앱의 동작에 필요한 여러 준비 동작을 수행
    • 메인 스레드(UI Thread)를 실행
    • 런처(Launcher)로 지정된 액티비티를 찾아서 실행

✔ 안드로이드는 싱글 스레드 모델

  • 안드로이드 UI는 기본적으로 싱글 스레드 모델로 작동합니다. 안드로이드 화면을 구성하는 뷰나 뷰그룹을 하나의 스레드에서만 담당하는 원칙을 싱글 스레드 라고 합니다

  • 싱글 스레드 모델은 2가지 규칙이 있습니다

      1. 메인 스레드(UI 스레드)를 블럭하지 말 것
      1. 안드로이드 UI 툴킷(android.widget,android.view)은 오직 UI 스레드에서 그릴 것
      • 이유: 화면의 UI그리기 담당을 하나의 스레드가 해야하는 이유는, 안드로이드 앱처럼 UI가 있는 프로세스는 UI를 제대로 표시하기 위해 각 요소를 그리는 순서가 절대적으로 중요하기 때문에 반드시 하나의 스레드, 즉, 메인 UI 스레드에서 순차적으로 그리도록 만들어야 하는 것입니다
      • EX) 메인 스레드와 다른 스레드, 두 개 이상의 스레드가 동시에 같은 텍스트뷰에 setText()를 시도하는 경우 둘 중 어느 스레드의 setText()가 적용될지 예측할 수 없고, 사용자는 둘 중 하나의 값만을 볼 수 있어 다른 한 스레드의 결과는 버려집니다. 이같이 두 개 이상의 스레드를 사용할 때의 동기화 이슈를 차단하기 위해 싱글 스레드 원칙으로 작동해야 합니다
  • 만약 UI스레드가 UI를 그리는 일 말고 네트워크 처리와 같은 무거운 작업을 함께 실행한다면 어떻게 될까요? (다른 스레드에서 UI를 그리는 것도 문제가 되지만 메인 스레드에서 UI 그리는 일 말고 다른 일을 한다면?)

  • 일단, 안드로이드는 초당 60프레임을 지원하고 한 프레임은 16ms안에 그려져야 합니다 16ms 안에 화면(UI)을 그리는 작업이 완료되지 않으면 쟁크가 발생합니다

    • 쟁크; jank : 프레임 누락으로 인해 끊겨보이는 현상
  • 쟁크 발생과 더불어 UI이벤트 및 작업에 대해 5초 내에 응답안하면 안드로이드 시스템은 ANR(Android Not Responding=응용프로그램 응답 안함)팝업창을 표시합니다(안드로이드 시스템이 UI가 그려지는 속도가 늦는 걸 유저의 모바일 사용 환경을 방해한다고 판단하기 때문)

✔ 결과적으로 비동기작업이 필요하다!

  • 따라서 시간이 걸리는 작업을 하는 코드는 여분의 스레드를 사용하여 메인 스레드에서 분리해야 하고, 자연스럽게 메인 스레드와 다른 스레드가 통신하는 방법이 필요하게 됩니다. 즉 UI스레드가 원활하게 돌아가기 위해서는 비동기작업이 필수적입니다
    • 동기(Synchronous): 작업을 수행하고 그 작업이 완료될 때까지 다른 작업을 하지못하고 기다리는 방식
    • 비동기(Asynchronous): 어떤 작업을 수행하지만 완료와 상관없이 계속해서 작업을 할 수 있는것(다른 스레드가 작업하고 메인 스레드는 UI그리기)

✔ 비동기 처리 방법

  • 다른 스레드에서 메인 스레드로 접근하기 위해(비동기 작업을 처리하기 위해) 사용할 수 있는 방법은 3가지가 있습니다.
    • Looper와 Handler: 두 개 이상의 스레드를 사용할 때의 동기화 이슈를 차단하기 위해서 Looper와 Handler를 사용 쓰레드는 수동으로 관리해야 하기 때문에 관리가 어렵고 코드를 읽기 어려워 질 수 있다는 단점이 있습니다
    • HandlerThread: 메인 스레드는 Looper가 기본적으로 생성돼 있지만, 새로 생성한 스레드는 기본적으로 Looper를 가지고 있지 않고, 단지 run 메서드만 실행한 후 종료하기 때문에 메시지를 받을 수 없어 불편합니다. HandlerThread는 생성할 때 Looper를 자동으로 보유한 클래스로 내부에 반복해서 루프를 도는 Looper를 가져 Looper와 Handler를 직접 사용할때보다 편리합니다
    • AsyncTask: Thread나 Message Loop 등의 작동 원리를 크게 고려하지 않고도 UI 스레드를 적절하고 쉽게 사용할 수 있습니다. 리스트에 보여주기 위한 데이터 다운로드 등 UI와 관련된 독립된 작업을 실행할 경우 AsyncTask로 간단하게 구현할 수 있습니다

✅사전지식 - HTTP 통신

✔ 웹

  • 1990년에 등장한 웹은 크게 4가지로 구성되어 있습니다
  • HTML: 웹페이지를 만드는 언어
  • URL, URI: 원하는 웹페이지에 방문하도록 돕는 주소체계
  • Web browser, Web server: 웹페이지를 주고받는 소프트웨어
  • HTTP(HyperText Transfer Protocol): Web browser, Web server가 통신할때 사용하는 규칙
  • 이것은 웹을 구성하는 가장 중요한 요소고 웹이 아무리 복잡해도 이 구성 4개를 벗어나는 일은 없습니다
  • HTTP(HyperText Transfer Protocol) 은 매우 단순한 통신 규칙이었는데 웹이 폭발성장하면서 기존 HTTP로는 성능 보안 안전성 확보가 어려워지고 더 풍부한 프로토콜로 발전하게 되었습니다 결국 멀티미디어 파일 전송도 할 수 있는 정도로 인터넷이 동작하는 근간이 되었습니다

✔ HTTP

  • html, css, 이미지 같은 파일은 서로 주고받는 컨텐츠고 이걸 서로 주고받으려면 서버 클라이언트가 공통으로 알아들을 수 있는 교환방식=HTTP 메시지(request Message/ Response Message)가 필요합니다

1. Request Message

① 요청 라인: HTTP Method, Web page, HTTP version
② 요청 헤더: Host, Accept, User-Agent, Cokie, Referer

  • Host: 클라이언트가 요청한 도메인 정보
  • Accept: 웹 서버로부터 수신되는 데이터 중 웹 브라우저가 처리할 수 있는 데이터의 형식 정의
  • User-Agent: 사용자 웹 브라우저 종류 및 버전 정보
  • Cookie: 클라이언트 로컬에 저장되는 key-value쌍의 데이터 파일
  • Referer: 경유한 웹 사이트에 대한 정보

③ 공백 라인: 요청 헤더와 요청 바디를 구분하는 라인
④ 요청 바디: 클라이언트가 서버에 실제 요청한 내용

2. Response Message


① 상태 라인: HTTP Version, Status Code
② 응답 헤더: Date, Server, Content-Type, Last-Modified
③ 응답 바디: 실제 응답받은 메시지(데이터)

html, css, 이미지 같은 파일은 서로 주고받는 컨텐츠고 이걸 서로 주고받으려면 서버 클라이언트가 공통으로 알아들을 수 있는 교환방식=HTTP 메시지(request Message/ Response Message)가 필요

✔ API

*인터페이스(Interface): 사물과 인간 사이의 경계에서 상호 간의 소통을 위해 만들어진 물리적 매개체나 프로토콜을 말합니다. 예를 들어 TV 리모콘 전원 버튼은 인터페이스다. 사람이 리모콘 버튼을 눌러 TV가 켜지도록 연결하는 매개체이기 때문입니다.

  • API(Application Programming Interface): 애플리케이션(응용프로그램)에서 사용할 수 있도록, 운영체제나 프로그래밍 언어가 제공하는 기능을 제어할 수 있게 만든 인터페이스. 컴퓨터와 인간을 연결시키는 사용자 인터페이스(UI)와 반대로, API는 컴퓨터나 소프트웨어를 서로 연결하는 인터페이스

  • 서버는 프로그램에게 자신이 제공하고자 하는 데이터나 기능을 제어할 수 있는 API로 만들면, 접근 권한이 있는 프로그래머나 프로그램이 API를 통해 서버에서 제공하는 데이터를 요청해서 사용할 수 있게 됩니다.

    • EX) 날씨 정보 앱을 만들려면, 기상청(서버)이 제공하는 API에 원하는 날씨 정보를 요청해 데이터를 받은 다음 UI 형태로를 사용자에게 날씨 정보를 제공할 수 있다. 더 자세히는 기상청이 DB서버에 날씨 정보를 보관해놓고, 다른 응용 프로그램들이 DB서버에서 날씨 데이터를 조회하고 조작할 수 있도록 API를 미리 개발해놓는다.
    • 서버 시스템이 동작하는 방식에 관한 내부의 프로세스를 숨기는 것으로, 내부의 세세한 부분이 나중에 변경되더라도 프로그래머가 유용하게 사용할 수 있고 일정하게 관리할 수 있는 부분들만 노출시킨다.

API는 컴퓨터, 응용프로그램 등을 연결하는 매개체/인터페이스. 서버가 제공하는 API를 사용한다면 서버는 API를 통해 시스템 동작 방식은 숨기고 필요한 부분만 노출시킬 수 있다(API사용하는 프로그래머는 신경쓸 사항이 적어지니 좋다!)

✔ HTTP API

  • HTTP(request message, response message)통신규칙을 사용하여 프로그램끼리 소통하는 API를 말합니다 보통 우리가 흔히 보는 OPEN API, facebook API, kakao API 등의 대부분 API는 HTTP(HyperText Transfer Protocol)라는 통신 규칙으로 소통하는 API(Application Programming Interface)입니다

    HTTP(request message, response message)통신규칙을 사용하여 프로그램끼리 소통하도록 만든 API(Application Programming Interface)

✔ HTTP를 사용하지 않는 API도 있나?


(참조: 짐코딩 API란 무엇인가? https://www.youtube.com/watch?v=Jg3FFBLyhK0)

  • 미세먼지 측정기에서 미세먼지 농도 값을 읽어와서 공기가 안 좋으면, 자동으로 창문을 개방하는 IoT어플리케이션을 개발하고 싶다고 하자. 미세먼지 측정기와 스마트 창문이 IoT 애플리케이션과 통신(소통)할 수 있는 API가 있어야 합니다. 이때 사용하는 소통 방법은 HTTP통신이 아닌 저사양/저전력 환경에 적합한 MQTT, CoAP프로콜을 사용해야 합니다

IOT 응용프로그램은 HTTP프로토콜이 아닌 MQTT, CoAP프로콜을 사용한 API로 통신

✔ REST

  • REST(Representational State Transfer): 한마디로 네트워크 아키텍처 스타일입니다. (네트워크 자원을 정의하고 처리하는 방법 전반/HTTP를 사용하는 청사진,모범사례라고 생각할 수 있습니다)

  • 2000년도에 로이 필딩 (Roy Fielding)의 박사학위 논문에서 최초로 소개되었습니다. 로이 필딩은 HTTP의 주요 저자 중 한 사람으로 그 당시 웹(HTTP) 설계의 우수성에 비해 제대로 사용되어지지 못하는 모습에 안타까워하며 HTTP의 장점을 최대한 활용할 수 있는 네트워크 아키텍쳐 스타일(네트워크 자원을 정의하고 처리하는 방법 전반)로써 REST를 발표했습니다

즉, REST는 HTTP를 잘 활용하기 위한 원칙이자 네트워크 아키텍쳐 스타일(청사진이자 모범사례)이라고 할 수 있다

✔ REST 원칙은 뭘까?

Representational State Transfer
자원의 표현으로 상태를 전달하는 것

  • URI로 자원을 표현하는 데에 집중하고, 자원의 상태(행위)에 대한 정의는 HTTP METHOD로 하는 것
  • RESTful하게 설계하는 것의 중심 규칙인 두가지
      1. URI은 자원(리소스)을 표현에 집중
      1. 자원에 대한 행위는 HTTP Method(GET, POST, PUT, DELETE)로 표현
        - GET /members/delete/1 -> DELETE /members/1
        위와 같은 방식은 REST를 제대로 적용하지 않은 URI이다. URI는 자원을 표현하는데 중점을 두어야 한다. delete와 같은 행위에 대한 표현이 들어가서는 안된다.
        - GET /members/show/1 ->GET /members/1
        자원은 URI로 표현되며 동사(자원의 행위)를 사용하지 않는다.

✔ REST API

  • 우아한형제들 김영한 개발이사님 답변에 따르면, HTTP API와 REST API는 사실 거의 같은 의미로 사용하고 있다고 하셨다!. 정확한 차이점으로 REST API는 HTTP 프로토콜을 따르면서 아래의 4가지 가이드 원칙을 지켜야 합니다.

1) 자원의 식별
2) 메세지를 통한 리소스 조작
3) 자기서술적 메세지
4) 애플리케이션의 상태에 대한 엔진으로서 하이퍼미디어(HATEOAS)

  • 이러한 제약 조건들을 완벽하게 지키면서 개발하는 것을 RESTful API라고 하는데 실무에서는 이런 방법으로 개발하는 것은 현실적으로 어렵고 개발비용 대비 효과가 있는 것도 아니라고 하셨다. (4번째 원칙이 특히나 구현하기 어렵다고,,)

  • 그런데 이미 많은 사람들이 이 조건들을 지키지 않아도 REST API라고 하기 때문에 HTTP API와 같은 의미로 사용하고 있다고 합니다. 하지만 위 제약 조건들을 모두 지켜야 REST API라고 말할 수 있습니다

REST API는 HTTP API(HTTP(request message, response message)통신규칙을 사용하여 프로그램끼리 소통하도록 만든 API(Application Programming Interface))와 거의 같은 개념으로 사용되지만 HTTP 프로토콜을 따르면서 4가지 원칙을 철저히 지킨 API


✅Android 통신 라이브러리의 역사

2007/11/05 : Android가 발표
2011/09/29 : HttpURLConnection을 권장하는 블로그가 나옴
2013/05/06 : OkHttp 1.0.0이 릴리즈 됨
2013/05/14 : Retrofit 1.0.0이 릴리즈 됨
2013/05/21 : Volley가 릴리즈 됨
2016 : Android6.0에서 HttpClient가 삭제 됨
2016/03/12 : Retrofit2가 릴리즈 됨

  • 안드로이드가 HTTP통신을 위해 사용한 기술들의 주요 역사를 살펴보면 다음과 같습니다

✔ HttpClient

  • Http 통신을 용이하게 수행하기 위해 Apache에서 제작한 라이브러리입니다. 안드로이드 초기에 주로 사용되었으며 실제로는 HttpClient를 래핑한 DefaultHttpClient나, 안드로이드에 맞게 개수한 AndroidHttpClient가 사용되었습니다.

  • HttpClient안드로이드와 독립적으로 개발되는 라이브러리인지라 변경점을 안드로이드 SDK에 일괄적으로 즉시 반영할 수 없었습니다. 결국 버전이 뒤쳐지면서 버그가 계속 발생하게 되었고, HttpClient는 Android 5.1에서 Deprecated 되며 6.0에서는 아예 삭제되었습니다. 이 시기 클라이언트의 버그는 네이버 D2 블로그의 Android의 HTTP 클라이언트 라이브러리에 잘 정리되어 있으니 참고하시기 바랍니다.

✔ HttpUrlConnection

  • HttpClient를 삭제하면서 구글에서 제시한 대안이 HttpUrlConnection인데요, 구글에서 HttpUrlConnection사용하라고 권장한 블로그 포스팅도 있습니다. 기존의 URLConnection에 HTTP를 다루는데 필요한 메서드를 추가한 클래스입니다.

  • 현재 HttpURLConnection은 Deprecated되었습니다

  • URL.openConnection()으로 얻어진 URLConnection 객체를 HttpURLConnection으로 캐스팅하여 데이터 송수신을 행하고 disconnect로 접속을 종료하는 방식으로 사용합니다. 비동기 처리는 직접 해주어야 합니다

// WeatherRepositoryImplHttpURLConnection.java
public class WeatherRepositoryImplHttpURLConnection implements WeatherRepository {
    public static final String TAG = WeatherRepositoryImplHttpURLConnection.class.getSimpleName();

    @Override
    public void getWeather(final RequestCallback callback) {
        new AsyncTask<Void, Void, Weather>() {
            // 처리 전에 호출되는 메소드
            @Override
            protected void onPreExecute() {
                super.onPreExecute();
            }

            // 처리를 하는 메소드
            @Override
            protected Weather doInBackground(Void... params) {
                final HttpURLConnection urlConnection;
                try {
                    URL url = new URL(uri.toString());
                    urlConnection = (HttpURLConnection) url.openConnection();
                    urlConnection.setRequestMethod("GET");
                    urlConnection.setRequestProperty("Content-Type", "application/json; charset=utf-8");
                } catch (MalformedURLException e) {
                    return null;
                } catch (IOException e) {
                    return null;
                }
                final String buffer;
                try {
                    BufferedReader reader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream(), "UTF-8"));
                    buffer = reader.readLine();
                } catch (IOException e) {
                    return null;
                } finally {
                    urlConnection.disconnect();
                }
                if (TextUtils.isEmpty(buffer)) {
                    return null;
                }
                return new Gson().fromJson(buffer, Weather.class);
            }

            // 처리가 모두 끝나면 불리는 메소드
            @Override
            protected void onPostExecute(Weather response) {
                super.onPostExecute(response);
                // 통신 실패로 처리
                if (response == null) {
                    callback.error(new IOException("HttpURLConnection request error"));
                } else {
                    Log.d(TAG, "result: " + response.toString());
                    // 통신 결과를 표시
                    callback.success(response);
                }
            }
        }.execute();
    }
}

✔ Volley

  • HttpUrlConnection을 사용할 때는 Application Not Responding(ANR)을 피하기 위해 백그라운드 스레드도 만들어야하고, 버퍼를 통한 입출력도 준비해야 하고, 캐시나 예외처리도 한땀한땀 다 처리해 주어야 하는 불편함이 있었습니다.

  • 그래서 구글에서는 HTTP 연결을 만들때마다 비동기 처리를 감싸주고 있기 때문에 AsyncTask 등을 사용하지 않아도 되는 라이브러리Volley를 2013년 Google I/O에서 발표했습니다.

  • 사용법은 다음과 같습니다. HTTP 메소드와 url 정보를 가진 Request를 만들어서 RequestQueue에 넣어줍니다. 그러면 Volley가 알아서 스레드를 만들고 HttpUrlConnection으로 통신을 수행한 뒤 response를 반환해줍니다. 코드를 보시면 HttpUrlConnection을 직접 사용할 때보다 코드가 더 읽기 쉬워진 것을 알 수 있습니다.

  • 하지만 Volley는 반환받은 JSON 객체를 데이터클래스로 바로 변환해주지 못하기 때문에, 별도의 과정을 통해 직접 변환해서 사용해야합니다

// WeatherRepositoryImplVolley.java
public class WeatherRepositoryImplVolley implements WeatherRepository {
    public static final String TAG = WeatherRepositoryImplVolley.class.getSimpleName();

    RequestQueue queue;

    public WeatherRepositoryImplVolley(Context context) {
        queue = Volley.newRequestQueue(context);
    }

    @Override
    public void getWeather(final RequestCallback callback) {
        final JsonObjectRequest request =
                new JsonObjectRequest(uri.toString(), null, new Response.Listener<JSONObject>() {
                    @Override
                    public void onResponse(JSONObject response) {
                        Log.d(TAG, "result: " + response.toString());
                        final Weather weather = new Gson().fromJson(response.toString(), Weather.class); //파싱해야함
                        callback.success(weather);
                    }
                }, new Response.ErrorListener() {
                    @Override
                    public void onErrorResponse(VolleyError error) {
                        callback.error(error);
                    }
                });
        queue.add(request);

    }
}

✔ OkHttp

  • 그런 와중에 2013년 5월 6일엔 Square에서 OkHttp라는 HTTP 클라이언트 라이브러리를 발표합니다. 이 라이브러리는 Okio와 코틀린을 활용해 쓰여졌고 다음과 같은 특징이 있습니다. Connection pooling과 Redirection을 도입해 접속을 더 안정적이게 하면서도, 속도를 개선시킬 수 있는 여러가지 기술이 적용된 것으로 보입니다

  • OkHttp는 통신을 동기화로할지 비동기 처리로할지 선택할 수 있습니다. 그러나 스레드를 넘나들 수 없으므로 Handler를 사용합니다.

// WeatherRepositoryImplOkHttp3.java
public class WeatherRepositoryImplOkHttp3 implements WeatherRepository {
    public static final String TAG = WeatherRepositoryImplOkHttp3.class.getSimpleName();

    private Handler handler = new Handler();

    @Override
    public void getWeather(final RequestCallback callback) {
        final Request request = new Request.Builder()
                // URL 생성
                .url(uri.toString())
                .get()
                .build();
        // 클라이언트 개체를 만듬
        final OkHttpClient client = new OkHttpClient();
        // 새로운 요청을 한다
        client.newCall(request).enqueue(new Callback() {
            // 통신이 성공했을 때
            @Override
            public void onResponse(Call call, Response response) throws IOException {
                // 통신 결과를 로그에 출력한다
                final String responseBody = response.body().string();
                Log.d(TAG, "result: " + responseBody);
                final Weather weather = new Gson().fromJson(responseBody, Weather.class);
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        callback.success(weather);
                    }
                });
            }

            // 통신이 실패했을 때
            @Override
            public void onFailure(Call call, final IOException e) {
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        callback.error(e);
                    }
                });
            }
        });
    }
}

✔ Retrofit

  • Retrofit은 OkHttp를 개발한 Square에서 2013/05/14 에 발표한 라이브러리입니다. (OkHttp를 발표한지 8일만에) HttpURLConnection을 사용하기 편하도록 랩핑한게 Volley라면 Retrofit은 OkHttp를 랩핑한 것입니다.

  • Retrofit은 어노테이션을 사용하여 코드를 생성하기 때문에 이를 위한 인터페이스를 만듭니다

  • 사용법은 다음과 같습니다. 우선 REST API 콜을 인터페이스 형식으로 준비합니다. 그리고 Retrofit 객체를 만들어서 인터페이스의 인스턴스를 생성합니다. 마지막으로 인터페이스를 동기 혹은 비동기적으로 구동시켜 response를 반환받게 되어 있습니다.

// WeatherRepositoryImplRetrofit2.java
public class WeatherRepositoryImplRetrofit2 implements WeatherRepository {
    public static final String TAG = WeatherRepositoryImplRetrofit2.class.getSimpleName();

    private final WeatherService service;

    public WeatherRepositoryImplRetrofit2() {
        final Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(new Uri.Builder().scheme(SCHEME).authority(AUTHORITY).build().toString())
                .client(new OkHttpClient())
                .addConverterFactory(GsonConverterFactory.create())
                .build();
        service = retrofit.create(WeatherService.class);
    }

    @Override
    public void getWeather(final RequestCallback callback) {
        service.getWeather(130010).enqueue(new Callback<Weather>() {
            @Override
            public void onResponse(Call<Weather> call, Response<Weather> response) {
                Log.d(TAG, "result: " + response.body().toString());
                callback.success(response.body());
            }

            @Override
            public void onFailure(Call<Weather> call, Throwable error) {
                callback.error(error);
            }
        });
    }

    private interface WeatherService {
        @GET(PATH)
        Call<Weather> getWeather(@Query("city") int city);
    }

}

✔ Volley / Retrofit

  • HTTP 라이브러리에는 Volley도 있는데, Retrofit을 가장 많이 사용하는 이유는 무엇일까요?

👉 높은 성능

  • 한 눈에 보아도 Retrofit 이 위의 두 항목보다 빠른 성능을 자랑하고 있는 모습을 확인할 수 있습니다

👉 뛰어난 가독성

  • Annotation으로 HTTP 메소드를 정의함으로서 코드의 구현이 쉬워지며 개발자들은 행위를 손쉽게 알아볼 수 있게 되어 직관적으로 코드를 설계할 수 있게 된다.

👉 쉬운 유지보수

  • Retrofit은 서버 연동 시 주로 주고받는 데이터인 JSON, XML을 자동을 파싱해주는 Converter 연동을 지원해주기 때문에, 개발자 입장에서는 유지보수가 매우 편리할 수 밖에 없다.
  • VolleyRetrofit은 지금도 꾸준히 갱신되고 있는 라이브러리입니다. 코드 가독성은 대부분 Retrofit이 더 좋다는 평가인 것 같습니다.

  • 개인적으로는 Annotation으로 HTTP 메소드를 정의해서 사용하는 Retrofit이 전체 구조를 파악하기 더 좋은것 같아 이쪽을 선호합니다. 한가지 재밌는 것은 구글의 권장 앱 아키텍처였던것에서는 HTTP 통신에 Volley가 아닌 Retrofit을 추천하고 있다는 점입니다.

Retrofit은 Volley보다 빠르고, Annotation으로 HTTP 메소드를 정의하므로 가독성이 좋고, JSON, XML을 자동을 파싱해주는 Converter 연동을 지원하기 때문에 좋다!!

✔ OkHttp3 / Retrofit2

  • 그런데 많은 프로젝트에서 OkHttp와 Retrofit을 함께 사용하는 모습을 보신적이 있을겁니다! 이 둘의 차이를 알아보고(Retrofit이 더 좋다는 이야기가 주된 내용이지만) 같이 사용하는 이유도 알아봅시다

  • 둘 다 같은 회사(Square)에서 만든 HTTP 통신 라이브러리입니다. 앞서 언급한것처럼 OkHttp를 래핑하여 더 Type-safe하고, 더 직관적으로 사용할 수 있도록 인터페이스로 만들어진 게 Retrofit입니다. 따라서 완전히 다르진 않지만, 지원 기능과 용도 면에서 어느정도 차이가 있습니다.

👉 Network

  • Retrofit에선 BASE URL을 인자로 전달하여 간단하게 객체를 생성합니다
Retrofit.Builder()
    .addConverterFactory(GsonConverterFactory.create())
    .baseUrl(BASE_URL)
    .build()
  • 다음으로 인터페이스를 생성하고, 원하는 요청 방식과 파라미터에 대해 정의합니다.
  interface wikiApiServe{
      @GET(END_POINT)
      fun hitCountCheckCall(
          @Query(PARAM_ACTION) action: String,
          @Query(PARAM_FORMAT) format: String,
          @Query(PARAM_LIST) list: String,
          @Query(PARAM_SRSEARCH) srsearch: String)
      : Call<Model.Result>
  }
  • 이후 네트워크 호출을 할 때마다 미리 정의해놓은 인터페이스 메서드에 필요한 데이터들을 전달해서 호출할 수 있습니다
  wikiApiServe.hitCountCheckCall(
          VALUE_QUERY, VALUE_JSON, VALUE_SEARCH, searchString)
  • 반면 OkHttp에선 메서드에 필요한 데이터를 전달하는게 아니라 다음과 같이 수동으로 요청을 구성해야 합니다. 물론 한다면 할 수야 있지만 URL을 입력하는 과정에서 오타가 날 수도 있고, 무엇보다 가독성 면에서 좋지 못합니다.
  val request = Request.Builder().url("$BASE_URL$BASE_PATH/?" +
        "$PARAM_ACTION=$VALUE_QUERY&$PARAM_FORMAT=$VALUE_JSON&" +
        "$PARAM_LIST=$VALUE_SEARCH&$PARAM_SRSEARCH=$searchString")
        .build()

👉 Result

  • 다음으로 네트워크 통신 시, 반환되는 응답 객체에 대해 두 라이브러리 간 어떤 차이가 있는지 알아봅시다. JSON은 자바나 코틀린에서 바로 사용할 수 있는 데이터 형식이 아니기 때문에 JSON을 데이터클래스로 파싱해줄 컨버터를 사용해야 합니다. 객체 생성 시 GSON Converter Factory를 연결했다면, Retrofit은 통신 성공 시 응답 객체의 body를 별도의 변환 과정 없이 바로 사용할 수 있습니다. (파싱해주는 Converter 연동을 지원)
call?.enqueue(
  object : Callback<Model.Result> {
    override fun onFailure(call: Call<Model.Result>, t: Throwable) {
          ...
    }

    override fun onResponse(call: Call<Model.Result>, 
    	response: Response<Model.Result>) {
          	response.body()?.let { //바로 사용 가능 }
    }
  • 반면 OkHttp에서는 반환받은 JSON 객체를 데이터클래스로 바로 변환해주지 못하기 때문에, 별도의 과정을 통해 직접 변환해서 사용해야 합니다
client.newCall(request).enqueue(
  object : okhttp3.Callback {
    override fun onFailure(call: okhttp3.Call, e: IOException) {
         ...
    }

    override fun onResponse(call: okhttp3.Call, 
          response: okhttp3.Response) {
            response.body()?.let { 
               val result = Gson().fromJson(it.string(), 
                            Model.Result::class.java)
               useResult(result) 
            }
        }
)
  • 또한 결과가 반환된 직후 메인스레드에서 바로 사용할 수 있냐 없냐에도 차이가 있습니다. Retrofit의 경우 enqueue를 사용하면 네트워크 호출이 자동으로 백그라운드에서 이루어지고 결과가 반환되면 자동으로 메인스레드에 전달되기 때문에 Toast와 같은 UI 관련 메서드에 결과값을 사용할 수 있습니다
override fun onResponse(call: Call<Model.Result>, 
                 response: Response<Model.Result>) {
          Toast.makeText(...).show()
    })
  • OkHttp도 enqueue를 사용하면 네트워크 호출이 자동으로 백그라운드에서 수행되지만, 스레드를 넘나들지 못해서 결과가 반환되어도 여전히 백그라운드에 남아있기 때문에 결과값을 메인스레드에서 사용하기 위해서 runOnUiThread를 사용해야 합니다.(handle를 사용해도 됨)
override fun onResponse(call: okhttp3.Call, 
          response: okhttp3.Response) {
          runOnUiThread {          
             Toast.makeText(...).show()
          }
      }
  • Retrofit은 훌륭한 가독성과 역직렬화할 필요가 없다는 면만 봐도 아주 쓸만한 라이브러리입니다. 그렇다고 무조건 Retrofit만 사용하는것은 아닙니다. Retrofit 역시 HTTP 통신을 할 때 OkHttp에 의존하고 있고, 두 라이브러리 모두 각각 이점을 가지고 있기 때문에(OkHttp의 경우 OkHttp Client에 네트워크 Intercepter를 통해 API가 통신되는 모든 활동을 모니터링 할 수 있으며 서버 통신 시간 조절이 가능하다는 장점이 있습니다), 최고의 성능을 내기 위해서 둘 다 사용하는 게 보편적입니다

Retrofit이 OkHttp보다 좋은점은,
1. 어노테이션(Annotation) 사용으로 코드의 가독성이 좋고, 직관적인 설계가능.
2. 통신 결과값을 JSON으로 변환해줄 필요가 없음
3. 결과값을 메인스레드에서 바로 사용할 수 있음
하지만 이 둘을 같이 쓰는 이유는, OkHttp가 제공하는 Intercepter를 통해 API가 통신되는 모든 활동을 모니터링 할 수 있으며 서버 통신 시간 조절이 가능하다는 장점!
결과적으로, 최고의 성능을 내기 위해서 둘 다 사용하는 게 보편적

profile
'왜?'라는 물음을 해결하며 마지막 개념까지 공부합니다✍
post-custom-banner

2개의 댓글

comment-user-thumbnail
2023년 4월 4일

통신라이브러리 차이에 대해 검색하다 좋은글 읽고가요. 저도 안드로이드 개발자인데 좋은글 잘 읽었습니다!

답글 달기
comment-user-thumbnail
2023년 9월 5일

덕분에 서버 통신 라이브러리의 역사와 지식에 대해 더욱 잘 알게 되었네요 감사합니다~!!

답글 달기