brunch

You can make anything
by writing

C.S.Lewis

by 서준수 Feb 12. 2021

안드로이드 Retrofit

with  YouTube data API

Retrofit은 Android에서 REST API를 쉽게 사용할 수 있는 클라이언트 라이브러리이다. 통신을 할 때 JSON 형태에 맞는 모델 클래스를 만들어 사용하기 때문에 JSON을 다루기 편리하다. 예를 들면 서버에서 응답받은 JSON을 별도의 파싱 없이 사용할 수 있다.


Retrofit을 사용해 보려면 REST API를 요청할 서버가 필요하다. 여기서는 YouTube API를 호출하여 응답을 받고 해당 데이터를 화면에 표시하는 예제를 통해 Retrofit2를 사용해 볼 것이다.


[예제 코드 파일]

GitHub


YouTube API 라이브러리 다운

먼저 YouTube API를 사용하기 위한 라이브러리를 아래 사이트에서 다운로드한다.

https://developers.google.com/youtube/android/player/downloads

압축파일을 풀면 libs 폴더 내에 YouTubeAndroidPlayerApi.jar가 있다. 해당 파일을 프로젝트의 libs 폴더에 넣는다.


build.gradle(:app)에 다음과 같이 추가한 후 sync 한다.


dependencies {
...
implementation files('libs/YouTubeAndroidPlayerApi.jar')
}


YouTube Data API 키 발급받기

YouTube API를 사용하기 위해서는 키가 필요한데 발급받는 과정은 이곳을 참고한다.


API 사용법은 다음에서 확인할 수 있다.

https://developers.google.com/youtube/v3/getting-started?hl=ko


여기서는 다음 URL을 사용해서 필요한 정보만 받아올 것이다. YOUR_API_KEY에 위에서 발급받은 키를 넣으면 된다.


https://www.googleapis.com/youtube/v3/videos?id=7lCDEYXw3mM&key=YOUR_API_KEY&fields=items(id,snippet(publishedAt,title,thumbnails),statistics(viewCount))&part=snippet,statistics


브라우저로 해당 URL에 접속하면 다음과 같은 JSON 타입 데이터가 반환되는 것을 확인할 수 있다.

사용할 데이터는 준비되었으니 이제 Retrofit을 사용하기 위해 다음과 같이 진행한다.


1) 권한 추가

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


2) Retrofit 라이브러리 추가

dependencies {
...
implementation 'com.squareup.retrofit2:retrofit:2.8.0'
implementation 'com.squareup.retrofit2:converter-gson:2.6.0'
}


3) request/response body를 관리할 모델 클래스 생성

모델 클래스 생성 시 JSON의 키 이름/타입이 프로퍼티 이름/타입과 동일하게 매칭되어야 한다. (프로퍼티명을 다르게 사용하려면 @SerializedName 애너테이션 사용한다. 예시: @SerializedName("id") val videoId: String)

(YouTube API 응답 데이터 타입 확인 : https://developers.google.com/youtube/v3/docs/videos?hl=ko)

items를 담기 위한 데이터 클래스는 다음과 같다.

data class Video(val items: List<VideoMeta>)

VideoMeta는 id, snippet, statistics를 담고 있는 클래스이다.

data class VideoMeta(
    val id: String,
    val snippet: VideoSnippet,
    val statistics: VideoStatistics
)

그럼 VideoSnippet은 어떤 구성일까? String 타입의 publishedAt, title을 가지고 thumbnails라는 프로퍼티를 default, medium, high를 가지는 클래스 타입으로 가질 것이다.

class VideoSnippet(
    val publishedAt: String, val title: String, val thumbnails: VideoThumbnail
)

thumbnails 정보를 담을 클래스는 VideoThumbnail로 다음과 같이 정의한다.

class VideoThumbnail(val default:ThumbnailURL, val medium:ThumbnailURL, val high:ThumbnailURL)

ThumbnailURL 클래스는 다음과 같다.

class ThumbnailURL(val url: String, val width: UInt, val height: UInt)

statistics의 커스텀 타입인 VideoStatistics 클래스는 다음과 같이 간단하다.

class VideoStatistics(val viewCount: Long)


4) RetrofitService Interface 정의

