Archive/패캠(올인원)

😼 ch15 검색을 이용해 서버에서 데이터를 받아와 파싱까지!

lgvv 2021. 6. 29. 11:28

✅ 이번시간에는 서버에서 API를 받아와서 파싱까지 하는 것을 알아보자!

 

✅ 이번시간에 알아볼 코드는 아래와 같은 코드이다.

class SearchAPI {
    static func search(_ term: String, completion: @escaping ([Movie]) -> Void) { // completion 클로저를 사용하기 위한 방법
        // 이스케이핑이 있으면 함수가 종료되고 나서 실행
        
        let session = URLSession(configuration: .default) // 1. 세션 만들기
        
        var urlComponents = URLComponents(string: "https://itunes.apple.com/search?")!
        let mediaQuery = URLQueryItem(name: "media", value: "movie")
        let entityQuery = URLQueryItem(name: "entity", value: "movie")
        let termQuery = URLQueryItem(name: "term", value: term)
        urlComponents.queryItems?.append(mediaQuery)
        urlComponents.queryItems?.append(entityQuery)
        urlComponents.queryItems?.append(termQuery)
        
        let requestURL = urlComponents.url! // 2. url 구성
        
        let dataTask = session.dataTask(with: requestURL) { data, response, error in
            let successRange = 200..<300
            guard error == nil,
                let statusCode = (response as? HTTPURLResponse)?.statusCode,
                successRange.contains(statusCode) else {
                // 에러가 없고, 상태코드에 응답에 대한 HTTPURLResponse에서 찾아 할당하고, 상태코드가 범위 안에 포함된다면 스무스하게 넘어가고
                // 그렇지 않으면 completion을 빈 배열을 준다
                    completion([]) // completion부분에 아무것도 정보를 담지 않는다.
                    return
            }
            
            guard let resultData = data else {
                // 위의 error이 없는걸 확인했고, dataTask의 data를 결과테이터 변수에 대입하는데, 이 작업이 실패하면 completion에 빈배열 주고 리턴
                completion([])
                return
            }
            
            let movies = SearchAPI.parseMovies(resultData) //
            completion(movies)
        }
        dataTask.resume() // 3. resume으로 데이터 받아오기
    }
    
    static func parseMovies(_ data: Data) -> [Movie] { // 타입메소드로 선언
        let decoder = JSONDecoder() // 1. 디코더 선언하고

        do {
            let response = try decoder.decode(Response.self, from: data) // data로부터 Response타입으로 디코딩!
            let movies = response.movies
            return movies
        } catch let error {
            print("--> parsing error: \(error.localizedDescription)")
            return []
        }
    }
}

struct Response: Codable {
    let resultCount: Int
    let movies: [Movie]
    
    enum CodingKeys: String, CodingKey {
        case resultCount
        case movies = "results"
    }
}

struct Movie: Codable {
    let title: String
    let director: String
    let thumbnailPath: String
    let previewURL: String
    
    enum CodingKeys: String, CodingKey {
        case title = "trackName"
        case director = "artistName"
        case thumbnailPath = "artworkUrl100"
        case previewURL = "previewUrl"
    }
}

 

우선 search 쪽에서 URL 세션을 통해 구성하는 모습을 볼 수 있어.

그 다음에 dataTask 쪽인데 볼까?

우선 successRange는 HTTP 응답코드에서 200번대가 성공적인 응답이라서 저렇게 범위를 정한거야.

그 다음에는 에러가 없고, 응답코드가 성공적인 번호에 속하면 guard 문을 지나가야겠지?

그 다음에 데이터를 변수에 대입해서 그 받아온 데이터를 파싱하는 함수로 보내주면 돼.

그 다음은 parseMovie 쪽인데 스태틱 메소드라 인스턴스(객체)를 생성하지 않아도 돼.

❗️ 그리고 제일 중요한게 resume 꼭 작성해줘야 코드가 작동하니까.. 잊지말기!

 

✅ parseMovie 쪽이 이제 진짜 parsing 부분이야.

1. 반환할 데이터 타입을 명시하고( 함수 선언부에 )

2. JSONDecoder를 대입하고 ( JSON 파일 디코딩 할 예정이라서 )

3. decoder.decode를 통해 파싱을 어떻게 할건지 간략하게 정해주는데 ( try를 사용하는건 이유는 이 작업이 실패할 수도 있기 때문에 ) 

--> 실패하면 catch로 이동

decode의 파라미터에 대해서 알아보면 

 첫번째 파라미터는 어떤 형식으로 바꿀건가

 두번째 파라미터(from)는 바꿀 데이터는 어떤건지

즉 from A to B (A에서부터 B로) 의 의미로 생각해보면 이해가 쉽다.

4. 우리가 필요한건 movies인데, 응답받아온거를 다 쓰는게 아니라, 그 중에서 movies에 대한 정보만 필요함으로 그에 맞게끔 변환해준 모습이야. 

📡 내가 예시에서 사용한 데이터에서 JSON 트리 구성된걸 참고하여 사용하였다.

5. 우리가 가져온 데이터를 movies의 형태로 리턴!

var movies: [Movie] = [] // 다른 viewController에서 선언되어 있는 형태

위에 이런 식으로 선언되어 있다!

 

✅ CodingKey 저 부분은 Codable을 사용할 때, 서버에서 내려준 key와 우리가 선언한 변수명이 같아야만 에러가 나지 않는데, 우리가 원하는 변수명으로 바꿔줄 때 저 코드를 사용한다.

❗️ 코딩키 쪽에 s 잘못 붙여서 에러 많이 내는데,,, 이거 스스로 주의하자...

 

 

(추가)

JSONSerializaition을 쓰는 경우가 있는데, 여기 예제에서는 JSON에서 하나의 key:value만 필요하니까 이게 필요 없었지만 만약 JSON을 딕셔너리 형태로 변환해서 여러개의 데이터를 배열식으로 관리해야하면 이 코드가 꼭 들어가야한다. 

이에 대한 부분이 포스팅을 참고하기

https://rldd.tistory.com/126