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

Joyce Hong
14 min readNov 20, 2019

Retrofit은

안드로이드 프로그래밍 할 때

API 서버에 Request를 보낸다?

하면 꼭 꼭 필요한 아이입니다.

전혀 어려운 아이는 아닌데

처음 시작하기에 약간 어려울 수도 있을 것 같아

이렇게 정리합니다.

2편은 Retrofit 과 RxJava의 만남 입니다

Retrofit Dependency 추가 및 Internet Permission 추가

// <AndroidManifest.xml> 파일에 아래 코드 추가
<uses-permission android:name="android.permission.INTERNET"/>
// <build.gradle (Module:app)> 파일에 해당 Dependency 추가
implementation 'com.squareup.retrofit2:converter-gson:2.6.2' //알아서 최신 버전으로..ㅎㅎ
implementation 'com.squareup.retrofit2:retrofit:2.6.0'

하셨죠?

그럼 일단 준비해야할 것이 있습니다.

2. Retrofit Client 만들기

object RetrofitClient {
private var instance : Retrofit? = null
private val gson = GsonBuilder().setLenient().create()

fun getInstnace() : Retrofit {
if(instance == null){
instance = Retrofit.Builder()
.baseUrl("https://wakatime.com/api/v1/")
.addConverterFactory(GsonConverterFactory.create())
.build()
}
return instance!!
}
}

<설명>

baseUrl : 말 그대로 주소를 넣어주면 됩니다.

만약

https://random.api.server.com/api/users

https://random.api.server.com/api/address

https://random.api.server.com/api/stat

이런 식으로 post나 get을 계속 해와야 한다면

baseUrl은

https://random.api.server.com/api/ 가 되겠죠

이해 되셨나요? 바뀌지 않는 부분을 baseurl 로 넣습니다.

addConverterFactory : Json형식의 파일을 나중에 만들 POJO Class 형식으로

자동으로 변환하여 줍니다. Pojo class는 아직 모르시더라도 괜찮아요

왜냐하면

지금 할 거 거든요

3. POJO class 만들기

일단은 request를 보내면 서버에서 JSON response를 줄겁니다.

JSON 은 그대로 java에서 쓸 수 없으므로,

껍데기 클래스를 만들어야 하는데요.

예를 들어 JSON 형식이

{

“name” : “joyce”

“age” : 21

}

이라면 POJO 클래스는 Java로 하면

//JAVA
public class Person {
private string name;
private int age;
public String getName() {return name;}
public void setName(String name) {this.name = name;}
public int getAge() { return age;}
public void setAge(int age) {this.age = age;}
}

이렇게 됩니다.

근데 사실상 JSON response가 key 값이 저렇게 두개 밖에 없는 경우는 드물죠.

json 결과 값을 들고

http://pojo.sodhanalibrary.com/

여기로 가면 자동으로 바꿔 줍니다.

그럼 POJO class 완성!

4. API interface 만들기

자 그럼 기본적인 준비는 끝났으니

중요하다고 할 수 있는

API interface를 만들어 보겠습니다.

텍스트 추가

//kotlin 
interface IWakaServer {
@GET("users/current/durations")
fun getCodingTime(
@Query("date") date : String,
@Query("api_key") string : String
): Call<RawResponseData>
@POST("signup")
@FormUrlEncoded
fun signup(@Field("email") email : String,
@Field("plain_password") password : String,
@Field("name") name : String
): Call<DataModel.SignUpResponse>
@PUT("user/basic")
fun uploadBasicInfo(@Query("email") email : String,
@Query("age") age : Int,
@Query("gender") gender : Int,
@Query("one_line") one_line : String?
): Call<DataModel.PutResponse>
}

POST,GET,DELETE,UPDATE중에

예제는 GET 을 통해

서버에서 정보를 가져옵니다.

쿼리 값으로는 내가 알고 싶은 날짜와, 나의 api_key를 넣어주었습니다.

Return 값으로는 Call<RawResponseData> 입니다.

PUT, POST 역시 마찬가지이지만

@Query 가 아닌 @Field인 점이 주목할 만한 부분입니다.

5. Activity에서 Request 보내기

해야할 것을

