[Android] Retrofit2 라이브러리로 서버와 통신하기

yuuuzzzinzzzang·2021년 7월 3일
2
post-thumbnail

❓ Retrofit2?

  • Retrofit은 Square사에서 만든 라이브러리로 서버와 통신을 하기 위해 HTTP API를 자바나 코틀린의 인터페이스 형태로 변환해 사용할 수 있도록 해준다.
  • 요즘은 안드로이드 개발 시, 통신 부분은 대부분 Retrofit 라이브러리를 사용한다고 한다.
  • AsyncTask나 Volley와 비교했을 때 응답 속도가 훨씬 빠름.

예제를 통해 Retrofit2 라이브러리를 사용해보자 !

👀 GithubAPI로 리포지토리 목록 불러오기

사용자 아이디를 검색하면 해당 사용자의 리포지토리 목록을 보여주는 앱을 만들어 보려고 한다.

◻️ 준비

① Internet 퍼미션 추가

  • Manifest 파일에 Internet permission 추가
  • 서버와의 통신이 인터넷을 거쳐서 이루어지기 때문
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.yuuuzzzin.githubapp">

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

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.GithubApp">

② Retrofit2 라이브러리 추가

  • build.gradle(module 수준)에 Retrofit2 라이브러리 종속성 추가
  • Retrofit Github에서 최신 버전 확인
    // Retrofit2 라이브러리
    def retrofit_version = "2.9.0"
    implementation "com.squareup.retrofit2:retrofit:$retrofit_version"
    implementation "com.squareup.retrofit2:converter-gson:$retrofit_version"
  • 첫 번째 implementation은 Retrofit 라이브러리
  • 두 번째 implementation은 Retrofit에 Converter로 함께 사용되는 Gson 라이브러리
    • 매우 큰 장점 ! gson이 Json을 자바 클래스로 바꿔 주는 역할을 해 따로 변환 객체를 구현하지 않아도 됨.

③ API 문서 확인하기

Github REST API Current version를 보면 Base Urlhttps://api.github.com라는 것을 알 수 있다.

  • BASE URL? 데이터를 요청할 서버의 이름.
  • BASE URL 뒤에 붙는 것에 따라 서버에 어떤 것을 요청하는 API인지 정해진다.

  • 사용자 아이디를 검색하면 리포지토리 목록을 보여주는 기능을 구현할 것이기 때문에 관련 API에 대한 문서를 살펴보았다.
  • URL 앞에 GET이 붙어있으므로 서버에 있는 데이터를 얻을 수 있는 API인 것을 알 수 있다.
  • /users/{username}/repos 부분이 BASE URL 뒤에 붙을 부분

◻️ 본격적으로 Retrofit 사용해보기 !

① Retrofit을 사용하기 위한 API 인터페이스 생성

interface GithubService {
    @GET("/users/{username}/repos?sort=created")
    @Headers("Autorization: token 발급받은 키")
    fun getRepos(@Path("username") username: String): Call<List<GithubRepo>>
}
  • 인터페이스를 생성하고 안에 Request 메소드를 작성한다.
  • GET Request를 요청해야 하므로 @GET 어노테이션을 이용해 서버에 GET 요청을 할 주소를 입력
    • 앞에서 본 BASE URL 뒤에 붙을 주소인 /users/{username}/repos를 넣고, 최근에 생성된 순으로 출력하기 위해 ?sort=created를 붙였다.
  • @Path 어노테이션은 동적으로 바뀔 수 있는 URL의 부분, Request 요청을 보낼 때의 매개변수를 뜻한다.
    • 사용자 이름을 입력 받고(getRepos메소드의 파라미터로 넘어온 값), 그 입력받은 이름을 요청보내야 함
  • Request 메소드가 호출된 후 리턴하는 Call< T >타입
    • Call< T > ? 여러 메소드들을 정의하고 있는 인터페이스
    • T는 성공적으로 응답될 response body type의 클래스-> 안드로이드에서 data class로 사용. 즉, <> 안에는 성공적으로 응답될 body type의 data class 넣어주면 됨.
    • 반환을 비동기식으로 처리
  • 따라서 반환값은 List< GithubRepo >인데, 이것은 리포지토리의 정보를 리스트로 담아 반환한다.
    • GithubRepo는 API를 통해 어떤 데이터가 넘어오는지 Java Object에 정의하는 것

② data class 작성

성공적으로 응답되는 Response의 예시이다.

  • 이러한 데이터들을 서버에서 받아올 수 있는 것을 확인할 수 있었다.
    위의 응답 데이터 형식은 json형식
    -> 서버에서 응답 받는 실데이터는 json 형식, 클라이언트에서 응답 받을 데이터 타입은 data class 객체
  • 따라서, 서버로부터 받은 json 형식의 데이터를 클라이언트에서는 data class 객체로 변환되어야 통신이 가능 !
  • 서버에 데이터를 보낼 때에도 클라이언트에서 data class 객체를 json 형식의 데이터로 변환해서 보내야 서버가 받을 수 있음.
  • json <-> data class 를 왔다 갔다 변환하는 기능은 Retrofit 라이브러리에 아까 추가한 Gson 라이브러리를 추가로 사용하면 가능하다.
