[iOS] 네트워크 통신1 - URLSession

LEEHAKJIN-VV·2022년 7월 13일
0

iOS 정리

목록 보기
6/7

본 글은 네트워크 통신을 위한 방법 중 URLSession api를 이용한 네트워크 통신을 설명합니다. 전송받은 데이터를 Decode하는 방법은 이 글에서 다루지 않으니, 해당 내용이 궁금하신 분들은 다음 글을 참고하시길 바랍니다.
잘못된 내용은 댓글로 알려주시면 감사하겠습니다.😎


네트워크 통신에 사용되는 API는 URLSession, Alamofire, Moya등이 있다고 합니다. URLsession api가 제일 처음 나왔고 다른 api들의 base가 되기 때문에 알아두는 것이 좋다고 생각하여 글을 작성하게 되었습니다.

URLSession

URLSession 클래스와 관련 클래스는 url로 표시되는 endpoint로 부터 데이터를 다운로드하거나 endpoint로 업로드를 수행하는 API라고 한다. 또한 이 API를 이용하여 iOS에서 앱이 작동하지 않을때 백그라운드 다운로드를 수행할 수 있다.

URLSession클래스를 네트워크 통신에 사용할 때, data task를 전반적으로 관리하는 객체라고 생각하자.

  1. URLsession의 session 종류

    • default session: shared session과 매우 비슷하지만 configuration할 수 있으며 delegate 할당 가능
    • Ephemeral session: shared session과 비슷하지만, cash, cookies 또는 자격 증명을 디스크에 쓰지 않음
    • Background session: 백그라운드 상태에서 content 업로드나 다운로드를 수행하는 session
  2. URLSession의 task의 종류

    • Data task: NSData객체를 사용하여 데이터를 보내고 받는 task
    • Upload task: data task와 유사하나, 파일 형식 데이터 전송을 지원하며, 백그라운드에서 업로드가 가능한 task
    • Donwload task: 파일 형식 데이터 전송을 지원하며, 백그라운드 상태에서 업로드와 다운로드가 가능한 task

해당 클래스에 관한 더 자세한 내용은 공식문서를 참고하면 된다.

이제 URLSession을 이용하여 네트워크와 연결하는 방법을 알아보자.


네트워크 연결

해당 챕터의 내용은 공식문서를 기반으로 하여 설명하니 문서를 참고하는 것을 권장드립니다.

네트워크로 부터 데이터 가져오기

iOS와 네트워크 사이의 통신에서 단순히 데이터를 전송하는 경우에는 파일 시스템에 데이터를 직접 저장하는 URLSessionDownloadTask 클래스 대신 메모리로 수신할 수 있는 URLSessionDataTask를 사용하는 것이 좋다고 한다.

URLSessionDataTask란?
서버로부터 다운로드한 데이터를 NSData 클래스의 타입으로 메모리에 반환하는 클래스로, URLSessionTask 클래스를 상속함.

URLSession을 이용한 네트워크의 통신방법에는 크게 2가지 방법이 있다. 두 방법 모두 URLSession 인스턴스를 이용하여 데이터 전송을 위한 data task를 만들어야 한다.

첫 번째는 간단한 작업을 하는 경우로 이때는 URLSession 클래스의 shared 인스턴스를 사용하는 것이 좋다.

URLSession 클래스의 shared 인스턴스

두 번째로 간단한 작업이 아닌 delegate callback을 이용하여 다양한 기능이 필요로 하는 데이터 전송을 수행하는 경우에는 shared 인스턴스를 사용하는 것이 아니라, 직접 session을 생성해야 한다. session을 생성할 때는, URLSessionConfiguration인스턴스를 사용해야 하며, URLSessionDelegate 프로토콜 및 서브 프로토콜을 구현하는 클래스를 생성해야 한다.

Session 생성할 때 유의할 점!
불필요하게 session을 생성하면 안 된다. 앱에서 유사한 session들이 필요한 경우 하나의 session을 만들고 이를 공유하여 사용하도록 설계하는 것이 좋다.

dataTask()메소드 중 하나를 사용하여 data task를 생성하면 처음에는 suspended 상태가 된다. 그러므로 task를 시작하는 resume() 메소드를 명시적으로 호출해야 한다.

아래에는 서버의 전송 결과를 받는 두 가지 방법을 사용하여 실습한다.


Completion Handler를 이용한 데이터 결과 받기

URLSession을 이용하여 서버로부터 데이터를 가져오는 방법 중 하나인 Completion Handler를 사용하는 방법은 언제 사용할까??

바로 특별한 작업 없이 간단히 데이터만 가져오는 경우에 사용하는 데, completion handler를 사용하는 data task를 생성하는 것이다.

그러면 task는 서버의 response, data, 발생한 error를 completion handler에 전달하게 된다. 아래는 completion handler를 이용하여 전송 결과를 받는 모습이다.

completion handler가 task로부터 데이터를 받는 과정

completion handler를 사용하여 data task를 생성하기 위해서는 URLSession클래스의 dataTask 메소드를 사용해야 한다. 해당 메소드의 completion handler는 아래 3가지 작업을 수행해야 한다.

  1. error 파라미터를 확인한다. 파라미터가 nil이면 전송이 성공적으로 수행되었다는 의미이다.

  2. response파라미터의 status code를 확인하여 서버와의 연결이 성공인지 확인하고, MIME 타입을 확인하여 전송받은 문서가 원래 예측된 문서 형식인지 확인한다.

  3. 1번과 2번을 확인하여 모두 정상인 경우에는 서버에서 전송받은 데이터가 담긴 data 파라미터를 사용하면 된다.


네트워크 통신을 하는 도중 아래 오류 문구를 보게 된다면 해결을 위해 여기를 참고 해주세요.
App Transport Security has blocked a cleartext HTTP connection since it is insecure. Use HTTPS instead or add Exception Domains to your app's Info.plist.

해당 방법을 코드로 직접 구현해 보자. (코드를 작성하는 파일은 ListViewController.swift이다.)

우선 데이터를 가져오는 함수 starLoad()ListViewController 클래스에 정의한다. 다음 글에서 전송받은 데이터를 테이블 뷰에 표시할 예정이기 때문에, viewDidLoad() 메소드에서 호출한다.

다음 startLoad() 메소드를 아래와 같이 구현한다.

코드에서 사용하는 url은 꼼꼼한 재은 씨의 Swift 기본편에서 제공되며 영화 정보를 제공하는 url입니다.

코드에서 shared 인스턴스의 dataTask(with:)메소드를 사용하여 completion handler에 서버로부터 전달받은 결과를 전달할 data task를 생성한다. dataTask(with:)메소드의 반환 값을 task 프로퍼티에 할당한다.

이제completion handler에 통신의 결과가 할당되었으므로 handler의 3개의 프로퍼티의 값을 확인하자. 우선 error 프로퍼티에 nil값이 할당되었는지 확인한다. 만약 nil값이 아닌 경우에는 client 에러가 발생했으므로 적절한 처리를 수행한다.

다음으로 response 프로퍼티는 HTTP 헤더status code와 같이 reponse의 메타데이터이다. HTTP/HTTPS 통신을 하는 경우, 실제로 반환받는 객체의 타입은 HTTPURLResponse타입이라고 한다.

httpResponse 프로퍼티의 statusCode를 사용하여 서버와의 전송 성공 여부를 확인할 수 있다. statusCode 프로퍼티는 reponse의 상태 코드로 특정 HTTP 요청이 성공적으로 완료되었는지 알려주는 값이다. 이 값이 200~299에 속한다는 의미는 정상적으로 연결되었다는 의미이다. 이는 RFC에 자세히 정의되어 있으므로 궁금하신 분들은 링크에서 참고하시길 바랍니다.

RFC2616의 HTTP/HTTPS 통신의 statusCode 정보

다음으로 MIME타입을 확인한다.

MIME 타입이란
클라이언트에게 전송된 문서의 다양성을 알려주는 메커니즘으로 파일의 확장자를 의미한다. 이 타입을 이용하여 전송받는 문서의 종류를 알고, 다음 동작을 정의하기 위해 사용한다. (MIME 타입 전체 목록)

우리는 서버에서 json파일을 내려받을 예정이므로 application/json으로 미리 정의한다. 만약 MIME타입이 일치한다면, 네트워크와의 연결이 정상적으로 수행되었으므로, 서버에서 전송받은 데이터를 전달받은 data 프로퍼티를 사용하면 된다. 현재 예제에서는 단순히 Log를 출력하여 값을 확인한다.


Delegate를 이용하여 결과받기

completion handler를 이용하는 대신 session에 delegate를 설정하여 전송 결과를 받을 수 있다.

그러면 어떤 상황에서 session에 delegate패턴을 적용해야 할까??

위에서 언급했듯이 캐싱과 인증 등 다양한 기능이 필요한 경우 delegate 패턴을 적용해야 한다.

아래는 delegate를 구현하여 task 로부터 전송 결과를 받는 과정의 모습이다.

데이터 전송을 수행할 클래스가 구현해야 할 프로토콜의 종류는 다음과 같다.

  • URLSessionDelegte: session Life cycle와 같은 session-level의 이벤트를 처리하는 프로토콜

  • URLSessionTaskDelegte: task-level의 이벤트를 처리하는 프로토콜

  • URLSessionDataDelegte: 데이터 업로드 task와 같은 특정 task-level의 이벤트를 처리하는 프로토콜

  • URLSessionDownloadDelegte: 다운로드 task를 수행하는 프로토콜

