๐Ÿ“‘ Project

[ Project ] WishListApp #1 / URLSession์„ ์ด์šฉํ•ด ๋ฐ์ดํ„ฐ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ

EarthSea 2024. 4. 15. 22:35
โ€‹โ€‹

 

 

 

 

 

์•ˆ๋…•ํ•˜์„ธ์š”!

์˜ค๋Š˜์€ WishListApp์˜ ์ฒซ ๋‹จ์ถ”๋ฅผ ๊ฟฐ๋งค๋Š” ๋‚ ์ด์—ˆ์–ด์š”!!

URLSession์— ๋Œ€ํ•œ ์ถฉ๋ถ„ํ•œ ๊ณต๋ถ€๋ฅผ ํ•œ ํ›„์— ํ”„๋กœ์ ํŠธ๋ฅผ ์‹œ์ž‘ํ•˜์˜€์Šต๋‹ˆ๋‹ค~!

๊ทธ๋Ÿผ ์ €์™€ URLSession์„ ํ†ตํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๋Š” ๊ณผ์ •์„ ํ•จ๊ป˜ ์‚ดํŽด๋ด…์‹œ๋‹ค~! โœจ

 

 

 

URLSession

 

Data ๊ตฌ์กฐ

์šฐ์„  URLSession์„ ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” Data ๊ตฌ์กฐ๋ฅผ ๋งŒ๋“ค์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค!

 

https://dummyjson.com/products

์œ„์˜ json์œผ๋กœ ์ด๋ฃจ์–ด์ง„ url ๋ฐ์ดํ„ฐ๋ฅผ ์•„์ฃผ ์‰ฝ๊ฒŒ Data ๊ตฌ์กฐ๋กœ ๋ฐ”๊พธ์–ด์ฃผ๋Š” ์›น์‚ฌ์ดํŠธ๊ฐ€ ์žˆ๋Š”๋ฐ์š”!

์ €๋Š” ์•„๋ž˜์˜ quicktype์„ ํ™œ์šฉํ•˜์—ฌ JSON ๋ฐ์ดํ„ฐ๋ฅผ Swift ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ๋กœ ๋ณ€ํ™˜ํ•ด์ฃผ์—ˆ์–ด์š”!

https://app.quicktype.io/

 

Instantly parse JSON in any language | quicktype

 

app.quicktype.io

 

 

JSON ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ -> Swift ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ๋กœ ๋ณ€ํ™˜

๋”๋ณด๊ธฐ

[ JSON ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ ]

