WishList 만들기 (1) - (URLSession 데이터 연결 완료)

WishList 만들기 (1) - (URLSession 데이터 연결 완료)

오늘은 새로운 숙련과제인 위시리스트 만들기를 진행하였다. 여기서 네트워킹과 코어데이터를 모두 사용해서 만들어야 하기 때문에 네트워킹과 코어데이터에 관한 내용을 공부해야해서 강의듣는데만 시간을 아주 많이 사용하게 되었다..

먼저 오늘은 시간 투자한거에 비해 많은 양을 하진 못하고 위시리스트를 만들어 API 와 연결하여 띄우기까지 성공했다.


 

스토리보드 구성

 

먼저 스토리보드를 구성해보자

스토리 보드는 간단하게 API에서 받아와 제품이미지, 정보를 띄우는 VC와 원하는 위시리스트를 담아두는 테이블뷰를 사용할 VC로 구성했다.

 

첫번째 VC에는 위시리스트를 담는 기능과 다음 제품을 확인할 수 있는 버튼, 그리고 위시리스트로 이동할 수 있는 버튼을 구성했다.

첫번째 VC에 있는 이미지, 레이블들을 먼저 연결하자 IBOutlet을 사용해서 쉽게 연결이 가능하다.

 

다음은 오토레이아웃을 잡아줬지만 이부분은 조금 숙달 되었으니 넘어가기로 하고

네트워킹에 연결할 데이터 모델링을 잡아주자

import UIKit
import CoreData

struct RemoteProduct: Decodable {
    let id: Int
    let title: String
    let description: String
    let price: Double
    let thumbnail: URL
    
    enum CodingKeys: String, CodingKey {
        case id, title, description, price, thumbnail
    }
}

코어 데이터를 추후에 사용하게 될거니까 일단 까먹지 않고 import 해두고

API 를 확인하여 가져올 데이터만을 선정해 이렇게 데이터 모델링을 잡아주었다.

 

네트워킹을 통해 API 데이터와 연결

 

먼저 네트워킹을 구분해 주기 위해 기존 Mode , View , Controller 외에 하나 새로운 그룹(Networking)을 추가했다.

class NetworkingManager {
    let productID = Int.random(in: 1 ... 100)
    
    func getMethod(completion: @escaping (Result<remoteproduct, error="">) -> Void) {
        // URL구조체 만들기
        guard let url = URL(string: "<https://dummyjson.com/products/\\(productID)>") else {
            print("Error: cannot create URL")
            completion(.failure(NetworkingError.invalidURL))
            return
        }
        
        // URL요청 생성
        var request = URLRequest(url: url)
        request.httpMethod = "GET"
        
        URLSession.shared.dataTask(with: request) { data, response, error in
            // 에러가 없어야 넘어감
            guard error == nil else {
                print("Error: error calling GET")
                print(error!)
                completion(.failure(NetworkingError.networkError))
                return
            }
            // 옵셔널 바인딩
            guard let safeData = data else {
                print("Error: Did not receive data")
                completion(.failure(NetworkingError.noData))
                return
            }
            // HTTP 200번대 정상코드인 경우만 다음 코드로 넘어감
            guard let httpResponse = response as? HTTPURLResponse, (200 ..< 300) ~= httpResponse.statusCode else {
                print("Error: HTTP request failed")
                completion(.failure(NetworkingError.invalidResponse))
                return
            }
            
            do {
                let product = try JSONDecoder().decode(RemoteProduct.self, from: safeData)
                completion(.success(product))
            } catch {
                print("Error decoding product data:", error)
                completion(.failure(NetworkingError.decodingError))
            }
        }.resume()
    }
}

enum NetworkingError: Error {
    case invalidURL
    case networkError
    case noData
    case invalidResponse
    case decodingError
}

</remoteproduct,>

URLSession 의 구문은 전부 이해할 순 없지만 추후 라이브러리를 통해 더 쉽게 구현할 수 있다고 생각해 원래 강의에서 사용했던 구문을 가져와 내 데이터 모델링에 맞게 수정했다.

 

그 이후 각각의 과정에서 어떤 에러가 났는지 확인할 수 있도록 각각의 네트워킹 과정마다 특정한 명칭의 에러를 넣어 특정 부분의 에러를 확인할 수 있도록 구성했다.

 

이번 과제에 URL의 마지막 특정 파라미터를 변경할 때마다 데이터가 변동되므로, 이를 랜덤으로 값이 들어갈 수 있도록 잡아줬다.

let productID = Int.random(in: 1 ... 100)
  
  func getMethod(completion: @escaping (Result<remoteproduct, error="">) -> Void) {
      // URL구조체 만들기
      guard let url = URL(string: "<https://dummyjson.com/products/\\(productID)>") else {
          print("Error: cannot create URL")
          completion(.failure(NetworkingError.invalidURL))
          return
      }
</remoteproduct,>

 

이렇게 하면 추후 다른 제품을 확인할때나, 처음 화면에 켜질때 각각 랜덤으로 상품이 구성되어 보여질 것이다.

 

네트워킹 호출 함수 구현

그 다음으로 이제 첫번째 VC 로 넘어와 네트워크 데이터를 받는 것을 보여주는 이미지와 레이블에 연결해보자

먼저 VC 내 네트워크매니저를 불러와 변수에 담아준다.

let networkingManager = NetworkingManager()

그 이후 네트워크매니저 안의 메소드 , 즉 URLSession으로 구성된 데이터를 각각의 원하는 이미지, 레이블에 넣을 수 있도록 함수를 구성한다.

 

먼저 UI만 별도로 보기 쉽게 구성했다.

func updateUI(with product: RemoteProduct) {
    imageView.load(url: product.thumbnail)
    titleLabel.text = product.title
    descriptionLabel.text = product.description
    priceLabel.text = "\\(product.price)"
}

이렇게 되면 내가 아까 연결해둔 아울렛들과 URLSession 에서 가져온 데이터가 연결된다.

그 이후 이 값을 감수를 통해 불러온다.

func fetchData() {
    networkingManager.getMethod { result in
        DispatchQueue.main.async {
            switch result {
            case .success(let product):
                self.updateUI(with: product)
            case .failure(let error):
                print("상품 불러오기가 실패했습니다:", error)
            }
        }
    }
}

이렇게 클로저를 활용하여 데이터를 호출하면 준비는 끝

마지막으로 viewDidLoad에 해당 값을 넣어준다.

override func viewDidLoad() {
    super.viewDidLoad()
    fetchData()
}

 

이렇게 되면 이제 URLSession을 통해 받고있는 데이터가 보여지게 된다.

오늘은 여기까지!

추후에 구현해야할 기능

위시리스트 담기 - 코어데이터를 통해 저장

다른 상품 보기 - URL 데이터 랜덤 변경된 거 활용해서 버튼 누를 때 URL 변경되는 방식으로 하면 되지 않을까?

위시리스트 보기 - 2번째 VC인 위시리스트 저장해둔 테이블뷰로 이동