본문 바로가기

프로그래밍/영화 TMDB API

영화 정보 앱 만들기 - TMDB API 사용, 인기 영화 정보 가져오기 1

반응형

인기 영화 정보 가져오기

 

완성하면 아래 앱이 됩니다.

https://play.google.com/store/apps/details?id=com.enigmah2k.movieinfo

 

영화정보 - Google Play 앱

영화 또는 TV 시리즈 정보를 검색할 수 있습니다. TMDB API를 사용하여 만들었습니다. 한국에 소개되지 않은 컨텐츠를 찾을 수 있습니다.

play.google.com

 

첫 단계로 movie의 popular API를 사용하여 인기 영화 목록을 받아 표시하는 것을 구현해 보겠습니다.

 

TitleBar 색상이나 테마를 변경하고 싶으면 구글의 Material Color 또는 Theme 관련 정보를 찾아서 적용하면 됩니다. 

https://material.io/design/color/the-color-system.html#tools-for-picking-colors

저는 기본 색상을 유지하고 진행하겠습니다.

 

아래와 같이 표시되도록 할 예정입니다.

주로 사용하는 것은 retrofit2 와 recyclerview 기능입니다.

 

1. String 추가

아래와 같이 추가 합니다.

<string name="popular">Popular 인기</string>
<string name="most_popular">가장 인기있는 영화</string>

 

 

2. retrofit2 사용법 확인

retrofit 에 대해 간략이 정리했습니다.

retrofit 은 interface 에 기술된 값을 HTTP API로 전환해 줍니다.

기본적으로 GET, POST, PUT, DELETE 를 지원하며, @GET("접속 endpoint") 형태로 사용합니다.

retrofit 객체 생성시 baseUrl 을 설정하므로, "접속 endpoint" 는 baseUrl 이후 경로로 작성해야 합니다.

반환되는 타입은 Call<반환객체> 형태로 기술해야 합니다.

 

retrofit 객체는 Builder 객체를 사용하여 생성합니다.

이 때, baseUrl 도 설정합니다. 

상세 소스코드는 아래 기술하겠습니다. 

 

 

3. 영화 Class 생성

공통으로 사용할 파일들을 정리하게 위해

\TMDBtest\app\src\main\java\com\tmdb\tmdbtest 폴더 아래 common 폴더를 추가합니다.

 

common 폴더에서 마우스 오른쪽 클릭을 하여 New > Kotlin File/Class 를 선택합니다.

아래와 같이 Class를 선택하고 Movie 를 입력하여 Movie.kt 파일을 생성합니다.

 

소스코드는 아래와 같이 작성했습니다.

package com.tmdb.tmdbtest.common

import com.google.gson.annotations.SerializedName

data class Movie(
    @SerializedName("id") val id : Long,
    @SerializedName("title") val title : String,
    @SerializedName("overview") val overview : String,
    @SerializedName("poster_path") val poster_path: String,
    @SerializedName("backdrop_path") val backdrop_path: String,
    @SerializedName("vote_average") val rating: Float,
    @SerializedName("vote_count") val vcount: Long,
    @SerializedName("release_date") val releaseDate: String,
    @SerializedName("popularity") val prating: Float
) {}

 

변수들은 아래 API 명세에서 results로 들어오는 부분의 값 중 필요한 값만 정의했습니다.

https://developers.themoviedb.org/3/movies/get-popular-movies

get-popular-movies 의 결과인 results 값은 movie의 정보입니다.

@SerializedName 는 응답받은 JSON KEY 값과 다른 이름으로 변수를 사용하고 싶을 때 사용합니다.

 

 

4. 응답을 받는 Class 생성. GetMoviesResponse.kt

개별 영화 정보 Class는 만들었고, 서버 응답 전체를 저장하는 Class를 만들겠습니다.

common 폴더에서 마우스 오른쪽 클릭하여 GetMoviesResponse.kt 파일을 생성합니다.

 

