멈춰!!! 선행 학습이 필요한 글이니 앞선 글들을 읽고 학습해보자!
선행학습을 끝냈으니 드디어 API를 통해 원하는 데이터를 가져와보자잇!
Codable 프로토콜을 채택한 구조체 선언
- 우선 구조체를 정의하기 전에 API를 호출했을때 데이터가 어떻게 날아오는지 먼저 확인하자
- 주소창에 API 호출 주소를 입력하고 표시된 내용을 복사하여 https://jsonlint.com 이곳에서 확인해보자
- 내용을 확인해보니 위와 같은 데이터로 Json 형태가 호출된다
- 여기서 [ 평점, 제목, 줄거리, 포스터 경로 ] 네개만 일단 받아와 보자!
struct Response: Codable {
let page: Int?
let result: [MovieInfo]
enum CodingKeys: String, CodingKey {
case page
case result = "results"
}
}
struct MovieInfo: Codable {
let title: String?
let rating: Double?
let summary: String?
let post: String?
enum CodingKeys: String, CodingKey {
case title
case rating = "vote_average"
case summary = "overview"
case post = "poster_path"
}
}
- Response 구조체를 Page 정보와, 정보를 배열로 담고있는 results를 정의한다
- MovieInfo 구조체에는 [ 영화제목, 평점, 개요, 영화 포스터 ] 의 정보를 담는다
- 구조체의 형태와 CodingKey 가 뭔지 이해하려면 본 글 제일 위에있는 선행학습을 하자! → Codable
Json 을 Model 로 Decode
// 데이터 파싱하기
do {
let decoder = JSONDecoder()
let respons = try decoder.decode(Response.self, from: resultData)
let searchMovie = respons.result
print("영화 제목 : \(searchMovie[0].title ?? "")")
print("영화 평점 : \(searchMovie[0].rating ?? 0)")
print("영화 줄거리 : \(searchMovie[0].summary ?? "")")
print("포스터 경로 : \(searchMovie[0].post ?? "")")
} catch let error {
print(error.localizedDescription)
}
- JSONDecoder 를 통해 파싱을 시도하고, 이때 Error가 발생하면 Catch로 이동 할 것이다
- 파싱이 완료 된후, 첫번째 인덱스의 정보들을 출력해 본다
/*
영화 제목 : 어벤져스 오브 저스티스
영화 평점 : 4.1
영화 줄거리 : 은하계 최강의 빌런 조크스터는 태양을 없애 지구에 새로운 빙하기를 불러일으키고자 한다. 슈퍼히어로 슈퍼배트는 히어로 동료들과 함께 조크스터에 맞서 지구를 지켜내야만 한다. 이제 전 인류의 운명이 걸린 최후의 대결이 시작된다.
포스터 경로 : /yymsCwKPbJIF1xcl2ih8fl7OxAa.jpg
*/
- 정보를 잘 가져온 것을 확인할수 있다!
- print 문에서 nil 병합 연산자 ( \\(searchMovie[0].title ?? "" ) 를 사용한 이유는 해당 데이터가 Optional로 오기 때문에 사용하였다, 실제 iOS 앱에서는 적절한 바인딩을 해야한다!
- nil 병합 연산자는 해당 변수 및 상수가 nil이 아니면 원래 값을 반환하고, nil 일 경우 ?? 뒤에 오는 Default 값을 반환한다
⇒ 포스터 경로의 경우 TMDB의 API 문서를 보면 되는데
https://www.themoviedb.org/t/p/[포스터 사이즈]/[포스터 경로]
형태로 들어가면 img 파일을 볼수 있다
예시 아래 주소를 들어가보자!
https://www.themoviedb.org/t/p/w220_and_h330_face/yymsCwKPbJIF1xcl2ih8fl7OxAa.jpg
전체코드
// 구조체 선언
struct Response: Codable {
let page: Int?
let result: [MovieInfo]
enum CodingKeys: String, CodingKey {
case page
case result = "results"
}
}
struct MovieInfo: Codable {
let title: String?
let rating: Double?
let summary: String?
let post: String?
enum CodingKeys: String, CodingKey {
case title
case rating = "vote_average"
case summary = "overview"
case post = "poster_path"
}
}
let API_KEY = "발급받은 API KEY"
var movieSearchURL = URLComponents(string: "https://api.themoviedb.org/3/search/movie?")
// 쿼리 아이템 정의
let apiQuery = URLQueryItem(name: "api_key", value: API_KEY)
let languageQuery = URLQueryItem(name: "language", value: "ko-KR")
let searchQuery = URLQueryItem(name: "query", value: "어벤져스")
// URLComponents 에 쿼리 아이템 추가
movieSearchURL?.queryItems?.append(apiQuery)
movieSearchURL?.queryItems?.append(languageQuery)
movieSearchURL?.queryItems?.append(searchQuery)
// 옵셔널 바인딩
guard let requestMovieSearchURL = movieSearchURL?.url else { throw NSError() }
// configuration 설정 -> default
let config = URLSessionConfiguration.default
// session 설정
let session = URLSession(configuration: config)
// dataTask 설정
let dataTask = session.dataTask(with: requestMovieSearchURL) { (data, response, error) in
guard error == nil else { return }
guard let statusCode = (response as? HTTPURLResponse)?.statusCode else { return }
let successRange = 200..<300
guard successRange.contains(statusCode) else { return }
guard let resultData = data else { return }
// 데이터 파싱하기
do {
let decoder = JSONDecoder()
let respons = try decoder.decode(Response.self, from: resultData)
let searchMovie = respons.result
print("영화 제목 : \(searchMovie[0].title ?? "")")
print("영화 평점 : \(searchMovie[0].rating ?? 0)")
print("영화 줄거리 : \(searchMovie[0].summary ?? "")")
print("포스터 경로 : \(searchMovie[0].post ?? "")")
} catch let error {
print(error.localizedDescription)
}
}
dataTask.resume()