{
  "products": [
    {
      "id": 1,
      "title": "iPhone 9",
      "description": "An apple mobile which is nothing like apple",
      "price": 549,
      "discountPercentage": 12.96,
      "rating": 4.69,
      "stock": 94,
      "brand": "Apple",
      "category": "smartphones",
      "thumbnail": "https://cdn.dummyjson.com/product-images/1/thumbnail.jpg",
      "images": [
        "https://cdn.dummyjson.com/product-images/1/1.jpg",
        "https://cdn.dummyjson.com/product-images/1/2.jpg",
        "https://cdn.dummyjson.com/product-images/1/3.jpg",
        "https://cdn.dummyjson.com/product-images/1/4.jpg",
        "https://cdn.dummyjson.com/product-images/1/thumbnail.jpg"
      ]
    },
    ...
}

 

[ Swift ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ ]

import Foundation

// MARK: - Welcome
struct Welcome: Codable {
    let products: [Product]
    let total, skip, limit: Int
}

// MARK: - Product
struct Product: Codable {
    let id: Int
    let title, description: String
    let price: Int
    let discountPercentage, rating: Double
    let stock: Int
    let brand, category: String
    let thumbnail: String
    let images: [String]
}

 

 

 

https://dummyjson.com/products/{id๊ฐ’}

 

id๊ฐ’์ด 1์—์„œ 100์‚ฌ์ด์˜ ๋žœ๋คํ•œ ๊ฐ’์„ ๊ฐ–๋„๋ก ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ๋ฅผ ์งœ์ฃผ๊ธฐ ์œ„ํ•ด Welcome ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ๋ฅผ ์ œ์™ธํ•œ Product ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ๋งŒ์„ ์‚ฌ์šฉํ•˜์˜€์–ด์š”~!

 

import Foundation

// MARK: - Product
struct Product: Codable {
    let id: Int
    let title, description: String
    let price: Int
    let discountPercentage: Double
    let brand, category: String
    let thumbnail: String
    let images: [String]
}

 

 

 

URLSession์„ ํ†ตํ•œ Data ์š”์ฒญ

 

 

์šฐ์„  Data๋ฅผ ๊ด€๋ฆฌํ•ด์ฃผ๋Š” ProductDataManager๋ฅผ ์ƒ์„ฑํ•ด์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค. 

 

struct ProductDataManager: Codable{
    
}

 

์œ„์˜ ๊ตฌ์กฐ์ฒด์•ˆ์— JSON ๋ฐ์ดํ„ฐ๋ฅผ Swift ๋ฐ์ดํ„ฐ๋กœ ๋ฐ”๊พธ์–ด์ฃผ๋Š” ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค์–ด ์ฃผ์—ˆ์–ด์š”.

 

func getProductData(id:Int, completion: @escaping (Product?) -> Void) {
       
}

 

์ง€๊ธˆ ๋‹ค๋ฃจ๊ณ  ์žˆ๋Š” URLSession์—์„œ์˜ dataTask ์ž‘์—…์€ ์‹œ๊ฐ„์ด ์˜ค๋ž˜๊ฑธ๋ฆด ์ˆ˜๋„ ์žˆ์œผ๋ฏ€๋กœ ์ž์ฒด์ ์œผ๋กœ ๋น„๋™๊ธฐ์ ์œผ๋กœ ์‹คํ–‰์„ ํ•ฉ๋‹ˆ๋‹ค. ๋น„๋™๊ธฐ์ ์œผ๋กœ ์‹คํ–‰์„ ํ•œ๋‹ค๋Š” ๊ฒƒ์€ ํ•ด๋‹น ์ž‘์—…์„ ๋ฉ”์ธ ์“ฐ๋ ˆ๋“œ๊ฐ€ ์•„๋‹Œ ๋‹ค๋ฅธ ์“ฐ๋ ˆ๋“œ๋กœ ๋ณด๋‚ด์–ด ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋น„๋™๊ธฐ์ ์œผ๋กœ ์ž‘์—…์„ ํ•œ๋‹ค๋ฉด ํ•ด๋‹น ์ž‘์—…์ด ๋๋‚˜์ง€ ์•Š๋”๋ผ๋„ ๋‹ค๋ฅธ ์ž‘์—…๋“ค์„ ๋™์‹œ์— ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

๋”ฐ๋ผ์„œ ๋น„๋™๊ธฐ์ ์œผ๋กœ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋ฉด ์ฝ”๋“œ๊ฐ€ ์ˆœ์ฐจ์ ์œผ๋กœ ์‹คํ–‰๋˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ํ•ด๋‹น ํ•จ์ˆ˜์—์„œ ๊ฐ’์„ ๋‹ค๋ฅธ ๋ณ€์ˆ˜์— ์ €์žฅํ•˜๊ฑฐ๋‚˜ return ํ•˜๋Š” ๊ณผ์ •์ด ๋ถˆ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. ํ˜„์žฌ ์ž‘์—…์ด ๋๋‚˜์ง€ ์•Š๋”๋ผ๋„ ๋‹ค์Œ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ํ•ด๋‹น ์ž‘์—…์ด ๋๋‚˜๋Š” ์‹œ์ ๊ณผ ๊ฐ’์„ ๋‹ค๋ฅธ ๋ณ€์ˆ˜์— ์ €์žฅํ•˜๊ฑฐ๋‚˜ return ํ•˜๋Š” ์‹œ์ ์ด ๋‹ค๋ฅธ ๊ฒƒ์ด์ฃ ! ๊ทธ๋ž˜์„œ URLSession์—์„œ ๊ฐ€์ ธ์˜จ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์ฝœ๋ฐฑํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค.

 

guard let url = URL(string: "https://dummyjson.com/products/\(id)") else {
    print("Error: URL ์ฃผ์†Œ๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๋Š”๋ฐ ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค.")
    completion(nil)
    return
}

var request = URLRequest(url: url)
request.httpMethod = "GET"

let session = URLSession.shared

session.dataTask(with: request) { data, response, error in
    guard error == nil else {
        print("Error: ์‹คํŒจ")
        completion(nil)
        return
    }
    
    guard let response = response as? HTTPURLResponse, (200 ..< 299) ~= response.statusCode else {
        print("Error: ์‘๋‹ต์ด ์—†์Šต๋‹ˆ๋‹ค.")
        completion(nil)
        return
    }
    
    guard let safeData = data else {
        print("Error: ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๋Š”๋ฐ ์‹คํŒจํ•˜์˜€์Šต๋‹ˆ๋‹ค.")
        completion(nil)
        return
    }
    
    do {
        let decoder = JSONDecoder()
        let product = try decoder.decode(Product.self, from: safeData)
    
        completion(product)
    } catch {
        print("Error: ๋ฐ์ดํ„ฐ๋ฅผ decoderํ•˜๋Š”๋ฐ์— ์‹คํŒจํ•˜์˜€์Šต๋‹ˆ๋‹ค.")
    }
}.resume()

 

์œ„์˜ ํ•จ์ˆ˜ ์•ˆ์— ๋„ฃ์–ด์ค€ url ์ฃผ์†Œ๋ฅผ ์ด์šฉํ•˜์—ฌ URLSession์—๊ฒŒ data๋ฅผ ์š”์ฒญํ•˜๋Š” ๋กœ์ง์„ ๋„ฃ์–ด์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค!

 

 

ํ•˜๋‚˜ํ•˜๋‚˜ ์ฝ”๋“œ๋ฅผ ๋œฏ์–ด๋ด…์‹œ๋‹ค.

1) URL ๊ตฌ์กฐ์ฒด ๋งŒ๋“ค๊ธฐ

