์ƒ์„ธ ์ปจํ…์ธ 

๋ณธ๋ฌธ ์ œ๋ชฉ

[ URLSession ] ํฌ์ผ“๋ชฌ API๋กœ ์„œ๋ฒ„์—์„œ ์ด๋ฏธ์ง€ ๋ฐ์ดํ„ฐ ๋ฐ›์•„์˜ค๋Š” ํ๋ฆ„ ์ฐพ๊ธฐ

๋ณธ๋ฌธ

1. URLSession์œผ๋กœ ์š”์ฒญ(request) - ํฌ์ผ“๋ชฌ API์—์„œ ๋งํฌ ์ฐพ๊ธฐ

https://pokeapi.co/

 

 

 

2. ์„œ๋ฒ„์—์„œ ๋‚ด๋ ค์ค€ ๋ฐ์ดํ„ฐ ๋ถ„์„ - JSON ํ˜•ํƒœ  

 

 

 

3. URLSession ๊ธฐ๋ณธ ์‚ฌ์šฉ๋ฒ•

1. URL ์ƒ์„ฑ ๋ฐ ๊ฒ€์ฆ
URL(string:) ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ด URL ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์œ ํšจ์„ฑ์„ ๊ฒ€์ฆํ•œ๋‹ค.

guard let url = URL(string: "https://example.com/api") else {
    print("Invalid URL")
    return
}

 

2. URLRequest ๊ฐ์ฒด ์ƒ์„ฑ (์˜ต์…˜)

๊ธฐ๋ณธ GET ์š”์ฒญ์€ URL ๊ฐ์ฒด๋กœ ๋ฐ”๋กœ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, HTTP ๋ฉ”์„œ๋“œ ๋ณ€๊ฒฝ์ด๋‚˜ ํ—ค๋” ์„ค์ •์ด ํ•„์š”ํ•˜๋‹ค๋ฉด URLRequest ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.

 

var request = URLRequest(url: url)
request.httpMethod = "GET" // ๊ธฐ๋ณธ๊ฐ’์€ GET, ํ•„์š”์— ๋”ฐ๋ผ ๋ณ€๊ฒฝ ๊ฐ€๋Šฅ
request.addValue("application/json", forHTTPHeaderField: "Content-Type")

 

 

3. URLSession ๊ฐ์ฒด ์ƒ์„ฑ
์• ํ”Œ์ด ์ œ๊ณตํ•˜๋Š” ๊ธฐ๋ณธ ์„ธ์…˜ ์ƒ์„ฑ, ์ผ๋ฐ˜์ ์ธ ์š”์ฒญ์€ URLSession.shared(Singleton Session)์„ ์‚ฌ์šฉํ•˜๊ณ  

ํ•„์š”์— ๋”ฐ๋ผ ์ƒ์„ฑ์ž๋ฅผ ํ†ตํ•œ ์ปค์Šคํ…€ ์„ค์ •์„ ์‚ฌ์šฉํ•ด URLSessionConfiguration ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.

// ๊ธฐ๋ณธ ์„ค์ • -> ์ปค์Šคํ…€ ๋ถˆ๊ฐ€, ์‘๋‹ต์€ ํด๋กœ์ € ํ˜•ํƒœ๋กœ ์ „๋‹ฌ, ๋ฐฑ๊ทธ๋ผ์šด๋“œ ํ˜•ํƒœ ์ฒ˜๋ฆฌ ๋ถˆ๊ฐ€
let session = URLSession.shared 

// ์ปค์Šคํ…€ ์„ค์ •
let session = URLSession(configuration: .default)

// default : shared์™€ ์œ ์‚ฌํ•˜๋‚˜ ์ปค์Šคํ…€ ๊ฐ€๋Šฅ, ์‘๋‹ต์€ ํด๋กœ์ € ๋˜๋Š” Delegate ํ˜•ํƒœ๋กœ ์ „๋‹ฌ
// ephemeral : ์ •๋ณด๋ฅผ ํœ˜๋ฐœ์‹œํ‚ค๊ณ  ์‹ถ์„ ๋•Œ
// background : ์•ฑ์„ ์“ฐ๊ณ  ์žˆ์ง€ ์•Š์„ ๋•Œ ์–ด๋–ค ์ฒ˜๋ฆฌ๋ฅผ ํ•˜๊ณ  ์‹ถ์„ ๋•Œ ์‚ฌ์šฉ (ex. ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์žฌ์ƒ)

 

 

