본 글은 네트워크 통신을 위한 방법 중
URLSession
api를 이용한 네트워크 통신을 설명합니다. 전송받은 데이터를 Decode하는 방법은 이 글에서 다루지 않으니, 해당 내용이 궁금하신 분들은 다음 글을 참고하시길 바랍니다.
잘못된 내용은 댓글로 알려주시면 감사하겠습니다.😎
네트워크 통신에 사용되는 API는
URLSession
,Alamofire
,Moya
등이 있다고 합니다.URLsession
api가 제일 처음 나왔고 다른 api들의 base가 되기 때문에 알아두는 것이 좋다고 생각하여 글을 작성하게 되었습니다.
URLSession 클래스와 관련 클래스는 url로 표시되는 endpoint로 부터 데이터를 다운로드하거나 endpoint로 업로드를 수행하는 API라고 한다. 또한 이 API를 이용하여 iOS에서 앱이 작동하지 않을때 백그라운드 다운로드를 수행할 수 있다.
즉 URLSession
클래스를 네트워크 통신에 사용할 때, data task
를 전반적으로 관리하는 객체라고 생각하자.
URLsession
의 session 종류
default session
: shared session
과 매우 비슷하지만 configuration할 수 있으며 delegate 할당 가능Ephemeral session
: shared session
과 비슷하지만, cash, cookies 또는 자격 증명을 디스크에 쓰지 않음Background session
: 백그라운드 상태에서 content 업로드나 다운로드를 수행하는 sessionURLSession
의 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()
메소드를 명시적으로 호출해야 한다.
아래에는 서버의 전송 결과를 받는 두 가지 방법을 사용하여 실습한다.
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가지 작업을 수행해야 한다.
error
파라미터를 확인한다. 파라미터가 nil
이면 전송이 성공적으로 수행되었다는 의미이다.
response
파라미터의 status code
를 확인하여 서버와의 연결이 성공인지 확인하고, MIME
타입을 확인하여 전송받은 문서가 원래 예측된 문서 형식인지 확인한다.
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를 출력하여 값을 확인한다.
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 handlers
와 delegate call
의 operation scheduling(작업 스케줄링) 큐로, serial(직렬) 큐이다. nil
인 경우 직렬 큐로 생성
다음으로 이전에 작성하였던 startLoad()
함수를 아래와 같이 수정해야 한다. 동일하게 dataTask(with:)
메소드를 호출하기 때문에 URL
를 만들며, 버퍼 역할을 할 recivedData
프로퍼티를 초기화한다. 다음 dataTask(with:)
를 호출하여 task
를 생성하고, 생성한 task
를 resume()
메소드로 시작한다.
이제 아래에 구현한 프로토콜의 메소드를 아래와 같이 구현한다.
각 메소드에 대한 설명은 다음과 같다.
urlSession(_:dataTask:didReceive:completionHandler:) 메소드
이 메소드는 URLSessionDataDelegate
프로토콜의 메소드로 서버로부터 초기의 응답 헤더를 수신했을 때 호출된다. resonse
의 HTTP status code
와 mimeType
을 확인하여 서버로부터 올바른 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 기본편