๋จผ์ € ์ •๋ณด๊ฐ€ ๋‹ด๊ธด URL์˜ ์ฃผ์†Œ๋ฅผ ๋ฌธ์ž์—ด๋กœ ๋ฐ›์•„์™€ URL ๊ตฌ์กฐ์ฒด๋ฅผ ๋งŒ๋“ค์–ด์ค๋‹ˆ๋‹ค.

guard let url = URL(string: "https://dummyjson.com/products/\(id)") else {
    print("Error: URL ์ฃผ์†Œ๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๋Š”๋ฐ ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค.")
    completion(nil)
    return
}

 

 

2) URLRequest ๋งŒ๋“ค๊ธฐ

URLSession์˜ ์š”์ฒญ ๋ฉ”์„œ๋“œ์˜ ์ข…๋ฅ˜๋ฅผ ์ •ํ•˜๋Š”  ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค!

var request = URLRequest(url: url)
request.httpMethod = "GET"

"GET" ์ž๋ฆฌ์—๋Š” ๋‹ค๋ฅธ ์š”์ฒญ ๋ฉ”์„œ๋“œ๋ฅผ ์“ธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • "POST" - ์ƒˆ๋กœ์šด ๋ฐ์ดํ„ฐ๋ฅผ ์“ฐ๊ธฐ
  • "PUT" - ๊ธฐ์กด์— ์žˆ๋˜ ๋ฐ์ดํ„ฐ์˜ ์ •๋ณด๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๊ธฐ
  • "DELETE" - ๋ฐ์ดํ„ฐ๋ฅผ ์‚ญ์ œํ•˜๊ธฐ

์‚ฌ์‹ค URLSession์—๋Š” Default ๊ฐ’์œผ๋กœ "GET" ์š”์ฒญ ๋ฉ”์„œ๋“œ๊ฐ€ ๋“ค์–ด๊ฐ€์žˆ์–ด์š”! ๊ทธ๋ž˜์„œ "GET" ์š”์ฒญ์„œ๋ฅผ ์“ฐ์ง€ ์•Š์•„๋„ ๋ฉ๋‹ˆ๋‹ค!

 

 

