본문 바로가기
Mobile : Android/Library

[Android Library] Retrofit2 #1 - 레트로핏 기본 개괄

by 신숭이 2021. 9. 8.

[Android Library] Retrofit2 #1 - 레트로핏 기본 개괄

 

 

 

REST API 통신을 사용하고자 한다면, 레트로핏을 쓰는게 가장 깔끔하고 편하다. 

 

주로 비동기 통신 구현을 위해 그동안 사용해 왔는데 한 번쯤 정리의 필요성을 느껴 기본 개괄과 HTTP 요청 방식 정리로 두 번의 포스트에 걸쳐 정리하고자 한다.

 

HTTP GET 메소드의 구현을 예시로 하겠다. 언어는 Kotlin 으로 하겠다. 

 

 

선행 사항

 

다음 기술들을 선행하면 좋다. 

 

  • REST API (POST, GET, PUT, DELETE) & CRUD (Create, Read, Update, Delete)
  • 비동기 통신

 

 

기본 정보 및 장점

  • Squareup사 에서 만든 라이브러리로 OkHttp의 위에서 구동되는 구현체이다. 
  • AsyncTask 가 아닌 백그라운드 스레드를 실행하여 콜백으로 결과를 제어할 수 있다. (성능 향상) 
  • 콜백으로 제어하는 결과는 Main Thread 에서 UI를 업데이트 할 수 있게한다
  • Annotation 을 통해 가독성이 뛰어나다.
  • 기존 HTTP 통신 구현에 필요했던 반복적인 작업을 라이브러리가 자동화하여 처리한다.

레트로핏의 성능 비교

 

 

Retrofit의 주요 구성

 

보통 아래의 순서대로 구현을 진행한다.

 

  1.  Data Transfer Object (DTO) : JSON 타입 변환에 사용할 객체
  2.  인터페이스 : Annotation 기반, 서버에 CRUD 동작을 요청할 HTTP 메소드 정의 인터페이스
  3.  Retrofit 객체 : 인터페이스에서 정의한 메소드를 호출할 수 있는 인스턴스

Result (DTO), Network (Interface), Retrofit

 

환경 설정

 

 1. App 수준 Gradle의 Dependencies

 

implementation 'com.squareup.retrofit2:retrofit:2.4.0'                 // REST API
implementation 'com.squareup.retrofit2:converter-gson:2.4.0'            // REST API json parser

 

여기서 converter-gson 은 Gson Converter를 사용하기 위함으로, 이 컨버터는 JSON 응답 결과를 DTO로 매핑해준다.

( 같은말 : JSON 응답을 Java나 Kotlin 객체로 바꿔준다)

 

 

 2.  AndroidManifest.xml 

 

<uses-permission android:name="android.permission.INTERNET" />

<application
        중략..
        android:usesCleartextTraffic="true">
        <activity
        ..

 

당연히 인터넷 권한을 허용해야하고, application 에서 android:usesCleartextTraffic="true"도 추가한다.

 

 

 

1. DTO 정의 

 

예를들어 앱이 서버에 본인 인증 문자를 발송하는  GET 요청 을 한다고 가정하자.

요청시에는 다음과 같은 URI가 필요하고    (아래 IP는 내 서버 아이피를 살짝바꾼거라 요청해도 소용없다!)

 

http://54.180.13.2:80/api/sms/verify?phoneNumber=01022115987&hashCode=aaaaaaaaaaa

 

응답은 다음과 같은 JSON 데이터가 온다고 가정하자.

{
    "isSuccess": true,
    "code": 202,
    "message": "본인인증 문자 발송 성공"
}

 

응답 JSON의 구조를 파악했다면 다음과 같이 데이터 클래스를 정의한다. (클래스명 무관)

반드시 타입을 일치시켜야 한다!

 

data class Result(
    val code: Int,
    val isSuccess: Boolean,
    val message: String
)

 

 

2. 인터페이스 정의

 

 

인터페이스는 우리가 HTTP 요청이 필요해 보이는 메소드를 정의해 놓는다. 여기서 Annotation을 활용한다.

 

이 메소드는 서버에 인증문자를 보내라고 요청하는 메소드이다.

 

앞서 아래와 같은 URI 로 요청을 한다고 했다. 여기서 GET 요청의 Path쿼리를 유심히 본다.

 