소스코드는 아래와 같이 작성했습니다.

package com.tmdb.tmdbtest.common

import com.google.gson.annotations.SerializedName

data class GetMoviesResponse(
    @SerializedName("page") val page: Int,
    @SerializedName("results") val movies: List<Movie>,
    @SerializedName("totla_pages") val pages: Int,
    @SerializedName("totla_results") val results: Int
) {}

get-popular-movies 의 결과인 Responses 내용 전체를 묶었습니다. 

results 값들을 List로 받습니다.

 

 

5. Api interface 생성

common 폴더에서 마우스 오른쪽 클릭을 하여 New > Kotlin File/Class 를 선택합니다.

아래와 같이 Interface를 선택하고 Api를 입력하여 Api.kt 파일을 생성합니다.

 

소스코드는 아래와 같이 작성했습니다.

package com.tmdb.tmdbtest.common

import retrofit2.Call
import retrofit2.http.GET
import retrofit2.http.Query

interface Api {
    @GET("movie/popular")
    fun getPopularMovies(
        @Query("api_key") apiKey: String = "<< API KEY >>",
        @Query("page") page : Int,
        @Query("language") language : String = "ko,en-US"
    ): Call<GetMoviesResponse>
}

baseUrl 은 "https://api.themoviedb.org/3/" 로 설정해야 합니다. 

@GET("접속 endpoint")에서 "접속 endpoint" 값을 "movie/popular" 로 해야 합니다.

 

아래와 같이 필요한 파라메터 값을 @Query 로 전달합니다.

 

6. 실행 함수 생성

Api에 정의된 함수를 직접 실행하는 Object 를 만들겠습니다.

common 폴더에서 마우스 오른쪽 클릭을 하여 New > Kotlin File/Class 를 선택합니다.

아래와 같이 Object 를 선택하고 MoviesRepository 를 입력하여 MoviesRepository.kt 파일을 생성합니다.

 

 

아래와 같이 초기화를 한다.

    private val api: Api //인터페이스 구현

    init {
        val retrofit = Retrofit.Builder()
            .baseUrl("https://api.themoviedb.org/3/")
            .addConverterFactory(GsonConverterFactory.create())
            .build()

        api = retrofit.create(Api::class.java)
    }

Retrofit 을 Builder객체로 만듭니다.

baseUrl 은 "https://api.themoviedb.org/3/" 로 설정합니다. 

JSON 데이터 객체를 사용하기 위해 GsonConverterFactory 를 사용합니다.

 

아래와 같이 실행 함수를 구현합니다.

    fun getPopularMovies(page: Int = 1) {
        api.getPopularMovies(page = page)
            .enqueue(object : Callback<GetMoviesResponse> {
                override fun onResponse(
                    call: Call<GetMoviesResponse>,
                    response: Response<GetMoviesResponse>
                ) {
                    if (response.isSuccessful) {
                        val responseBody = response.body()

                        if (responseBody != null) {
                            Log.d("Repository", "Movies: ${responseBody.movies}")
                        } else {
                            Log.d("Repository", "Failed to get response")
                        }
                    }
                }

                override fun onFailure(call: Call<GetMoviesResponse>, t: Throwable) {
                    Log.e("Repository", "onFailure", t)
                }
            })
    }

 

enqueue() 함수를 사용하면 백그라운드 쓰레드에서 요청을 수행한 후에 콜백은 현재 쓰레드에서 처리합니다.

Callback interface 의 void onResponse(Call<T> call, Response<T> response); 는 통신 성공시에 실행되고,

void onFailure(Call<T> call, Throwable t); 는 통신 실패시에 실행됩니다.

 

enqueue, Callback, Call, Response 등은 Retrofit2 기능이므로 Retrofit2 을 좀더 깊게 알아보면 도움이 될 것입니다.

 

 

MovieFragment.kt 파일에 호출 함수를 주가합니다.

