์๋ ํ์ธ์!
์ค๋์ WishListApp์ ์ฒซ ๋จ์ถ๋ฅผ ๊ฟฐ๋งค๋ ๋ ์ด์์ด์!!
URLSession์ ๋ํ ์ถฉ๋ถํ ๊ณต๋ถ๋ฅผ ํ ํ์ ํ๋ก์ ํธ๋ฅผ ์์ํ์์ต๋๋ค~!
๊ทธ๋ผ ์ ์ URLSession์ ํตํ ๋ฐ์ดํฐ๋ฅผ ๋ถ๋ฌ์ค๋ ๊ณผ์ ์ ํจ๊ป ์ดํด๋ด ์๋ค~! โจ
URLSession
Data ๊ตฌ์กฐ
์ฐ์ URLSession์ ํ๊ธฐ ์ํด์๋ Data ๊ตฌ์กฐ๋ฅผ ๋ง๋ค์ด์ผ ํฉ๋๋ค!
https://dummyjson.com/products
์์ json์ผ๋ก ์ด๋ฃจ์ด์ง url ๋ฐ์ดํฐ๋ฅผ ์์ฃผ ์ฝ๊ฒ Data ๊ตฌ์กฐ๋ก ๋ฐ๊พธ์ด์ฃผ๋ ์น์ฌ์ดํธ๊ฐ ์๋๋ฐ์!
์ ๋ ์๋์ quicktype์ ํ์ฉํ์ฌ JSON ๋ฐ์ดํฐ๋ฅผ Swift ๋ฐ์ดํฐ ๊ตฌ์กฐ๋ก ๋ณํํด์ฃผ์์ด์!
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๊ณผ ์กฐ๊ธ ์นํด์ง ๋๋์ด ๋๋ค์ฉ!!ใ ใ ใ