http://54.180.13.2:80/api/sms/verify?phoneNumber=01022115987&hashCode=aaaaaaaaaaa

 

interface Network {
    @GET("/api/sms/verify")
    fun requestAuthMsg(
        @Query("phoneNumber") phoneNumber:String,
        @Query("hashCode") hashCode:String) : Call<Result>
}

 

URI를 기반으로 인증 메시지를 요청하는 메소드를 정의하였다. 응답 객체 아까 정의한 DTO를 사용한다. Call<DTO>

이 반환 타입은 응답에 대한 콜백 처리에 이용된다.

 

@GET("/api/sms/verify")   :  이 메소드는 GET 요청임을 알리고, 서버내의 경로(path)를 지정한다.

 

이 메소드의 각 파라미터는 URI에 명시된 쿼리를 지정한다. 반드시 서버에서 요구하는 이름과 같게 적는다.

 

@Query("phoneNumber")

@Query("hashCode")

 

 

 

 

3. Retrofit 인스턴스 

 

이제 인터페이스에서 정의한 메소드를 사용할 수 있는 Retrofit 인스턴스를 생성한다. 이 인스턴스를 필요한 곳에서 활용하여 메소드를 호출하면 된다.

(필요한곳 : 액티비티 & 서비스 등의 컴포넌트)

 

액티비티나 서비스에서 인스턴스를 생성해도 된다. 하지만 어차피 똑같은 호출 구조이고 가독성을 높히기 위해 Object 파일로 싱글톤 느낌으로 만들어두는게 깔끔하다.

 

object RetrofitClient {
    private const val BASE_URL:String = "http://54.180.13.2:80"
    private val retrofit:Retrofit.Builder by lazy{
        Retrofit.Builder()
            .baseUrl(BASE_URL)
            .addConverterFactory(GsonConverterFactory.create())
    }

    val apiService: Network by lazy{
        retrofit.build().create(Network::class.java)
    }
}

 

BASE_URL 에 서버 주소를 명시한다. 여기서  / 를 조심해야한다.

 

http://54.180.13.2:80 

 

이렇게 적었으면 HTTP 메소드 Annotation 에서 /api/sms/verify 로 경로를 적는게 맞다.

 

http://54.180.13.2:80/

 

이렇게 적었으면 HTTP 메소드 Annotation 에서 api/sms/verify 로 경로를 적는게 맞다.

 

슬래시가 한번 들어가야하기 때문이다.

많은 사람들이 여기서 처음에 실수해서 맞왜틀을 외치는 사례가 구글에 낭자하다.

 

 

Retrofit.Builer 를 통해 메서드 체인으로 인스턴스의 환경을 설정한다. 베이스 URL 지정과 컨버터를 지정해준다. 

응답 형식이 JSON 이기에 GsonConverterFactory.create() 를 반드시 추가해준다.

 

apiService 객체(이름 무관)가 실제로 컴포넌트에서 사용하게 될 객체이다. 여기서 인스턴스를 생성하는데, 필요한 시점에 생성하도록 지연 초기화하게 했다.

 

 

 

 

4. 메소드 호출

 

 

액티비티가 실행되는 시점에 요청 메소드를 호출한다고 하자. 

 

http://54.180.13.2:80/api/sms/verify?phoneNumber=01022115987&hashCode=aaaaaaaaaaa

 

인자로 필요한 값을 건네준다.

 

이제 콜백만 정의하면 된다. 응답이 오면 Result (DTO) 객체에 들어오고 이를 활용한다.

 

성공적으로 응답이 오면 onResponse, 서버가 꺼져있다던가 여러가지 이유로 실패한다면 onFailure 가 실행된다.

override fun onCreate(savedInstanceState: Bundle?) {
	super.onCreate(savedInstanceState)

	.. 중략

        apiService.requestAuthMsg("01022115987","aaaaaaaaaaa").enqueue(object : Callback<Result>{
            override fun onResponse(call: Call<Result>, response: Response<Result>) {
                expireTime = 180
                mTimerHandler?.postDelayed(mTimerRunnable,1000)
            }
            override fun onFailure(call: Call<Result>, t: Throwable) {
                t.printStackTrace()
            }
        }

 

 

다음 포스트에서는 각 HTTP 요청 방법(GET, POST 등) 및 멀티파트 파일업로드 방법을 작성하겠다.

댓글