GET https://www.googleapis.com/youtube/v3/videos

interface RetrofitYouTubeService {
    @GET("/youtube/v3/videos")
    fun requestVideoInformation(
        @Query("id") videoId: String,
        @Query("key") developerKey: String = "YOUR_API_KEY",
        @Query("fields") fields: String = "items(id,snippet(publishedAt,title,thumbnails),statistics(viewCount))",
        @Query("part") part: String = "snippet,statistics"
    ): Call<Video>
}


5) Retrofit 객체 생성 클래스 정의

object RetrofitYouTubeData {
    private var instance: Retrofit? = null
    private const val BASE_URL = "https://www.googleapis.com"

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


6) MainActivity 구현

class MainActivity : AppCompatActivity() {
    var mDataHandler: DataHandler? = null
    private lateinit var retrofit: Retrofit
    private lateinit var youtubeService: RetrofitYouTubeService
    lateinit var bmp: Bitmap
    lateinit var videoTitle: String
    lateinit var uploadDate: String
    lateinit var previewImageView: ImageView
    lateinit var titleTextView: TextView
    lateinit var dateTextView: TextView


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        mDataHandler = DataHandler()
        retrofit = RetrofitYouTubeData.getInstance()
        youtubeService = retrofit.create(RetrofitYouTubeService::class.java)
        previewImageView = findViewById<ImageView>(R.id.previewIv)
        titleTextView = findViewById<TextView>(R.id.titleTv)
        dateTextView = findViewById<TextView>(R.id.dateTv)
        loadData()
    }


    fun loadData() {
        //https://www.youtube.com/watch?v=dyRsYk0LyA8
        youtubeService.requestVideoInformation(
            "dyRsYk0LyA8"
        ).enqueue(object : Callback<Video> {
            override fun onFailure(call: Call<Video>, t: Throwable) {
            }

            override fun onResponse(call: Call<Video>, response: Response<Video>) {
                if (response.isSuccessful) {
                    val result = response.body()
                    val localDate: DateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'")
                    val date: Date = localDate.parse(result?.items?.get(0)?.snippet?.publishedAt)
                    val uploadDateFormat = SimpleDateFormat("yyyy-MM-dd").format(date)
                    videoTitle = result?.items?.get(0)?.snippet?.title!!
                    uploadDate = uploadDateFormat.toString()

                    Thread(Runnable {
                        while (true) {
                            if (result?.items?.get(0)?.snippet?.thumbnails?.high?.url != null) {
                                bmp = BitmapFactory.decodeStream(
                                    URL(result?.items?.get(0)?.snippet?.thumbnails?.high?.url).openConnection()
                                        .getInputStream()
                                )
                            }

                            if (bmp != null) {
                                break
                            }
                        }
                        mDataHandler?.sendEmptyMessage(1)
                    }).start()
                }
            }
        })
    }

    inner class DataHandler : Handler() {
        override fun handleMessage(msg: Message) {
            previewImageView.setImageBitmap(bmp)
            titleTextView.text = videoTitle
            dateTextView.text = uploadDate
        }
    }
}

RetrofitService Interface를 정의할 때 첫 번째 인자 videoId에 대한 값을 할당하지 않았기 때문에 다음과 같이 값을 넘겨준다. (이 값은 유튜브 영상의 주소 https://www.youtube.com/watch?v=dyRsYk0LyA8 에서 마지막 v= 뒤에 있는 값이다.)

youtubeService.requestVideoInformation("dyRsYk0LyA8")

그 후 enqueue()를 호출하는데 이 메서드는 비동기로 request를 보낸 후 response 콜백을 받는다. 성공적으로 응답을 받으면 onResponse() 메서드가 호출된다. 이때 응답받은 데이터를 가지고 있는 두 번째 인자를 하나씩 풀어서 적절히 사용하면 된다.


여기서는 프리뷰 이미지와 영상 제목, 업로드 날짜를 가져와서 아래와 같이 보여준다.

브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari