brunch

안드로이드 Retrofit

with YouTube data API

by 서준수

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() 메서드가 호출된다. 이때 응답받은 데이터를 가지고 있는 두 번째 인자를 하나씩 풀어서 적절히 사용하면 된다.


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

keyword
매거진의 이전글안드로이드 RecyclerView