간단하게 설명을 해보면

1) 2번에서 만든 client의 instance를 불러온다.

2) 4번에서 만든 API interface를 Retrofit Client 를 이용해 구현한다.

3) 구현된 API 로 request를 날리고 response를 받는다.

코드는 꽤나 간단합니다.

Kotlin으로 작성 되었습니다.

lateinit var retrofit : Retrofit
lateinit var myAPI : IWakaServer
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val TAG = MainActivity::class.java.simpleName
Log.d(TAG,"on create!")
setContentView(R.layout.activity_main)
//retrofit setting retrofit = RetrofitClient.getInstnace() // 2에서 만든 Retrofit client의 instance를 불러옵니다.
myAPI = retrofit.create(IWakaServer :: class.java) // 여기서 retrofit이 우리의 interface를 구현해주고
//우리는 이제 그것을 사용할 수 있습니다.
//Runnable로 감싸주는 이유는!
// Android 에서 MainThread 에서 네트워킹 관련 일을 못해서
// 새로운 스레드에서 해주어야 합니다. 마지막에 .run() 잊지 마세요
Runnable { myAPI.getCodingTime("2019-11-09","MY_API_KEY").enqueue(object : Callback<RawResponseData>{

//이때 onFaliure는 Cal을 서버쪽으로 아예 보내지 못한 경우입니다.
override fun onFailure(call: Call<RawResponseData>, t: Throwable) {
Log.d(TAG,t.message)
}


//만약 보낸 것이 성공했을 경우는 resonse를 가지고 들어옵니다.
//그리고 call을 때릴 때 RawResponseData로 갔으니까 Reponse도 그 타입을 가지고 옵니다.
override fun onResponse(call: Call<RawResponseData>, response: Response<RawResponseData>) {
Log.d(TAG,"response : ${response.body()!!.start}") // 정상출력이 되야 합니다.

//만약 정상 출력이 되지 않으면 문제가 있는 겁니다.
//이때는 Call은 제대로 보냈으나 서버에서 이거뭐냐? 하고 reponse를 보낸 경우 입니다.
Log.d(TAG,"response : ${response.errorBody()}")
Log.d(TAG,"response : ${response.message()}")
Log.d(TAG,"response : ${response.code()}") //이게 가장 에러를 알아보기 쉬운 곳 입니다.
Log.d(TAG,"response : ${response.raw().request().url().url()}") //무슨 url로 api call 을 보냈는지
//확인 할 수 있습니다.
}
})
}.run() //잊지 마세요!

텍스트 추가

이게

Retrofit2 를 안드로이드에서

쓰는 방법입니다 .

어려우셨나요? 부디 기본적인 사용법을 익히는데

도움이 되었으면 좋겠습니다

Dive deeper, Retrofit2

이제 제가 Retrofit을 오래 사용하면서

가져왔던 궁금증과 그 답에 대해서 이야기 해볼 것인데요,

혹시 같은 궁금증이 있으시다면

보시고

아니면 넘기셔도 좋습니다.

일단 제가 모호했던 부분 중

첫번째.

시작합니다.

의문1.

어떻게 API interface를 구현해서 request를 날린 다는 것일까?

하는 부분입니다.

아니 대체 무슨 일이 안에서 일어나길래

Network request를 보낼 수 있는 것일까?

왜 interface를 작성해야하는 것일까?

이에 대한 답은

myAPI = retrofit.create(IWakaServer :: class.java)

여기에서 찾을 수 있었습니다.

저 .create 메소드를 클릭해보면

메소드 설명이 길게 나오는데

“ Create an implementation of the API endpoints defined by the {@code service} interface.’

번역 : 인터페이스에 정의된 API 엔드포인트들의 구현체를 만든다.

저 문장이 키포인트 입니다.

사실 저는 클라이언트 단만 주로 하지

서버쪽은 잘 몰라서 일단

API endpoint가 정확히 뭔지 잘 감이 안왔습니다.

API endpoint란 무엇일까?

여러 자료를 모아보았습니다.

1.자료 수집

<자료1>

What is an API Endpoint?