4. Data Task ์ƒ์„ฑ ๋ฐ ์‹œ์ž‘ : ๋„คํŠธ์›Œํฌ ์š”์ฒญ์€ ๋น„๋™๊ธฐ์ ์œผ๋กœ ์ฒ˜๋ฆฌ๋˜๋ฏ€๋กœ ํด๋กœ์ €๋ฅผ ์‚ฌ์šฉํ•ด ์‘๋‹ต์„ ์ฒ˜๋ฆฌํ•œ๋‹ค. 

dataTask(with:  completionHandler:) ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋„คํŠธ์›Œํฌ ์š”์ฒญ์„ ์ˆ˜ํ–‰ํ•˜๊ณ , ๋ฐ์ดํ„ฐ๋ฅผ ์ฒ˜๋ฆฌ

 

๋„คํŠธ์›Œํ‚น์„ ํ•˜๋Š” ์ฝ”๋“œ๋“ค์€ ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ๋ฅผ ํ•ด์•ผํ•˜๋Š” ์ด์œ ๋Š” CPU์— ๋ถ€ํ•˜๋ฅผ ์ผ์œผํ‚ค๋Š” ์ž‘์—…์œผ๋กœ

๋ฉ”์ธ ์Šค๋ ˆ๋“œ์—์„œ ์‹คํ–‰ ์‹œํ‚ค๋ฉด ์•ฑ์ด ๋ฒ„๋ฒ… ๊ฑฐ๋ฆด ์ˆ˜ ์žˆ๋‹ค. 

 

๊ทธ๋ž˜์„œ .resume()์ด ์‹คํ–‰๋œ ๋‹ค์Œ์— .shared.dataTask ์ฝ”๋“œ๋“ค์ด ์‹คํ–‰๋œ๋‹ค. 

 

๋ฐฑ๊ทธ๋ผ์šด๋“œ ์Šค๋ ˆ๋“œ์—์„œ ๋™์ž‘ํ•˜๊ธฐ ๋•Œ๋ฌธ์— UI๊ด€๋ จ ์ฒ˜๋ฆฌ๋Š” ๋ฉ”์ธ ์Šค๋ ˆ๋“œ๋กœ ์ „ํ™˜ํ•ด์ค˜์•ผ ํ•œ๋‹ค. 

๊ตฌํ˜„๋ถ€์—์„œ ๋ฉ”์ธ ์Šค๋ ˆ๋“œ๋กœ ๋ฐ”๊ฟ”์ฃผ๋Š” ์ฒ˜๋ฆฌ๋ฅผ ํ•˜๋Š” ๋ฐฉ๋ฒ•๋„ ์žˆ๋‹ค. ๐Ÿ‘‡

let url = URL(string: "")!

URLSession.shared.dataTask(with: url) { data, response, error in

    // ์•„์˜ˆ ๊ตฌํ˜„๋ถ€์—์„œ ๋ฉ”์ธ ์Šค๋ ˆ๋“œ๋กœ ๋ฐ”๊ฟ”์ฃผ๋Š” ์ฒ˜๋ฆฌ๋ฅผ ํ•ด์ฃผ๊ธฐ๋„ ํ•œ๋‹ค.
    DispatchQueue.main.async {
        guard error == nil else {
            completion(nil, .failedRequest)
            return
        }

        guard let data = data else {
            completion(nil, .noData)
            return
        }

        // HTTPURLResponse๋กœ ํƒ€์ž… ์บ์ŠคํŒ…
        // URLResponse > HTTPURLResponse๋Š” ์ƒ์† ๊ด€๊ณ„
        guard let response = response as? HTTPURLResponse else {
            completion(nil, .invaildResponse)
            return
        }

        guard response.statusCode == 200 else {
            completion(nil, .failedRequest)
            return
        }

        do {
            let result = try JSONDecoder().decode(Lotto.self, from: data)
            completion(result, nil)

        } catch {
            completion(nil, .invaildData)
        }
    }

}.resume()

์ฝ”๋“œ ์ถœ์ฒ˜ : https://taekki-dev.tistory.com/115

 

 

 

