위시리스트 화면을 제작해야한다. 대충 UI 가이드는 주어졌으며 필수 사항은 다음과 같다.
여태까지 네트워크 연결은 Alamofire, 사진 불러올 때는 KingFisher 등의 외부 라이브러리만 써서 xcode에서 제공하는 것만으로 네트워크 연결이랑 사진 불러오기를 해본적이 없다...ㅎㅎ😅 그래서 이번에 URLSession을 사용해서 네트워크 연결을 해보고 불러온 URL로 사진도 불러와 보았다.(사실 열심히 구글링 했다!)
먼저 URLSession 인스턴스를 생성하고
let session = URLSession.shared
url 상수에 URL 정보를 바인딩한다. 그리고 session의 dataTask 메서드로 URL 요청을 생성한다. 클로저는 요청이 완료되면 실행되며 여기서는 data, response, error를 받아와서 처리한다.
if let url = URL(string: "https://dummyjson.com/products/\(productID)"){
let task = session.dataTask(with: url) { (data, response, error) in
if let error = error {
print("Error: \(error)")
} else if let data = data {
print(data)
}
}
task.resume()
}
}
이게 정상 실행되서 네트워크 연결이 되면 data에 응답이 들어온다. 그런데 이 응답은 api에 따라 다르겠지만 저 api는 JSON을 반환한다. 그래서 이 JSON을 미리 만들어준 RemoteProduct 구조체 객체로 디코딩 하기 위해 다음과 같은 코드를 적어줘야 한다.
do {
let product = try JSONDecoder().decode(RemoteProduct.self, from: data)
} catch {
print("디코딩 에러: \(error)")
}
요러면 네트워크 연결 완료!!
사진도 같은 맥락으로 불러오면 된다!
if let imageURL = URL(string: product.thumbnail) {
URLSession.shared.dataTask(with: imageURL){ (imageData, _, _) in
if let imageData = imageData {
// 이미지 데이터를 UIImage로 변환하여 UIImageView에 설정
DispatchQueue.main.async {
self.imageView.image = UIImage(data: imageData)
}
}
}.resume()
}
위시리스트의 데이터들은 코어데이터로 저장해야한다. 코어데이터를 추가하는 방법은 저번에 정리했었기 때문에 뚝딱 만들어주었다.

그런데 데이터를 불러오는 과정에서 에러가 났었다!!

검색해보니 iOS 앱은 기본적으로 메인 스레드에서 UI 업데이트 및 상호 작용이 이루어져야 한다고 한다! 그런데 지금 이 do-catch 구문은 fetchData() 메서드의 일부분으로 이 메서드는 백그라운드 스레드에서 비동기적으로 실행된다. 그래서 UI를 직접 업데이트 하고 있는 코드들은 메인스레드로 이동시켜주어야 한다. 이를 위해 DispatchQueue.main.async를 사용하여 메인 스레드에서 UI 업데이트를 수행하도록 변경하여 문제를 해결할 수 있었다.

- DispatchQueue.main.async : DispatchQueue에서 작업을 비동기적으로 실행. 주로 UI 업데이트와 같은 비동기적인 작업을 처리할 때 사용
- DispatchQueue.main.sync : DispatchQueue에서 작업을 동기적으로 실행. 주로 백그라운드 스레드에서 메인 스레드로 작업을 전환할 때 사용
어째저째 흘러가다가 코어데이터에서 정보를 조회할때 에러가 발생했다. 정확히 말해서는 에러가 발생했다기 보다 정보가 조회가 안되었다! 아래와 같이 위시리스트에 상품정보만 뜨고 아이디랑 가격이 안뜨는 문제가 발생했다!!!!!!!!!!!!🙉
그래서 cellForRowAt 메서드에 print문을 찍어서 받아오는 title, total(id임, 네이밍 잘못함), price를 확인해봤다.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let record = self.dataList[indexPath.row]
let title = record.value(forKey: "title") as? String
let total = record.value(forKey: "id") as? String
let price = record.value(forKey: "price") as? String
print(title, total, price)
let cell = wishListTableView.dequeueReusableCell(withIdentifier: "wishListTableViewCell", for: indexPath) as! wishListTableViewCell
cell.cellTitleLabel.text = title
cell.cellPriceLabel.text = price
cell.cellTotalLabel.text = total
return cell
}
실행하니 요래 나왔다..엥 왠 nil?? 뜬금없다. 일단 코어데이터에 저장하는 부분은 확인했다. 정상 저장된다. 즉 불러오는 과정이 문제라는 것이다.

그래서 검색해보니 value(forKey:) 메서드를 사용하여 값을 가져올 때 반환되는 값의 타입이 Any?이라고 한다. id 랑 price에는 Int 타입 정보가 들어있는데 나는 Int -> String 변환은 항상 가능하기 때문에 as? String으로 타입을 변환했는데 이러면 안되고 as? Int로 변환해주어야한다고 한다. 그래서 고친 코드는 다음과 같다.
let title = record.value(forKey: "title") as? String
let total = record.value(forKey: "id") as? Int
let price = record.value(forKey: "price") as? Int
any타입을 잘 몰라서 발생하는 실수였다. 정리하자면
- any : 어떤 종류의 값이든 할당할 수 있는 유연한 타입이다. Int를 넣었다가 String 넣는것 가능!
- as? : 값의 타입을 지정된 타입으로 변환하고, 변환이 실패할 경우 nil을 반환
오호.. 구현부터 문제해결까지...! 근거 있게 쓰신 거 같아서 매우 좋습니다 ㅎㅎ 역시 저희 조의 챌린지!