새싹

상황에 따른 초기화 방법 (옵셔널은 빈 값이 아니다!)

Thor_yeom 2023. 8. 16. 17:14

Kakao Video API를 이용해서 검색을 하던중 

 

Alamofire + SwiftyJSON을 이용했던 코드를 

Alamofire + Codable로 리팩토링하는 작업이 있었다.

    func callRequest(type: EndPoint, query: String, page: Int, completionHandler: @escaping (JSON) -> () ) {
        
        // 한글에 대한 인식이 안되기 때문에 한글에 대한 처리를 해줘야함
        let text = query.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
        
        
        let url = type.requestURL + text + "&page=\(page)"

        // 카카오는 .validate(statusCode: 200...500) : 실패 코드를 상세적으로 볼 수 있음
        print("prefetchRowsAt - url",url)
        AF
            .request(url, method: .get, headers: header)
            .responseJSON { response in

            switch response.result {
            case .success(let value):
                let json = JSON(value)
                print("JSON: \(url)")
                completionHandler(json)

            case .failure(let error):
                print(error)
            }
        }
    }

이러한 코드를 Codable를 사용해서 바꾸면 이렇게 바꿀 수 있다.

import Foundation

// MARK: - KakaoVideo
struct KakaoVideo: Codable {
    var documents: [Document]
}

// MARK: - Document
struct Document: Codable {
    let author: String
    let datetime: String
    let playTime: Int
    let thumbnail: String
    let title: String
    let link: String
    
    var contents: String {
        return "\(author) | \(playTime)회\n \(datetime)"
    }

    enum CodingKeys: String, CodingKey {
        case author, datetime
        case playTime = "play_time"
        case thumbnail, title
        case link = "url"
    }
}


func callRequest(type: EndPoint, query: String, page: Int, completionHandler: @escaping (KakaoVideo?) -> () ) {
        
        // 한글에 대한 인식이 안되기 때문에 한글에 대한 처리를 해줘야함
        let text = query.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
        
        
        let url = type.requestURL + text + "&page=\(page)"

        // 카카오는 .validate(statusCode: 200...500) : 실패 코드를 상세적으로 볼 수 있음
        print("prefetchRowsAt - url",url)
        AF
            .request(url, method: .get, headers: header)
            .validate(statusCode: 200...500)
            .responseDecodable(of: KakaoVideo.self) { response in
                print("APIManger: \(response.value)")
                // @escaping 클로저를 사용해서 KakaoVideo? 타입을 넘겨 줄 수 있다.
                completionHandler(response.value)
            }
}

과정만 알면 크게 어렵지 않았다. 

 

VC에서 클로저를 활용해서 적용해보자

클로저로 넘어오는 result가 KakoVideo? "타입이니까 변수를 KakoVideo? 으로 만들고 append 해주면
videoList.documents에 하나씩 쌓이겠지?" 라고 생각하면서 만들었다...

  var videoList: KakaoVideo?
  
  
  func callRequest(query: String, page: Int) {

        KakaoAPIManager.shared.callRequest(type: .video, query: query, page: page) { result in
         
            
            guard let result else { return }
            self.videoList?.documents.append(contentsOf: result.documents)
            self.tableView.reloadData()
        }
    }

 

결과는... nil 이었다...

nil

 

어디서 부터 잘못된걸까? 자료형이 같으면 쓸 수 있을 거 같은데... 

해서 플레이그라운드에 똑같이 만들어봤다.

 

같은 자료형의 옵셔널끼리 계산하고 있지만 컴파일 오류가 발생하는 것을 알 수 있다.

 

nil은 값이 없음을 의미하는 것이기 때문에

같은 자료형 옵셔널값이 오더라도 계산 할 수 없음

 

 

 

 

코드로 다시 돌아와서 그렇다면 어떻게 개선 할 수 있을까?

답은 아주 쉽다.  사용하려고 하는 변수의 자료형을 옵셔널로 처리하지 않고 빈값으로 만들면 된다.

var videoList: KakaoVideo = KakaoVideo(documents: [])

  func callRequest(query: String, page: Int) {

        KakaoAPIManager.shared.callRequest(type: .video, query: query, page: page) { result in
         
            
            guard let result else { return }
            self.videoList.documents.append(contentsOf: result.documents)
            self.tableView.reloadData()
        }
    }

 

이렇게 하면 스크롤을 내려 page + 1을 했을때

 append 시킬 수 있다.