๐ŸŒŸ ํด๋กœ์ € (data, response, error) in

  • data: ์„œ๋ฒ„์—์„œ ์‘๋‹ต์œผ๋กœ ๋ฐ›์€ ๋ฐ์ดํ„ฐ. ์„ฑ๊ณต์ ์ธ ์š”์ฒญ์˜ ๊ฒฝ์šฐ, ์ด ๋ฐ์ดํ„ฐ๋Š” ์„œ๋ฒ„๊ฐ€ ๋ฐ˜ํ™˜ํ•œ ๋‚ด์šฉ์ด ๋œ๋‹ค.
  • response: ์„œ๋ฒ„์˜ ์‘๋‹ต ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ. HTTP ์ƒํƒœ ์ฝ”๋“œ์™€ ํ—ค๋” ์ •๋ณด๊ฐ€ ํฌํ•จ๋œ๋‹ค.
  •  error: ์š”์ฒญ ์ค‘ ๋ฐœ์ƒํ•œ ์˜ค๋ฅ˜๊ฐ€ ์žˆ์œผ๋ฉด ์ด ๋ณ€์ˆ˜์— ์˜ค๋ฅ˜ ์ •๋ณด๊ฐ€ ๋‹ด๊ธด๋‹ค.
let task = session.dataTask(with: request) { data, response, error in
    // ์—๋Ÿฌ ์ฒ˜๋ฆฌ
    if let error = error {
        print("Error: \(error.localizedDescription)")
        return
    }
    
    // ์‘๋‹ต ์ƒํƒœ ์ฝ”๋“œ ํ™•์ธ
    if let httpResponse = response as? HTTPURLResponse {
        print("Status code: \(httpResponse.statusCode)")
        guard (200...299).contains(httpResponse.statusCode) else {
            print("Request failed with status code: \(httpResponse.statusCode)")
            return
        }
    }
    
    // ๋ฐ์ดํ„ฐ ํ™•์ธ
    guard let data = data else {
        print("No data received")
        return
    }
    
    // ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ (์˜ˆ: JSON ๋””์ฝ”๋”ฉ)
    // JSON ํŒŒ์‹ฑ : ์„œ๋ฒ„์—์„œ ๋ฐ›์€ JSON ๋ฐ์ดํ„ฐ๋ฅผ JSONDecoder๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ Swift ๊ตฌ์กฐ์ฒด๋กœ ๋””์ฝ”๋”ฉ(๋ณ€ํ™˜)
    do {
        let json = try JSONSerialization.jsonObject(with: data, options: [])
        print("JSON response: \(json)")
    } catch {
        print("JSON decoding error: \(error.localizedDescription)")
    }
}

// ํƒœ์Šคํฌ ์‹œ์ž‘
task.resume()

 

 

 

๐Ÿ“Œ JSON ํŒŒ์‹ฑ

์„œ๋ฒ„๊ฐ€ JSON ํ˜•์‹์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์‘๋‹ต์œผ๋กœ ๋ณด๋‚ด์ค„ ๋•Œ ์ด๋ฅผ Swfit์˜ ๋ฐ์ดํ„ฐ ๊ตฌ์กธ ๋ณ€ํ™˜ํ•˜๋Š” ๊ณผ์ •

 


ํ…œํ”Œ๋ฆฟ ์ฝ”๋“œ

์ „์ฒด ์ฝ”๋“œ - NetworkManager 

class ImageRepository {
    func fetchRandomPokemonImage(completion: @escaping (UIImage?) -> Void) {
        let randomId = Int.random(in: 1...1000)
        let urlString = "https://pokeapi.co/api/v2/pokemon/\(randomId)"
        
        guard let url = URL(string: urlString) else {
            completion(nil)
            return
        }
        
        let task = URLSession.shared.dataTask(with: url) { data, response, error in
            // ๋ฐ์ดํ„ฐ๊ฐ€ ์กด์žฌํ•˜๊ณ , ์—๋Ÿฌ๊ฐ€ ์—†๋Š”์ง€ ํ™•์ธ
            guard let data = data, error == nil,
                  // JSON ๋ฐ์ดํ„ฐ๋ฅผ ํŒŒ์‹ฑํ•˜์—ฌ ๋”•์…”๋„ˆ๋ฆฌ๋กœ ๋ณ€ํ™˜
                  let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any],
                  // JSON์—์„œ 'sprites' ๋”•์…”๋„ˆ๋ฆฌ๋ฅผ ๊ฐ€์ ธ์˜ด
                  let sprites = json["sprites"] as? [String: Any],
                  // 'sprites' ๋”•์…”๋„ˆ๋ฆฌ์—์„œ 'front_default' URL ๋ฌธ์ž์—ด์„ ๊ฐ€์ ธ์˜ด
                  let imageUrlString = sprites["front_default"] as? String,
                  // URL ๋ฌธ์ž์—ด์„ URL ๊ฐ์ฒด๋กœ ๋ณ€ํ™˜
                  let imageUrl = URL(string: imageUrlString) else {
                // ์‹คํŒจํ•œ ๊ฒฝ์šฐ completion ํ•ธ๋“ค๋Ÿฌ ํ˜ธ์ถœ
                completion(nil)
                return
            }
            
            // ์„ฑ๊ณตํ•œ ๊ฒฝ์šฐ ์ด๋ฏธ์ง€ ๋‹ค์šด๋กœ๋“œ ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ
            self.downloadImage(from: imageUrl, completion: completion)
        }
        task.resume()
        
    }
    
    private func downloadImage(from url: URL, completion: @escaping (UIImage?) -> Void) {
        DispatchQueue.global().async {
            if let imageData = try? Data(contentsOf: url),
               let image = UIImage(data: imageData) {
                DispatchQueue.main.async {
                    completion(image)
                }
            } else {
                DispatchQueue.main.async {
                    completion(nil)
                }
            }
        }
    }
}

 

 

 