현 예제에서는 아래와 같이 3개의 프로토콜만 구현한다.

delegate 패턴을 적용하는 경우 URLSession 인스턴스를 만들어야 한다고 앞에서 언급한 적 있다. 그러므로 URLsession클래스의 인스턴스 session을 생성한다. delegate는 현재 작업 중인 클래스 ListViewController로 지정한다.

이니셜라이저에 사용되는 프로퍼티에 대한 설명을 하면..

  • configuration: 캐싱 정책, timeouts, 프록시, 파이프라이닝, tls 지원 버전, 쿠키 정책, 자격 증명 저장소와 같은 동작 설정

  • delegate: 인증 및 기타 세션 관련 이벤트에 대한 요청을 처리하는 delegate, nil인 경우 오직 completion handlers만 사용

  • queue: completion handlersdelegate call의 operation scheduling(작업 스케줄링) 큐로, serial(직렬) 큐이다. nil인 경우 직렬 큐로 생성

다음으로 이전에 작성하였던 startLoad()함수를 아래와 같이 수정해야 한다. 동일하게 dataTask(with:) 메소드를 호출하기 때문에 URL를 만들며, 버퍼 역할을 할 recivedData 프로퍼티를 초기화한다. 다음 dataTask(with:)를 호출하여 task를 생성하고, 생성한 taskresume() 메소드로 시작한다.

이제 아래에 구현한 프로토콜의 메소드를 아래와 같이 구현한다.

각 메소드에 대한 설명은 다음과 같다.

urlSession(_:dataTask:didReceive:completionHandler:) 메소드

이 메소드는 URLSessionDataDelegate프로토콜의 메소드로 서버로부터 초기의 응답 헤더를 수신했을 때 호출된다. resonseHTTP status codemimeType을 확인하여 서버로부터 올바른 header를 전송받았는지 확인한다. 만약 올바른 데이터가 아닌경우 task를 즉시 취소하고, 성공한 경우에는 task를 계속해서 실행한다.

task 작업 지속 여부는 completionHandler의 인자에 전달하는 값으로 결정된다. 성공인 경우에는 URLSession.ResponseDisposition.allow 실패인 경우에는 URLSession.ResponseDisposition.cancel을 전달하면 된다.

urlSession(_:dataTask:didReceive:) 메소드

해당 메소드는 URLSessionDataDelegate프로토콜의 메소드로, data task가 data를 받은 경우 호출되는 메소드이다. 전송받은 데이터는 버퍼 역할을 하는 Data타입의 인스턴스에게 추가하면 된다.

urlSession(_:task:didCompleteWithError:) 메소드

마지막 메소드는 URLSessionTaskDelegate 프로토콜 메소드로, 데이터 전송이 완료되었을 때 호출된다. 그러므로 전송 오류가 발생하였는지 확인해야 한다. 그러기 위해서 error 프로퍼티의 값이 nil인지 확인하고, nil이 아니라면 전송받은 데이터가 올바르지 않으므로 사용하지 않고, 반대인 경우면 버퍼(receivedData)에 저장된 데이터를 사용하면 된다.

전송받은 결과


현 실습에서는 단순히 데이터를 출력하지만 다음 글에서는 전송 받은 데이터를 Coadable 프로토콜을 사용하여 테이블 뷰에 직접 표시하는 방법에 대해 설명합니다.

URLSession 통신방법이 생각보다 내용이 많아서 많은 문서를 참고했습니다. 특히 글의 대부분이 공식 문서를 기반으로 작성되었기 때문에 URLSession을 이용한 통신의 전반적인 과정을 이해하고 싶다면 아래 참조한 자료 및 관련 자료들을 꼭 읽는 것을 추천드립니다.

참조자료

공식문서

https://developer.apple.com/documentation/foundation/urlsession
https://developer.apple.com/documentation/foundation/url_loading_system
https://developer.apple.com/documentation/foundation/url_loading_system/fetching_website_data_into_memory
https://developer.apple.com/documentation/foundation/urlsessiondatatask
https://developer.apple.com/documentation/foundation/urlsession/1409000-shared
https://developer.apple.com/documentation/foundation/urlsessiondelegate
https://developer.apple.com/documentation/foundation/urlsessiondatadelegate/1410027-urlsession
https://developer.apple.com/documentation/foundation/urlsessionconfiguration
https://developer.apple.com/documentation/foundation/urlsession/1407613-datatask
https://developer.apple.com/documentation/foundation/httpurlresponse/1415870-init
https://developer.apple.com/documentation/foundation/urlsession/1411597-init

자료

꼼꼼한 재은씨의 Swift 기본편

0개의 댓글