MoviesRepository.getPopularMovies()

class MovieFragment : Fragment() {

    lateinit var root: View

    override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?,
            savedInstanceState: Bundle? ): View? {
        root = inflater.inflate(R.layout.fragment_movie, container, false)

        MoviesRepository.getPopularMovies()

        return root
    }
}

 

아래와 같이 결과가 로그로 찍힙니다.

com.tmdb.tmdbtest D/Repository: Movies: [Movie(id=528085, title=2067, overview=A lowly utility worker is called to the future by a mysterious radio signal, he must leave his dying wife to embark on a journey that will force him to face his deepest fears in an attempt to change the fabric of reality and save humankind from its greatest environmental crisis yet., poster_path=/7D430eqZj8y3oVkLFfsWXGRcpEG.jpg, backdrop_path=/5UkzNSOK561c2QRy2Zr4AkADzLT.jpg, rating=5.9, vcount=133, releaseDate=2020-10-01, prating=3739.463), Movie(id=741067, title=Welcome to Sudden Death, overview=Jesse Freeman is a former special forces officer and explosives expert now working a regular job as a security guard in a state-of-the-art basketball arena. Trouble erupts when a tech-savvy cadre of terrorists kidnap the team's owner and Jesse's daughter during opening night. Facing a ticking clock and impossible odds, it's up to Jesse to not only save them but also a full house of fans in this highly charged action thriller., poster_path=/elZ6JCzSEvFOq4gNjNeZsnRFsvj.jpg, backdrop_path=/aO5ILS7qnqtFIprbJ40zla0jhpu.jpg, rating=6.6, vcount=112, releaseDate=2020-09-29, prating=2058.684), Movie(id=497582, title=Enola Holmes, overview=While searching for her missing mother, intrepid teen Enola Holmes uses her sleuthing skills to outsmart big brother Sherlock and help a runaway lord., poster_path=/riYInlsq2kf1AWoGm80JQW5dLKp.jpg, backdrop_path=/kMe4TKMDNXTKptQPAdOF0oZHq3V.jpg, rating=7.6, vcount=2108, releaseDate=2020-09-23, prating=1636.807), Movie(id=337401, title=Mulan, overview=When the Emperor of China issues a decree that one man per family must serve in the Imperial Chinese Army to defend the country from Huns, Hua Mulan, the eldest daughter of an honored warrior, steps in to take the place of her ailing father. She is spirited, determined and quick on her feet. Disguised as a man by the name of Hua Jun, she is tested every step of the way and must harness her innermost strength and embrace her true potential., poster_path=/aKx1ARwG55zZ0GpRvU2WrGrCG9o.jpg, backdrop_path=/zzWGRw277MNoCs3zhyG3YmYQsXv.jpg, rating=7.3, vcount=2524, releaseDate=2020-09-04, prating=1168.889), Movie(id=724989, title=Hard Kill, overview=The work of billionaire tech CEO Donovan Chalmers is so valuable that he hires mercenaries to protect it, and a terrorist group kidnaps his daughter just to get it., poster_path=/ugZW8ocsrfgI95pnQ7wrmKDxIe.jpg, backdrop_path=/86L8wqGMDbwURPni2t7FQ0nDjsH.jpg, rating=4.7, vcount=141, releaseDate=2020-08-25, prating=1034.935), Movie(id=694919, title=Money Plane, overview=A professional thief with $40 million in debt and his family's life on the line must commit one final heist - rob a futuristic airborne casino filled with the world's most dangerous criminals., poster_path=/6CoRTJTmijhBLJTUNoVSUNxZMEI.jpg, backdrop_path=/pq0JSpwyT2URytdFG0euztQPAyR.jpg, rating=6.0, vcount=147, releaseDate=2020-09-29, prating=1032.342), Movie(id=721656, title=Happy Halloween Scooby-Doo!, overview=Scooby-Doo and the gang team up with their pals, Bill Nye The Science Guy and Elvira Mistress of the Dark, to solve this mystery of gigantic proportions and save Crystal Cove!, poster_path=/5aL71e0XBgHZ6zdWcWeuEhwD2Gw.jpg, backdrop_path=/5gTQmnGYKxDfmUWJ9GUWqrszRxN.jpg, rating=7.8, vcount=38, releaseDate=2020-10-06, prating=975.728), Movie(id=697064, title=Beckman, overview=A contract killer, becomes the reverend of a LA church, until a cult leader and his minions kidnap his daughter. Blinded by vengeance, he cuts a bloody path across the city. The only thing that can stop him is his newfound faith., poster_path=/z0r3YjyJSLqf6Hz0rbBAnEhNXQ7.jpg, backdrop_path=/7WKIOXJa2JjHygE8Yta3uaCv6GC.jpg, rating=5.4, vcount=6, releaseDate=2020-09-10, prating=902.208), Movie(id=592350, title=My Hero Academia: Heroes Rising, overview=Class 1-A visits Nabu Island where they finally get to do some real hero work. The place is so peaceful that it's more like a vacation … until they're attacked by a villain with an unfathomable Quirk! His power is eerily familiar, and it looks 

 

 

7. lambda 와 higher order functions 적용

Kotlin의 lambda 와 higher order functions을 적용하여 통신 성공/실패 여부에 따라 Callback 함수의 onResponse(), onFailure() 동작을 하는 함수를 구성해 보겠습니다.

 

MoviesRepository.kt 의 getPopularMovies() 에 onSuccess, onError 파라메터를 추가하여 아래와 같이 구성합니다.

    fun getPopularMovies(page: Int = 1,
                         onSuccess: (movies: List<Movie>) -> Unit,
                         onError: () -> Unit ) {
        api.getPopularMovies(page = page)
            .enqueue(object : Callback<GetMoviesResponse> {
                override fun onResponse(
                    call: Call<GetMoviesResponse>,
                    response: Response<GetMoviesResponse>
                ) {
                    if (response.isSuccessful) {
                        val responseBody = response.body()

                        if (responseBody != null) {
                            onSuccess.invoke(responseBody.movies)
                        } else {
                            onError.invoke()
                        }
                    } else {
                        onError.invoke()
                    }
                }

                override fun onFailure(call: Call<GetMoviesResponse>, t: Throwable) {
                    onError.invoke()
                }
            })
    }

invoke() 를 사용하여 getPopularMovies() 호출시 전달하는 파라메터 함수를 실행하도록 합니다.

결과 파라메터로 responseBody.movies 를 넘겨줍니다.

 

실제 Popular 정보를 호출하는 MovieFragment.kt 를 수정하겠습니다. 

 

MoviesRepository.getPopularMovies() 는 파라메터가 추가되어야 하므로 getPopularMovies() 를 따로 만들어 호출하였습니다.

Success 시 호출되는 함수는 onPopularMoviesFetched() 로 만들었고, Fail 시 호출되는 함수는 onError() 로 만들었습니다.

    private fun getPopularMovies() {
        MoviesRepository.getPopularMovies(
            1,
            ::onPopularMoviesFetched,
            ::onError
        )
    }

    private fun onPopularMoviesFetched(movies: List<Movie>) {
        Log.d("MovieFragment", "Movies: $movies")
    }

    private fun onError() {
        Toast.makeText(activity, "error Movies", Toast.LENGTH_SHORT).show()
    }

 

로그가 잘 나오는 것을 확인하였습니다.

 

 

다음으로 recyclerview 를 적용하여 아래와 같이 나타나게 하는 것은 인기 영화 정보 가져오기 2로 작성하겠습니다.

 

 

TMDB API 를 활용한 Android 앱 만들기

https://stockant.tistory.com/530?category=1156604

 

 

반응형