3) URLSession ๋งŒ๋“ค๊ธฐ

let session = URLSession.shared
let session = URLSession(configuration: .default)

์œ„์˜ ๋‘ ๊ฐ€์ง€ ์ค‘ ํ•˜๋‚˜๋งŒ ์‚ฌ์šฉํ•˜์…”๋„ ๋ฉ๋‹ˆ๋‹ค. ๊ฐ™์€ ์˜๋ฏธ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ์ฝ”๋“œ์—์š”!!

 

 

4) dataTask ๋ฐ resume

URLSession์˜ dataTask ํ•จ์ˆ˜๋ฅผ ํ†ตํ•ด data๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค!

session.dataTask(with: request) { data, response, error in
    guard error == nil else {
        print("Error: ์‹คํŒจ")
        completion(nil)
        return
    }
    
    guard let response = response as? HTTPURLResponse, (200 ..< 299) ~= response.statusCode else {
        print("Error: ์‘๋‹ต์ด ์—†์Šต๋‹ˆ๋‹ค.")
        completion(nil)
        return
    }
    
    guard let safeData = data else {
        print("Error: ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๋Š”๋ฐ ์‹คํŒจํ•˜์˜€์Šต๋‹ˆ๋‹ค.")
        completion(nil)
        return
    }
}.resume()

 

data์™€ response, error๋ฅผ ์˜ต์…”๋„ ๋ฐ”์ธ๋”ฉํ•˜์˜€์–ด์š”!

์ด ๋•Œ, dataTask๋ผ๋Š” ์ž‘์—…์€ URLSession์— ์ž‘์—…์„ ๋ถ€์—ฌํ•˜๋Š” ๊ณผ์ •์œผ๋กœ ์ผ์‹œ์ ์œผ๋กœ ์ •์ง€๊ฐ€ ๋œ ์ƒํƒœ์ž…๋‹ˆ๋‹ค! ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— ๊ผญ task๋ฅผ ์‹œ์ž‘ํ•œ๋‹ค๋Š” resume() ์ž‘์—… ๊ผญ ์‹คํ–‰ํ•ด์ฃผ์…”์•ผํ•ด์š”!!! โญ๏ธ ( ์ €๋„ ์˜ค๋Š˜ ๊นŒ๋จน๊ณ  ์•ˆ์ ์—ˆ๋‹ค๊ฐ€ error ๋‚ฌ์Šต๋‹ˆ๋‹ค ใ…Ž )

 

 

์ด์ œ ์ฝ”๋“œ ์ฒ˜๋ฆฌ ๋‹จ๊ณ„์—์„œ ํ•ด๋‹น ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด Decoder ํ•˜๋Š” ๊ณผ์ •์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค!

Decoder ๋กœ ๋ฐ์ดํ„ฐ ๋ณ€ํ™˜ํ•˜๊ธฐ

    do {
        let decoder = JSONDecoder()
        let product = try decoder.decode(Product.self, from: safeData)
    
        completion(product)
    } catch {
        print("Error: ๋ฐ์ดํ„ฐ๋ฅผ decoderํ•˜๋Š”๋ฐ์— ์‹คํŒจํ•˜์˜€์Šต๋‹ˆ๋‹ค.")
    }

 

๋ฐ์ดํ„ฐ๋ฅผ ์šฐ๋ฆฌ๊ฐ€ ์›ํ•˜๋Š” ํ˜•ํƒœ๋กœ ๋ฐ”๊พธ๊ธฐ ์œ„ํ•ด์„œ๋Š” JSONDecoder์ด๋ผ๋Š” ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค. JSONDecoder๋ฉ”์„œ๋“œ๋Š” ์—๋Ÿฌ์ฒ˜๋ฆฌ๊ฐ€ ๊ฐ€๋Šฅํ•œ ๋ฉ”์„œ๋“œ์ž„์œผ๋กœ do-catch ๋ฌธ์„ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค! JSONDecoder์˜ decode๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํ•ด๋‹น ํด๋ž˜์Šค์˜ ํ˜•ํƒœ๋กœ JSON๋ฐ์ดํ„ฐ๋ฅผ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์–ด์š”!

 

