[iOS] URLSession

URLSession

URL로 표시된 엔드포인트에서 데이터를 업로드하거나 다운로드하는 등의 API를 제공해 주는 클래스를 말하며, 내부의 여러 설정을 통해 데이터를 어떻게 전송하고 어떻게 동작할지를 설정할 수 있다.

URLSession의 shared의 경우에는 은 싱글톤 객체로 구성되어 있어 사용자가 커스터마이징한 것을 등록할 수는 없지만, 그 사용 범위가 제한적인 경우 내부의 method나 설정을 이용하여 네트워크 통신을 할 수 있다. 간단하고 기본적인 요청일 경우에는 앞서 말한 Shared Session나 조금 더 커스터마이징을 해서 사용할 수 있는 Default Session을 이용하여 사용할 수 있고, 이외에도 다른 Session Configuration을 이용하여 별도의 처리를 할 수 있다.

 

  1. URLSessionConfiguration
    • 이... 이게 몰까?
      • 환경 설정이라고 보면 된다! 일반적인 프로퍼티 설정, 쿠키 정책, 보안 정책, 캐시 정책, 백그라운드 전송 등을 세부적으로 설정할 수 있다.
        • 그 예를 들어보자면... 
        • 앱을 켰을 때 wifi 데이터를 사용할지, 아니면 셀룰러 데이터까지 사용할지를 설정한다든가?
        • 브라우저를 열었을 때, 시크릿 브라우저를 띄울지 아니면 일반 브라우저를 띄울지! 라든가
        • 보안 / 캐싱 / 세션 처리를 어떻게 할 것인가? 같은 것을 말한다.
    • 그 종류는 뭐가 있을까?
      • Shared Session
        • 환경 설정의 기본!
        • 다만, 너무 기본이라 completionHandler로만 데이터 처리가 가능하며, SessionDelegate를 사용할 수 없다.
        • 그렇기 때문에 진행률을 보여주고자 한다면? DefaultSession으로 구현해야 한당. 
        • 백그라운드 전송은 지원하지 않는다.
      • Default Session
        • Shared와 마찬가지로 기본적인 설정을 말하며, Shared와 달리 커스터마이징이 가능하다.
        • SessionDelegate를 통해 네트워크 응답에 대한 세부적인 제어가 가능하다! (shared는 불가능했음)
      • Ephemeral Session
        • Shared와 달리, 직접 생성할 수 있는 설정을 말하며 Shared와 비슷하지만 메모리에 처리된 데이터가 저장되지 않게끔 보안에 신경쓰고 싶을 때 사용한다!
          • 쿠키, 캐시, 인증 정보 등을 기록하지 않는다. (= 프라이빗 기능을 구현할 때 사용된다!)
          • 아마 은행쪽이나 결제쪽 통신을 할 때  자주 사용하지 않을까?
      • Background Session
        • 영화를 다운받는 것 같이 큰 파일을 다운받을 때는 핸드폰을 켜놓고 그 화면을 기다리고 있는 게 아니라 백그라운드에서도 파일 작업이 진행되어야 할 것이다!! 그때 이 Session을 사용한다.
        • 앱이 실행 중이지 않을 때(화면에 떠 있지 않는 상태일 때) 데이터를 다운로드하거나 업로드할 수 있다.
  2. 데이터 양에 따른 요청 리소스 응답
    • 앞서 말한 Session들이 생성된 이후에는 Task를 생성하게 되는데, URLSession을 통해 생성되는 개별적인 데이터 요청을 task라고 한다. 
    • 기본적으로 openAPI 환경에서 통신하는 것은 리소스가 적게 들어가서 속도가 그렇게 오래 걸리지 않는다.
    • 데이터를 전달하려는 방식과 구현하려는 목적에 따라서 우리는 task 타입을 설정해 주게 된다. 
    • 그 종류는 뭐가 있을까?
      • dataTask
        • 일반적으로 사용하는 방식을 말한다! 
      • uploadTask
        • 영상을 업로드한다든가 데이터가 큰 영상을 보낼 때 사용한다.
      • downloadTask
        • dataTask에서 명시적으로 더 큰 단위의 데이터를 다운로드 받고 싶을 때 다운로드 테스크를 기반으로 요청하게 된다!
      • streamTask
        • 소켓 관련 통신을 할 때 사용한다.
  3. Request
    • 네트워크 요청에 대한 정보를 표현하는 객체를 말하며, HTTP Header와 그 정책들을 캡슐화하여 요청하며, 어떤 데이터를 요청하는지에 대한 정보가 담겨있다.
    • 네트워크에 정보를 요청하기 위해서는 URLSession이 필요하다! (request 하나만으로는 요청 불가)
  4. Response
    • URL 데이터 요청의 응답에 대한 데이터를 말하며, 이 데이터를 처리할 수 있는 방법은 두 가지가 있다.
      • Completion Handler
        • Task가 종료된 시점에 실행되어 응답을 받을 수 있는 클로저를 말한다.
        • 요청에 성공했을 때 response를, 실패했을 때 error값을 전달받는다. 
          • 따라서, 중간 단계에서 얼마나 진행되고 있는지를 알 수 없다.
      • Session Delegate
        • Task가 실행되는 동안 발생할 수 있는 상황에 세부적인 처리가 필요할 때 사용된다.
        • 진행도나 큰 사진을 불러올 때, 그 진행도를 표시해 주기 위해서는 SessionDelegate를 사용해야 한다.
          • 응답을 요청이 끝났을 때, 한 번 받는 CompletionHandler와 달리 서버로부터 최초 응답을 받은 이후로 서버로부터 데이터를 받을 때마다 다 받은 시점까지 원하는대로 이벤트 처리가 가능하다.

URLSessionDelegate의 경우 이런 프로토콜로 구성되어 있어 사용할 경우 채택해 주어야 한다 ^_^ ...

 

 

 

아래는 간단한 예시이다!

    func callRequest() {
        // 첫 번째 필수 Method url / 두 번째 캐시 정책 / 세 번째 타임아웃인터벌(시간 설정)
        // 네트워크 통신에서 응답이 너무 안 오면 사용자는 기다릴 수 없음 ㅡㅡ
        // 걍 n초만 기다리다가 안 오면 다시 시도해달라고 하자!! => 기본으로 60초 가지고 있지만 보통 3~5초 정도로 사용함
        guard let url = URL(string: "https://www.dhlottery.co.kr/common.do?method=getLottoNumber&drwNo=1079") else { return }
        let request = URLRequest(url: url, timeInterval: 5)
        // 5초 뒤에도 통신 안 오면 걍 안 온 걸루 처리함 ㅡㅡ
        
        // 요청을 보내는 게 앞에 시작!!
        // 어디를 보낼지에 대한 게 첫 번째 매개변수로 실행댐
        // 응답 받고 나면 클로저 실행!!
        // 클로저의 첫 번째 매개변수는 data 형식으로 매개변수 반환된다.
        // 클로저 내부에서 받는 매개변수들은 모두 옵셔널로 정의되어 있는데
        // data가 정상적으로 오게 된다면 error에서는 nil이, 반대라면 data와 response에서 nil이 나오기 때문이당.
        URLSession.shared.dataTask(with: request) { data, response, error in
            
            guard let data else { return }
            let value = String(data: data, encoding: .utf8)
            
            // json 형태로 나오게 댐
            
            print(value)
            print(data)
            // urlResponse를 알려주는 거잉
            print(response)
            print(error)
        }.resume() // 네트워크 통신 시작!
        
    }
}

completionHandler를 이용한 예시이다.

 

 

 

 

 

extension URLSessionViewController: URLSessionDataDelegate {
    
    // 서버에서 최초로 응답받은 경우에 호출(상태코드 처리)
    // 200~500까지 상태코드를 처리했던 것처럼 처리하는 게 필요함!!
    func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse) async -> URLSession.ResponseDisposition {
        // response가 잘 왔는지 체크하고 상태코드 구성!!
        print("RESPONSE", response)
        if let response = response as? HTTPURLResponse, (200...500).contains(response.statusCode) {
            
            // 이 헤더키에 있는 값을 가져오는 거잉
            total = Double(response.value(forHTTPHeaderField: "Content-Length")!)!
            
            // 이후 method들이 실행되도록 허락~
            return .allow
        } else {
            // 이후 서버에서 데이터를 호출하는 경우에 대해서 미리 처리해 주는 거임
            // 응답이 이상하게 됐을 때 이후 반복적으로 호출하거나 호출이 완료되었을 때 method가 실행되지 않도록!!
            return .cancel
        }
        
    }
    
    
    // 서버에서 데이터 받을 때마다 반복적으로 호출!!
    func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
        print("Data:", data)
        
        // buffer가 nil이면 append 구문 자체가 실행이 안 될 수 있음!!
        buffer?.append(data)
    }
    
    
    // 서버에서 응답이 완료된 이후에 호출
    func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
        print("End")
        if let error {
            print(error)
        } else {
            
            guard let buffer = buffer else {
                print(error)
                return
            }
            
            imageView.image = UIImage(data: buffer)
                
        }
    }
}

위 코드는 이런 식으루 viewDidLoad에서 호출한다!

 

 

 

 

class ViewController: UIViewController {

    var session: URLSession!
    
    var total: Double = 0
    
    var buffer: Data? {
        // 프로퍼티 옵저버!!
        didSet {
            let result = Double(buffer?.count ?? 0) // total
            if total == 0 {
                progressLabel.text = "0%"
            } else {
                progressLabel.text = "\(Int(result / total * 100))%"
            }
            print(result, total)
        }
    }
    
    ... ...


    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        view.backgroundColor = .white
        // buffer?.append(data)가 실행되기 위함!!
        // buffer가 nil이면 append가 실행되지 않으니까!
        buffer = Data()
     
        
        guard let url = URL(string: "https://apod.nasa.gov/apod/image/2308/M66_JwstTomlinson_3521.jpg") else { return }
        
        // delegateQueue => delegateMethod 실행은 main에서 할 거라고 말하고 잇음 ..
        // 만약에 이걸 global로 만들어놨다고 한다면 view 업데이트의 경우에는 메인에서 하는 걸 추가해야 했을 텐데 요기서는 지금 main에서 하고 잇음!!
        session = URLSession(configuration: .default, delegate: self, delegateQueue: .main)
        
        // 시작점과 끝점을 알 수 있도록!!
        session.dataTask(with: url!).resume()
    }
    
    ... ...
}

 

드문드문 잘라서 올린 거라 (...) 코드적으로 도움은 되지 않겠지만 ^_ ㅠ 일단 주석은 열심히 달아보았다

 

추후 괜찮은 예시가 생긴다면 깃헙 링크를 추가하도록 하겠다~~!

 

 

 

 

 

 

 

 

https://developer.apple.com/documentation/foundation/url_loading_system

 

URL Loading System | Apple Developer Documentation

Interact with URLs and communicate with servers using standard Internet protocols.

developer.apple.com

https://developer.apple.com/documentation/foundation/urlsession

 

URLSession | Apple Developer Documentation

An object that coordinates a group of related, network data transfer tasks.

developer.apple.com

https://developer.apple.com/documentation/foundation/urlrequest

 

URLRequest | Apple Developer Documentation

A URL load request that is independent of protocol or URL scheme.

developer.apple.com

https://developer.apple.com/documentation/foundation/urlsessiontask

 

URLSessionTask | Apple Developer Documentation

A task, like downloading a specific resource, performed in a URL session.

developer.apple.com

https://developer.apple.com/documentation/foundation/urlrequest

 

URLRequest | Apple Developer Documentation

A URL load request that is independent of protocol or URL scheme.

developer.apple.com

https://developer.apple.com/documentation/foundation/urlresponse

 

URLResponse | Apple Developer Documentation

The metadata associated with the response to a URL load request, independent of protocol and URL scheme.

developer.apple.com

 

'iOS > App' 카테고리의 다른 글

[iOS] NavigationBar BackgroundColor 노치까지 채우기  (0) 2023.12.02
[RxSwift] Single  (1) 2023.11.21
[iOS] Push Notification 보내기  (1) 2023.11.13
[CoreLocation] Location  (1) 2023.08.28
[iOS] Notification  (2) 2023.08.21