[iOS] 위시리스트 만들기 [1]

Kiwi·2024년 4월 18일

iOS

목록 보기
9/15
post-thumbnail

위시리스트 화면을 제작해야한다. 대충 UI 가이드는 주어졌으며 필수 사항은 다음과 같다.

  • URLsession을 이용해 네트워크 통신하기
  • core data를 사용해서 데이터 저장하기
  • 화면전환으로 위시리스트 보여주기

🌳 URLSession 사용해서 네트워크 연결 및 사진불러오기

여태까지 네트워크 연결은 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을 반환
profile
🐣 iOS Developer

2개의 댓글

comment-user-thumbnail
2024년 4월 18일

오호.. 구현부터 문제해결까지...! 근거 있게 쓰신 거 같아서 매우 좋습니다 ㅎㅎ 역시 저희 조의 챌린지!

1개의 답글