GET ์š”์ฒญ

import Foundation

func performGetRequest(urlString: String, completion: @escaping (Data?, URLResponse?, Error?) -> Void) {
    // 1. URL ์ƒ์„ฑ
    guard let url = URL(string: urlString) else {
        print("Invalid URL")
        return
    }

    // 2. URLSession ์ƒ์„ฑ (๊ณต์œ  ์ธ์Šคํ„ด์Šค ์‚ฌ์šฉ)
    let session = URLSession.shared

    // 3. Data Task ์ƒ์„ฑ ๋ฐ ์‹œ์ž‘
    let task = session.dataTask(with: url) { data, response, error in
        // 4. ์‘๋‹ต ์ฒ˜๋ฆฌ
        completion(data, response, error)
    }

    task.resume() // ์š”์ฒญ ์‹œ์ž‘
}

// ์‚ฌ์šฉ ์˜ˆ์ œ
let url = "https://api.example.com/data"
performGetRequest(urlString: url) { data, response, error in
    if let error = error {
        print("Error: \(error.localizedDescription)")
        return
    }
    guard let data = data else {
        print("No data received")
        return
    }
    // ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ (์˜ˆ: JSON ํŒŒ์‹ฑ)
    print(String(data: data, encoding: .utf8)!)
}

 

 

 

 

 

POST์š”์ฒญ

import Foundation

func performPostRequest(urlString: String, parameters: [String: Any], completion: @escaping (Data?, URLResponse?, Error?) -> Void) {
    // 1. URL ์ƒ์„ฑ
    guard let url = URL(string: urlString) else {
        print("Invalid URL")
        return
    }

    // 2. URLRequest ๊ฐ์ฒด ์ƒ์„ฑ ๋ฐ ์„ค์ •
    var request = URLRequest(url: url)
    request.httpMethod = "POST"
    request.addValue("application/json", forHTTPHeaderField: "Content-Type")

    do {
        // 3. ์š”์ฒญ ๋ฐ”๋”” ์„ค์ •
        request.httpBody = try JSONSerialization.data(withJSONObject: parameters, options: [])
    } catch let error {
        print("Error serializing JSON: \(error.localizedDescription)")
        return
    }

    // 4. URLSession ์ƒ์„ฑ (๊ณต์œ  ์ธ์Šคํ„ด์Šค ์‚ฌ์šฉ)
    let session = URLSession.shared

    // 5. Data Task ์ƒ์„ฑ ๋ฐ ์‹œ์ž‘
    let task = session.dataTask(with: request) { data, response, error in
        // 6. ์‘๋‹ต ์ฒ˜๋ฆฌ
        completion(data, response, error)
    }

    task.resume() // ์š”์ฒญ ์‹œ์ž‘
}

// ์‚ฌ์šฉ ์˜ˆ์ œ
let url = "https://api.example.com/submit"
let parameters = ["key1": "value1", "key2": "value2"]
performPostRequest(urlString: url, parameters: parameters) { data, response, error in
    if let error = error {
        print("Error: \(error.localizedDescription)")
        return
    }
    guard let data = data else {
        print("No data received")
        return
    }
    // ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ (์˜ˆ: JSON ํŒŒ์‹ฑ)
    print(String(data: data, encoding: .utf8)!)
}

 

 

 

๊ด€๋ จ๊ธ€ ๋”๋ณด๊ธฐ