반응형
URL Loading System
- URL을 통해 네트워크에 존재하는 서버와 통신하는 기술입니다.
- 이에 포함된 프레임워크는 높은 수준의 API를 제공하기 때문에 이것만으로 서버연동, 다운로드, 업로드 기능을 구현할 수 있습니다.
URLSession
- 이 시스템에서 가장 중요한 것이 URLSession입니다. 이를 통해 네트워크 연결을 설정하고 요청과 응답을 처리할 수 있습니다.
- URLSession은 네가지 Session 타입을 가집니다.
- Shared : 기본 설정을 기반으로 단순한 네트워크 요청을 수행할 수 있습니다.
- Default : 세션을 직접 구성할 수 있습니다. Degation을 제공하기 때문에 세부적인 제어도 가능합니다. 해당 세션을 통해 전송된 응답은 Disk 및 Memory에 캐싱됩니다.
- Ephemeral : Default와 유사하지만 캐싱처리가 Memory에만 수행됩니다. 주로 private한 처리를 하려할 때 사용됩니다.
- Background : 백그라운드 전송을 구현할 때 사용합니다. 앱 실행상태과 관련없이 데이터를 다운로드하고 업로드할 수 있습니다.
- Shared Session을 제외한 모든 세션은 Session Configuration 객체를 통해 생성합니다.
Task
- URLSession에 전달하는 개별요청
- 데이터 전달방식 및 목적에 따라 4가지 Task 타입을 사용합니다.
- DataTask : API 서버와 통신하기 적합합니다. 가장 많이 사용되는 Task입니다.
- DownloadTask / UploadTask : 파일 전송(다운로드, 업로드)에 적합합니다. (백그라운드 전송 지원)
- StreamTask : 채팅과 같은 TCP 프로그램을 구현할 때 사용하는 Task입니다.
- URLSession.shared.
task()와 같은 형식으로 손쉽게 Task를 생성할 수 있습니다. - 하지만 이는 suspended 상태로 생성되기 때문에 resume 메서드를 호출해야 작업을 시작합니다.
Data Task 과정
- 이제 이들을 활용한 대표적인 처리인 DataTask 과정을 살펴봅시다.
guard let url = URL(string: urlStr) else { fatalError("Invalid URL") }
- 먼저 URL 인스턴스를 생성합니다.
let session = URLSession.shared
- URLSession.shared를 좀 더 사용하기 좋도록 상수에 저장합니다.
let task = session.dataTask(with: url) { (data, response, error) in
if let error = error {
// 통신 중 에러가 발생한 경우 에러 처리
return
}
guard let httpResponse = response as? HTTPURLResponse else {
// HTTPURLResponse로 변환할 수 없는 경우 에러 처리
return
}
guard (200...299).contains(httpResponse.statusCode) else {
// 상태코드가 성공이 아닐 경우 에러 처리
return
}
guard let data = data else {
// 데이터가 nil일 경우 에러 처리
return
}
do {
let decoder = JSONDecoder()
// 디코딩 및 데이터 활용 처리
} catch {
// 디코딩이 성공적으로 수행되지 않았을 경우 에러처리
}
}
- URL을 전달하여 Task를 생성합니다.
- URL이 아닌 URLRequest를 전달할 수도 있는데, 이는 Task 별로 네트워크 설정(캐시 정책 등)을 변경하고 싶을 때 사용합니다.
- completion handler는 URL을 통해 네트워크 작업이 완료되면 호출됩니다. 이를 통해 data, response, error값을 전달받을 수 있습니다.
- data : 서버에서 전달된 Binary Data
- response : 서버 응답에 대한 정보
- error : 에러 발생시 에러 정보
- 이들을 활용해 data를 decoding하고 이를 활용합니다.
- 아직은 task가 suspended 상태입니다.
task.resume()
- task를 실행합니다. (끝)
Session Configuration
- Session Configuration을 통해 네트워크 연결과 관련된 속성을 설정할 수 있습니다.
- 셀룰러 연결 금지, 캐시 저장위치 설정, 쿠기 설정 변경, 타임아웃 값 설정 등을 수행할 수 있습니다.
- URLSession을 생성하기 전에 미리 구성을 완료한 뒤, URLSession 생성자로 전달해 사용할 수 있습니다.
- URLSession이 생성된 후에는 Session Configuration을 수정할 수 없습니다. (Read Only)
- 기본적으로 제공하는 Session Configuration은 총 4가지입니다.
- Shared : shared session에서 사용되는 Configuration으로, 일반적으로 직접 사용하는 경우는 없습니다.
- Default : 기본 Configuration 설정을 기반으로 구성되어 있습니다. delegate를 통한 이벤트 처리 제공하며 disk 캐싱으로 서버 전달 데이터를 전달합니다.
- Ephemeral : Default와 유사하지만 메모리에만 캐싱을 처리한다는 특징이 있습니다.
- Background : Download 및 Upload Task에서 활용합니다. 앱 실행상태와 관계없이 데이터를 전송할 수 있도록 합니다.
let configurationDefault = URLSessionConfiguration.default
let configurationEphemeral = URLSessionConfiguration.ephemeral
let sessionWithDefault = URLSession(configuration: configurationDefault)
let sessionWithEphemeral = URLSession(configuration: configurationEphemeral)
- 이와 같은 방식으로 Configuration을 생성하고 활용합니다.
let configurationBackground = URLSessionConfiguration.background(withIdentifier: "DownTask")
let sessionWithBackground = URLSession(configuration: configurationBackground)
- background Configuration에서는 Identifier 및 Delegate를 필요로 합니다.
- 이 부분은 나중에 다시 공부해서 포스팅하겠습니다!
let configurationCustom = URLSessionConfiguration.default
configurationCustom.timeoutIntervalForRequest = 30
configurationCustom.httpAdditionalHeaders = ["API-VERSION": "2.0.0"]
let sessionWithCustom = URLSession(configuration: configurationCustom)
- 이렇게 생성된 URLSessionConfiguration은 자유롭게 수정이 가능합니다.
- timeout 시간을 설정하는 timeoutIntervalForRequest,
- 모든 task에 추가 헤더를 설정해주는 httpAdditionalHeaders 등 다양한 프로퍼티가 존재합니다.
- 더 많은 프로퍼티는 공식문서를 참고합시다.
URL Session Deleagate
- 서버에서 전달된 데이터는 Completion Handler 혹은 Session Delegate를 기반으로 처리합니다.
- 일반적으로는 Completion Handler를 사용하지만, Session Task 처리 중 내부에서 발생하는 다양한 이벤트를 세부적으로 확인 및 처리하고 싶다면 Session Delegate를 사용해야 합니다.
- Session Delegate를 사용하면 Completion Handler를 통한 처리는 동작하지 않습니다.
- Session Delegate Protocol은 가장 최상위에 URLSessionDelegate가 존재하며
- 그 아래에 이를 상속받는 URLSessionTaskDelegate가 존재하고
- 그 아래에 이를 상속받는 URLSessionDataDelegate와
- URLSessionDownloadDelegate가 존재합니다.
- 그 아래에 이를 상속받는 URLSessionTaskDelegate가 존재하고
- 각 Delegate는 이름에 걸맞는 Delegate Method들을 보유하고 있습니다.
session = URLSession(configuration: URLSessionConfiguration.default, delegate: self, delegateQueue: OperationQueue.main)
let task = session.dataTask(with: url)
task.resume()
- Delegate를 사용하기 위해서는 처음 Session을 생성할 때
URLSession(configuration:, delegate:, delegateQueue:)
메서드를 사용해야합니다. - delegate를 self로 지정했습니다.
- task 처리에는 completion handler가 없는 dataTask 메서드를 사용합니다.
class ViewController: UIViewController {
var buffer: Data?
...
}
extension ViewController: URLSessionDataDelegate {
// 서버로부터 최초로 응답을 받았을 때 호출
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse, completionHandler: @escaping (URLSession.ResponseDisposition) -> Void) {
guard let response = response as? HTTPURLResponse,
(200...299).contains(response.statusCode) else {
// 아닐 경우 cancel 처리
completionHandler(.cancel)
return
}
// 맞을 경우 allow 처리
completionHandler(.allow)
}
// 전체 데이터가 아니기 때문에 이들을 누적하다가 완료되면 파싱처리
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
buffer?.append(data)
}
// 데이터 전송이 완료되면 호출
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
if let error = error {
// 에러 처리
}
// buffer data를 이용해 파싱처리
}
}
- delegate를 self로 지정했기 때문에 이를 구현해주어야합니다.
- 각각 최초로 응답을 받았을 때, 각 데이터가 전송될 때 마다, 데이터 전송이 완료되면 호출되는 메서드입니다.
- 각각 최초로 응답을 받았을 때 : 조건에 따라 작업을 계속할지 말지를 결정해 completionHandler에 전달합니다.
- 각 데이터가 전송될 때 마다 : 버퍼에 데이터를 저장해놓습니다.
- 데이터 전송이 완료되면 : 에러 처리 및 파싱 처리를 수행합니다.
session = URLSession(configuration: URLSessionConfiguration.default, delegate: self, delegateQueue: OperationQueue.main)
buffer = Data()
let task = session.dataTask(with: url)
task.resume()
- 버퍼는 멤버변수로 등록해 계속해서 활용합니다.
- 따라서 task를 실행하기 전에 buffer를 초기화해야합니다.
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
session.finishTasksAndInvalidate() // Task가 모두 종료될 때까지 기다렸다가 리소스 정리
session.invalidateAndCancel() // 모든 Task를 취소하고 바로 리소스 정리
}
- delegate를 사용하면서 ViewController와 URLSession이 서로 순환참조를 일으킬 수 있습니다.
- 따라서 ViewWillDisapear에서 이를 메모리에서 해제해주는 작업이 필요합니다.
- finishTasksAndInvalidate 혹은 invalidateAndCancel를 사용합니다.
POST Request
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
let body = ["email": id, "password": password]
let encoder = JSONEncoder()
request.httpBody = try encoder.encode(body)
- POST 작업에는 URLRequest 객체가 필요합니다.
- httpMethod 속성을 통해 POST Method로 요청 메서드를 변경합니다.
- addValue 속성을 통해 헤더를 추가합니다.
- httpBody 속성을 통해 바디를 추가합니다.
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
...
}
- 이 후의 과정은 GET Requsest와 유사합니다.
- 서버가 보내온 데이터를 필요에 따라 처리하면 됩니다.
반응형
'🍎 Apple > Swift' 카테고리의 다른 글
[Swift] PhotoKit으로 사진앱에서 사진 가져오기 (0) | 2022.05.22 |
---|---|
[Swift] URLSession Cahce Policy (1) | 2022.05.18 |
[Swift] CodingKeys / Custom CodingKeys (0) | 2022.05.12 |
[Swift] JSON Encoding / Decoding (0) | 2022.05.12 |
[Swift] ATS (App Transport Security) (0) | 2022.05.12 |