Retrofit으로 LOL API를 다뤄보자

김흰돌·2023년 2월 23일
0
post-thumbnail

Retrofit을 이용하여 LOL 서버와 통신하여 내가 많이 플레이한 챔피언들을 받아 RecyclerView에 뿌리는 걸 목표로 한다.

들어가기 전에

HTTP란?

만약 클라이언트에서 서버로 여러 형식의 요청을 보내면 어떨까?

서버가 제대로 요청에 응답하기 위해선 각 요청을 해석하는 방식을 알아야한다.

요청할 때, 응답할 때 형식을 통일할 필요가 생겼고 HTTP 프로토콜을 사용하면 처음 통신하는 서버에 요청을 보낼 때도 큰 고민을 할 필요가 없다.

HTTP 요청 메서드

  • GET 대상 자원을 요청할 때 사용. ex) 사용자 정보 요청

  • POST 클라이언트에서 서버로 어떤 정보를 제출할 때 사용. ex) 사용자 정보 추가

  • PUT 대상 자원을 대체하고자 할 때 사용. ex) 사용자 정보 수정

  • DELETE 대상 자원을 삭제하고자 할 때 사용. ex) 사용자 정보 삭제


Retrofit과 JSON

Retrofit

레트로핏 공식 사이트에서는 레트로핏을 안드로이드와 자바를 위한 타입 안전 HTTP 클라이언트라고 소개한다.

레트로핏은 RESTful API를 이용할 때 필요한 여러 가지 작업을 간편화해준다.
이 라이브러리를 사용하면 API 요청을 보내고, 응답을 받아서 처리하며, JSON 형식의 데이터를 파싱할 수 있다.
또한, 레트로핏은 HTTP 클라이언트 라이브러리인 OkHttp를 내부적으로 사용하므로, OkHttp에서 지원하는 기능들을 레트로핏에서도 사용할 수 있다.

레트로핏은 인터페이스를 사용하여 API를 정의한다.
인터페이스에서는 API 엔드포인트, HTTP 메서드, 요청 본문 등을 정의할 수 있다.
레트로핏은 이러한 인터페이스를 구현한 구현체를 자동으로 생성해준다.
이를 통해 개발자는 API 요청을 보내는 코드를 간결하게 작성할 수 있다.

레트로핏을 사용할 때는 3가지 요소를 반드시 구현해야 한다.

  • HTTP 메서드들을 정의한 인터페이스
  • 레트로핏 클라이언트 객체를 생성하는 레트로핏 클래스
  • JSON 데이터를 담을 데이터 클래스

JSON

JSON은 사람이 읽을 수 있는 텍스트 기반의 데이터 교환 형식이다.
JSON은 텍스트 기반이기 때문에 사람이 읽고 이해할 수 있으며, 가볍고 처리 속도가 빨라서 많은 환경에서 사용하고 있는 방식이다.

{
  	"name": "흰돌",
  	"age": 15,
  	"email": "whitestone@example.com"
  	"hobby": [ "sleep", "eat treat", "walking park" ]
}

모든 데이터는 속성-값 으로 이루어져 있다.
속성은 꼭 문자열로 써주어야 하고, 값은 기본 자료형, 객체, 배열 등이 들어올 수 있다.

레트로핏 응답 메시지는 JSON으로 이루어지기 때문에 이를 사용하려면 JSOM 객체를 적절한 객체 타입으로 변환해주어야 한다. 적절한 객체 타입은 뒤에서 이어서 설명하겠다.


사전 준비

https://developer.riotgames.com/ 에서 API키 발급 받기


DASHBOARD에 들어가면 API키를 발급 받을 수 있다.

내 소환사 ID를 암호화 한다.

SUMMONER의 /lol/summoner/v4/summoners/by-name/{summonerName}를 선택하면


아래와 같이 소환사 ID를 입력할수 있는 텍스트 박스가 있고 함호화 하고 싶은 ID를 입력후 EXECUTE REQUEST로 요청을 실행한 후 RESPOSE BODY의 ID를 보면 암호화 된 소환사 ID를 볼 수 있다

원하는 API를 고른다.


정말 다양한 API들을 제공하지만 위에서 얘기했듯 내가 많이 플레이한 챔피언을 순서대로 반환해주는 API를 선택하였다.

Retrofit 라이브러리 추가

dependencies {

    
    implementation 'com.squareup.retrofit2:retrofit:2.9.0'
    implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
}

레이아웃 구성

리사이클러 뷰를 사용하기 위해 간단히 구성한다.

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".patter.MainActivity">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

item_view.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="100dp"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <ImageView
        android:id="@+id/champ_img"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:background="#FFFFFF"
        android:src="@drawable/icon_lol"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        android:layout_marginStart="20dp"
        />

    <TextView
        android:id="@+id/champ_name"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="championName"
        app:layout_constraintStart_toEndOf="@id/champ_img"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toTopOf="@id/champ_level"
        app:layout_constraintEnd_toEndOf="parent"
        android:layout_marginEnd="20dp"
        android:layout_marginStart="10dp" />

    <TextView
        android:id="@+id/champ_level"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="championLevel"
        app:layout_constraintStart_toEndOf="@id/champ_img"
        app:layout_constraintTop_toBottomOf="@id/champ_name"
        app:layout_constraintBottom_toTopOf="@id/champ_points"
        app:layout_constraintEnd_toEndOf="parent"
        android:layout_marginEnd="20dp"
        android:layout_marginStart="10dp" />

    <TextView
        android:id="@+id/champ_points"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="championPoints"
        app:layout_constraintStart_toEndOf="@id/champ_img"
        app:layout_constraintTop_toBottomOf="@id/champ_level"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:layout_marginEnd="20dp"
        android:layout_marginStart="10dp" />

</androidx.constraintlayout.widget.ConstraintLayout>

ImageView는 추후에 챔피언 초상화를 넣을 수 있으면 넣어보려고 일단 만들었다.


HTTP 메서드를 정의할 인터페이스 작성

interface LOLService {
	// LOL API 페이지에서 골랐던 API 주소를 써주면 된다.
    @GET("champion-mastery/v4/champion-masteries/by-summoner/암호화된소환사ID") 
    // 응답 받을 객체를 Call 타입으로 감싸고 반환 타입을 명시해 준다.
    fun  getInformation(@Query("api_key") api_key: String) : Call<List<LOLResponse.LOLResponseItem>>
    
    @GET("ranked/v1/leaderboards")
    fun getRanking(....)
}

인터페이스 안에는 사용할 API를 정의한다.
다른 API도 요청하고 싶다면 인터페이스 안에 추가로 작성해주면 된다.


레트로핏 클라이언트 객체를 생성하는 레트로핏 클래스

class RetrofitConnection {
    companion object {
        private const val BASE_URL = "https://kr.api.riotgames.com/lol/"
        private var INSTANCE: Retrofit? = null

        fun getInstance(): Retrofit {
            if (INSTANCE == null) {
                INSTANCE = Retrofit.Builder().baseUrl(BASE_URL).addConverterFactory(GsonConverterFactory.create()).build()
            }
            return INSTANCE!!
        }
    }
}

레트로핏 클래스에서는 레트로핏 빌더를 사용하여 레트로핏 객체를 생성해준다.
빌더 클래스에서 API의 베이스 URL과 JSON 객체를 변환해줄 Gson 컨버터를 사용한다.
Gson이란 자바에서 JSON을 파싱하고, 생성하는 데 사용되는 구글에서 개발한 오픈소스이다.


JSON 데이터를 담을 데이터 클래스

class LOLResponse : ArrayList<LOLResponse.LOLResponseItem>() {
    data class LOLResponseItem(
        val championId: Int,
        val championLevel: Int,
        val championPoints: Int,
        val lastPlayTime: Long,
        val championPointsSinceLastLevel: Int,
        val championPointsUntilNextLevel: Int,
        val chestGranted: Boolean,
        val tokensEarned: Int,
        val summonerId: String
    )
}

배열 안에 각 챔피언 별 정보를 반환하기 때문에 위와 같이 작성한다.

레트로핏 이용하여 데이터 가져오기

private fun getAPI() {
	// 레트로핏 객체를 이용해 LOLService 인터페이스 구현체를 가져온다.
    val retrofitAPI = RetrofitConnection.getInstance().create(LOLService::class.java)
    
    /*excute() 함수는 요청을 현재 쓰레드에서 처리하지만,
    enqueue() 함수는 백그라운드 스레드에서 요청을 수행한 후에 콜백은 현재 쓰레드에서 처리함 */
    retrofitAPI.getInformation(
        "여기에 암호화된 키를 넣어주세요!"
    ).enqueue(object : Callback<List<LOLResponse.LOLResponseItem>> {
        override fun onResponse(
            call: Call<List<LOLResponse.LOLResponseItem>>,
            response: Response<List<LOLResponse.LOLResponseItem>>
        ) {
        	// 성공시 리사이클러 뷰에 값을 전달한다.
            if (response.isSuccessful) {
            	Toast.makeText(this@MainActivity, "데이터 가져오기 성공!", Toast.LENGTH_SHORT).show()
            	response.body()?.let { setUI(it as ArrayList<LOLResponseItem>) 
            } else {
            	Toast.makeText(this@MainActivity, "데이터 가져오기 실패...", Toast.LENGTH_SHORT).show()
            }
        }

        override fun onFailure(call: Call<List<LOLResponse.LOLResponseItem>>, t: Throwable) {
            Log.e("가져오기 실패", "실패?")
            t.printStackTrace()
        }
    })
}

구현된 LOL Service 인터페이스 객체를 이용하여 Call 객체를 만든 후 enqueue() 함수를 실행하여 서버에 API 요청을 보낸다.

함수의 반환값은 Call<List<LOLResponse.LOLResponseItem>>이다.

레트로핏에서 요청을 처리하는 Call 객체는 HTTP 요청을 보내는 두 가지 방식을 제공한다.

  • execute(): 동기적으로 요청을 보내고 응답을 받는다. 함수가 실행되는 스레드에서 실행되므로 만약 메인 스레드에서 해당 함수를 실행할 경우 응답이 오기까지는 UI가 블로킹 되는 상황이 발생하므로 추천되는 방식은 아님

  • enqueue(retrofit2.Callback): 비동기적으로 백그라운드 스레드에서 요청을 보내고 응답이 오면 등록한 콜백 함수를 실행시킨다. 메인 스레드에서 실행하더라도 백그라운드 스레드에서 요청이 처리되기 때문에 UI를 블로킹하지 않는다.

여기까지 일부 Retrofit 관련 코드에 관한 설명이고 전체 코드를 보려면 여기를 참고!

실행화면

위에서 말한 것처럼 내가 많이 플레이한 챔피언 순으로 값을 받아오는 데 참 게임 많이 하기도 했다.

0개의 댓글