decode( ๋ณ€ํ˜•ํ•˜๊ณ  ์žˆ์€ ํ˜•ํƒœ.self, from: ์›๋ž˜์˜ ๋ฐ์ดํ„ฐ )

์ด ๋•Œ, .self๋Š” ํด๋ž˜์Šค ๊ทธ ์ž์ฒด ( ๋ถ•์–ด๋นต ํ‹€ ์ž์ฒด )๋ฅผ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

 

์—ฌ๊ธฐ์„œ ์ฃผ์˜ํ•  ์  JSONDecoder๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” Swift Data๊ฐ€ ์ฑ„ํƒํ•ด์•ผํ•  ํ”„๋กœํ† ์ฝœ์ด ์žˆ์–ด์š”!

  • Decodable : ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ”๋“œ๋กœ ๋ณ€ํ˜•ํ•˜๋Š” ํ”„๋กœํ† ์ฝœ
  • Encodable : ๊ตฌ์กฐ์ฒด๋‚˜ ํด๋ž˜์Šค๋ฅผ ๋ฐ์ดํ„ฐ์˜ ํ˜•ํƒœ๋กœ ๋ณ€ํ˜•ํ•˜๋Š” ํ”„๋กœํ† ์ฝœ
  • Codable : Decodable + Encodable ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ํ”„๋กœํ† ์ฝœ

์œ„์˜ ๋ฐ์ดํ„ฐ์— Codable์ด๋ผ๋Š” ํ”„๋กœํ† ์ฝœ์„ ์ฑ„ํƒํ•œ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค!

 

 

[ ์ „์ฒด ์ฝ”๋“œ ]

struct ProductDataManager: Codable{
    

    // MARK: - getProductData
    func getProductData(id:Int, completion: @escaping (Product?) -> Void) {
        
        guard let url = URL(string: "https://dummyjson.com/products/\(id)") else {
            print("Error: URL ์ฃผ์†Œ๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๋Š”๋ฐ ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค.")
            completion(nil)
            return
        }
        
        var request = URLRequest(url: url)
        request.httpMethod = "GET"
        
        let session = URLSession.shared
        
        session.dataTask(with: request) { data, response, error in
            guard error == nil else {
                print("Error: ์‹คํŒจ")
                completion(nil)
                return
            }
            
            guard let response = response as? HTTPURLResponse, (200 ..< 299) ~= response.statusCode else {
                print("Error: ์‘๋‹ต์ด ์—†์Šต๋‹ˆ๋‹ค.")
                completion(nil)
                return
            }
            
            guard let safeData = data else {
                print("Error: ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๋Š”๋ฐ ์‹คํŒจํ•˜์˜€์Šต๋‹ˆ๋‹ค.")
                completion(nil)
                return
            }
            
            do {
                let decoder = JSONDecoder()
                let product = try decoder.decode(Product.self, from: safeData)
            
                completion(product)
            } catch {
                print("Error: ๋ฐ์ดํ„ฐ๋ฅผ decoderํ•˜๋Š”๋ฐ์— ์‹คํŒจํ•˜์˜€์Šต๋‹ˆ๋‹ค.")
            }
        }.resume()
    }
    
}

 

 

 

์ด๋ ‡๊ฒŒ URLSession์„ ํ†ตํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ถˆ๋Ÿฌ์™€ Swift ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๊ฒƒ์„ ์™„๋ฃŒํ•˜์˜€์Šต๋‹ˆ๋‹ค!

์ด์ œ URLSession๊ณผ ์กฐ๊ธˆ ์นœํ•ด์ง„ ๋Š๋‚Œ์ด ๋‚˜๋„ค์šฉ!!ใ…Žใ…Žใ…Ž