Simply put, an endpoint is one end of a communication channel. When an API interacts with another system, the touchpoints of this communication are considered endpoints. For APIs, an endpoint can include a URL of a server or service. Each endpoint is the location from which APIs can access the resources they need to carry out their function.

APIs work using ‘requests’ and ‘responses.’ When an API requests information from a web application or web server, it will receive a response. The place that APIs send requests and where the resource lives, is called an endpoint.

출처: https://smartbear.com/learn/performance-monitoring/api-endpoints/

요약

- 커뮤니케이션을 가능하게끔 하는 “터치포인트” 들이 바로 엔드포인트 이다.

- API에서는 엔드포인트는 서버나 서비스의 URL을 포함할 수 있다.

- 엔드포인트는 API 가 필요한 자원을 받아올 수 있는 위치이다.

- API 가 request를 보내고 resource가 있는 곳. 바로 그곳이 엔드포인트이다.

<자료 2>

엔드포인트는 이런 것들이다.

/this-is-an-endpoint

/another/endpoint

/some/other/endpoint

/login

/accounts

/cart/items

만약 도메인을 넣으면

https://example.com/this-is-an-endpoint

https://example.com/another/endpoint

https://example.com/some/other/endpoint

https://example.com/login

https://example.com/accounts

https://example.com/cart/items

또한 엔드포인트는 서로 다른 HTTP 메소드를 사용해도 달라질 수가 있다.

아래 두개는 서로다른 엔드 포인트다.

GET /item/{id}

PUT /item/{id}

2. 결론

일단은 API 를 왜 사용하느냐부터 생각을 해야하는 문제인 것 같다.

API 는 서로 다른 두 시스템 간에

편리한 소통을 하기 위해 만들어졌다.

근데 서로 사용하는 언어도 규격도 다르니까

같은 규격을 사용해야한다.

즉 냉장고, 전자레인지, 전기 밥솥

다 다른 전자제품이지만

전기라는 resource를 사용하기 위해

220v짜리 코드를 꼽는 것과 같다.

그래서 우리는

Http 프로토콜을 사용하여

GET,POST,DELETE,UPDATE 기능을 실행한다.

이 때 Retrofit이 하는 일은

우리가 Java(혹은 kotlin)언어로

우리가 접근하고자 하는 Endpoint에 대한 상세 규격을

Interface로 작성해 놓으면

.create 메소드를 이용하여

API 의 그 Endpoint로 도달할 수 있게끔

구현체를 만들어주고, response 역시 handling 해준다.

의문2.

그래.. 인터페이스를 구현해서

그 객체로 메소드를 쓰는거지..

잠깐만 인터페이스를 객체화 해서 쓴다고???

일단 자바에서는

인터페이스는 메소드가 implement 해서 쓰는 경우밖에 없다.

근데 보면

MyRetrofitInterface myapi = retrofit.create(MyRetrofitInterface.class)

myapi.getAddress() 이런식으로

인터페이스 자체를 객체화 해서 사용한다.

자바에서는 이게 원칙적으로 되지 않는다.

그럼 어떻게 하는걸까..?

답은

https://proandroiddev.com/how-does-retrofit-work-6ecad1bb683b

제발 위에 글 읽어보세요

진짜 좋아용

Retrofit 을 이해하는 데 조금 더 도움이 될 것입니당..

요약 하면

동적으로 런타임에 객체를 만드는 것입니다.

그게 바로 프록시!!

When you have a set of classes that do the same thing, and the only thing that is different can be expressed as a method call, using a proxy of an interface can be an option.

번역하면 만약 똑같은 일을 하는 클래스가 여러개 있고, 메소드로만 하는 일을 다 구분 지을 수 있다면

인터페이스의 프록시를 만드는 것이 하나의 옵션일 수 있다.

즉 API 의 엔드포인트와 어떤 파라미터를 넣느냐와는

무관하게

HTTP Request는 같은 방식으로 만들어진다.

그러므로 프록시를 사용하는것!!

다른 의문점들은 2편에 갖고 돌아오겠습니다…

글이 도움이 되셨다면

👏 부탁드립니다

댓글 환영합니당

어우 잠와…

--

--

Joyce Hong

Blockchain, Android Developer | Share the knowledge 🌏 | Defi will rule the world one day