data class GithubRepo(
    @SerializedName("name")
    val repoName: String,
    @SerializedName("created_at")
    val date: String,
)
  • SerializedName은 json 형식 데이터로 변환된 이름이라는 것.
    • @SerializedName 어노테이션은 data class 변수 이름을 서버 쪽 json 포맷 데이터의 key 이름과 동일하게 사용하고 싶지 않을 때 작성해주면 이름을 다르게 사용할 수 있게 도와줌
  • 나는 리포지토리의 이름과, 생성된 날짜를 받아 출력해보기로 했당.

③ Retrofit 인터페이스 구현체 생성

  • 앱에서 서버 호출이 필요한 곳마다 인터페이스를 사용해야 하는데, 인터페이스를 여러 번 구현하지 않고, 한 번만 구현해 놓고 필요한 곳에서 사용하려면?
    • 코틀린의 object 사용
    • object는 singleton 이기 때문에 전역 변수처럼 앱의 모든 곳에서 접근 가능
// singleton(메모리를 하나만 씀)
object RetrofitClient {
    // retrofit client 선언

    // BASE_URL을 private const 변수로 저장
    private const val BASE_URL = "https://api.github.com/"

    val retrofit = Retrofit.Builder()
    .baseUrl(BASE_URL)
    .addConverterFactory(GsonConverterFactory.create())
    .build()

    val githubService: GithubService = retrofit.create(GithubService::class.java)
}
  • Retrofit.Builder를 통해 baseUrl을 설정하고, converter를 GSON Converter로 설정해 build()메소드 호출 -> Retrofit 객체 생성
  • request하기 위해서는 client가 필요.
val githubService: GithubService = retrofit.create(GithubService::class.java)

retrofit.create(클라이언트 클래스(인터페이스))를 통해 클라이언트 생성

  • 만든 클라이언트에서 정의된 메소드(여기선 getRepos 메소드)를 통해 Request를 보냄

④ Request 메소드를 호출하고, 서버로부터 응답 받아 처리하기

RetrofitClient.githubService.getRepos(username).enqueue(object : Callback<List<GithubRepo>> {
			// 서버 통신 실패 시의 작업
            override fun onFailure(call: Call<List<GithubRepo>>, t: Throwable) {
                Log.e("실패", t.toString())
            }
            
			// 서버 통신 성공 시의 작업
            // 매개변수 Response에 서버에서 받은 응답 데이터가 들어있음.
            override fun onResponse(call: Call<List<GithubRepo>>, response: Response<List<GithubRepo>>) 
            {
            	// 응답받은 데이터를 가지고 처리할 코드 작성
                val repos:List<GithubRepo>? = response.body()
                repos?.forEach { it ->
                    Log.d("성공", it.toString())
                }
            }
        })
  • 앞의 과정에서 API 인터페이스를 구현했고, 이 구현체는 object를 사용해 singleton으로 구현해놨었다.
  • 따라서 그 인터페이스 안에 구현된 getRepos()라는 Request 메소드를 호출해준다.
  • 그 다음은 Response를 받기 위해 enqueue 메소드를 사용해준다.
    • Response를 받는 방식은 동기execute() / 비동기enqueue(Callback<T> callback)가 있다.
    • 서버와 통신하는 것은 시간이 오래걸리는 작업이다. 따라서 동기방식으로 통신하면 통신이 완료될 때까지 다른 작업이 이루어지지 못하고 앱 상에서 UI가 멈춰보이게 되며 모든 기능이 중단된다.
    • 따라서, 비동기식 통신을 해 다른 작업을 처리하다가 통신 결과를 받으면 다시 작업을 처리할 수 있도록 해주어야 효율적.
  • 서버로부터 Response를 받지 못했을 때 onFailure(), 그 외에 onResponse()를 호출
  • 위의 코드에서는 response의 body()를 로그에 출력해보았다.

    아주 잘 뜬당 !

📸 실행 결과

profile
yuuuzzzin의 개발 블로그

3개의 댓글

comment-user-thumbnail
2021년 8월 17일

유진님 덕분에 막혀있는 부분이 뻥~ 뚫리는 느낌이네요!

1개의 답글
comment-user-thumbnail
2023년 7월 10일

이 멋진 단락을 이 자리에 공유해주신 웹페이지 소유자에게 진심으로 감사드립니다. Rainbow Friends: Chapter 2

답글 달기