URLSession
-iOS 앱에서 서버와 통신하기 위해 애플에서 지원하는 API다.
-HTTP / HTTPS 기반 요청을 처리하기 위한 클래스 및 클래스의 세트 모음이다.
-유명한 통신 라이브러리인 Alamofire나 SDWebImage 등이 모두 URLSession을 기반으로 하고 있다.
-여러 가지 프로토콜을 지원하고, 인증, 쿠키 관리, 캐시 관리 등을 지원하고 있다.
URLSession
-URLSession Task 인스턴스를 만들고 관리하는 기본 클래스.
-URLSession Configuration 개체를 제공하여 URLSession 인스턴스를 만들 수 있다.
-Request와 Response를 가지고 있다.
Request
1. URL 객체를 통해 직접 통신하는 형태,
2. URL Request 객체를 만들어서 옵션을 설정하여 통신하는 형태
서버로 요청을 보낼 때 어떻게 데이터를 캐싱할 것인지, 어떤 HTTP 메서드를 사용할 것인지, 어떤 내용을 전송할 것인지 등을 설정할 수 있다.
Response
1. 설정된 Task의 Completion Handler 형태로 response를 받기
2. URLSessionDelegate를 통해 지정된 메서드를 호출하는 형태로 response를 받기
Response에서는 일반적으로 1번을 사용한다.
하지만 앱이 백그라운드 상태로 들어갈 때에도 파일 다운로드를 지원해야 하거나, 인증과 캐싱을 default 옵션으로 사용하지 않는 상황 등의 경우에는 2번을 사용한다.
URLSession Configuration
-이 클래스는 URLSession의 동작을 정의한다. 이를 통해 캐싱, 시간 제한 간격 및 추가 헤더와 같은 다양한 설정을 구성할 수 있다. 구성에는 3가지 유형이 있음.
1. .default : 기본적인 Session으로 디스크 기반 캐싱을 지원한다. (캐싱, 쿠키 및 자격 증명에 대한 기본 시스템 설정을 사용하는 표준 세션 구성)
2. .ephemeral : 어떤 데이터도 저장하지 않는 형태의 세션이다. (기본 구성과 유사하지만 캐시, 쿠키 또는 자격 증명을 위한 영구 저장소가 없음.)
임시 네트워크 활동과 같이 디스크에 정보를 저장하지 않는 네트워크 작업을 수행하려는 경우에 유용하다.
3. .background : 앱이 백그라운드 상태로 들어 간 이후에도 통신하는 것을 지원하는 세션이다.
대용량 파일 다운로드 또는 업로드와 같이 앱이 종료되거나, 앱이 다시 시작된 경우에도 작업이 계속되어야 하는 장기 실행작업에 이상적이다.
-상황에 맞게 URLSessionConfiguration의 속성을 정의해서 네트워크 작업을 보다 쉽게 관리하고, 최적화할 수 있음.
URLSession Task
-URL에서 수행할 수 있는 작업 객체(task object)를 나타내는 추상 클래스 이다.
-Task 객체는 일반적으로 Session 객체가 서버로 요청을 보낸 후, 응답을 받을 때 URL 기반의 내용들을 받는 역할을 한다.
-3가지 유형의 Task가 지원된다.
1. URLSessionDataTask
-Data 객체를 통해 데이터를 주고 받는 Task이다.
-HTTP GET 또는 POST 메서드를 사용하여 원격 서버에서 데이터를 검색하는데 사용된다.
-데이터 작업은 서버의 응답을 메모리의 Data 개체로 반환한다.
-일반적으로 JSON 또는 XML과 같은 소량의 데이터에 대해 원격 서버에서 데이터를 가져오는 데 사용한다.
2. URLSessionDownloadTask
-원격 서버에서 데이터를 다운로드하고, 로컬 파일에 저장하는 데 사용된다.
-Data를 파일 형태로 전환 후, 다운 받는 Task이다.(백그라운드 다운로드 지원)
임시 파일 위치로 원격 서버에서 파일을 다운로드할 때 사용한다.
3. URLSessionUploadTask
-일반적으로 POST 또는 PUT 메서드를 사용하여 원격 서버에 데이터를 업로드하는 데 사용된다.
-Data를 파일의 형태로 전환 후, 업로드 하는 Task이다.
업로드 작업은 메모리 또는 파일에서 데이터를 업로드 할 수 있다.
URLSessionTask는 일시정지, 다시시작, 취소 등의 기능을 할 수 있다.
URLSession Delegate
-URLSession은 데이터 수신, 인증, 처리, 작업 진행률 모니터링 등 네트워크 작업 수명주기에서 다양한 상황을 처리할 수 있는 delegate 프로토콜을 지원한다.
URLSession Delegate, URLSessionTaskDelegate, URLSessionDataDelegate, URLSessionDownloadDelegate가 포함된다.
-URLSessionDelegate를 통해 작업할 수 있는 예.
1. 서버와의 SSL 인증을 수행하거나, 보안을 강화하기 위해 Certificate pinning을 수행할 수 있음.
2. 서버로부터 redirect 응답을 받았을 때의 처리 방식을 정의할 수 있음.
3. 파일의 다운로드 작업이 완료되었을 때의 동작을 정의할 수 있음.
URLSession의 Life Cycle
1. URLSession Configuration 객체를 만들고, 필요한 상황에 맞게 속성을 구성한다.
2. 구성한 개체를 사용하여 URLSession 인스턴스를 만들고, 필요한 경우 다양한 이벤트를 처리할 수 있는 delegate를 채택한다.
3. 통신 할 URL과 Request 객체를 설정한다. (요청에 대한 추가 매개변수 또는 헤더를 만들어 줄 수 있음.)
4. URLRequest 및 URLSession 인스턴스를 통해 사용 할 Task를 결정하고, 그에 맞는 Completion Handler나 Delegate 메서드를 작성한다.
5. 해당 Task 실행 (.resume)
6. Task 완료 후 Completion Handler가 실행된다.
URLSession을 통한 네트워크 요청 관리
1. Request 생성
-URLSession은 URLRequest객체를 사용하여 네트워크 요청을 생성한다.
URLRequest는 요청할 URL, HTTP메서드, 헤더 등의 정보를 포함할 수 있다.
2. Request 실행
-생성된 URLRequest은 URLSession에 의해서 실행된다.
URLSession은 URLSessionTask를 생성하고 이 Task를 통해 네트워크 요청을 실행한다.
3. Request 상태 관리
-URLSessionTask는 네트워크 요청의 상태를 관리할 수 있다.
Request의 일시 중지, 다시 시작, 취소하는 기능을 제공한다.
4. 응답 처리
-URLSessionTask는 네트워크 요청의 결과를 처리하는 역할도 수행한다.
서버로부터 응답을 받았을 때 응답 데이터, 에러, 응답코드 등을 제공한다.
네트워크 요청의 성능을 최적화시키는 방법
1. 병렬 요청 처리(여러 작업을 동시에 수행하는 것. 멀티쓰레딩 등의 기술을 통해 가능함.)
URLSession을 사용하면 여러 개의 Request를 동시에 보낼 수 있다.
이는 URLSessionTask의 독립성 덕분이다. 각 Task는 별도의 네트워크 연결을 사용하여 요청을 처리한다.
이를 통해 여러 개의 작업을 병렬로 실행하여 전체 응답 시간을 줄일 수 있다.
ex) 여러 개의 이미지를 다운로드해야 하는 상황에서 각 이미지를 순차적으로 다운로드 받는다면 한 이미지를 받아올 때까지 다음 이미지의 다운로드 작업은 기다려야겠죠? 하지만 별도의 Task로 만들어 병렬처리를 한다면 전체적인 다운로드 시간을 줄일 수 있다!
2. 데이터 캐싱
URLSession은 HTTP 요청에 대한 응답을 캐싱하는 기능을 제공한다.
이전에 다운로드한 데이터를 캐싱을 통해 재사용함으로써 같은 요청에 대한 반복적인 네트워크 호출을 줄일 수 있다.
이를 위해 URLSession은 URLCashe 객체를 사용한다. URLCashe는 HTTP, HTTPS 요청에 대한 응답을 캐시에 저장하고 관리하는 역할을 한다. 요청이 발생하면, URLCashe는 캐시를 검사하여 캐시된 응답이 있는지 확인하고, 유효한 응답이 존재한다면 네트워크 요청을 수행하지 않는다. 이 과정을 통해 성능을 향상시킬 수 있는 것이다.
URLSession은 기본적으로 캐싱동작을 수행하지만, 원하는 캐싱동작을 수행하고 싶다면 URLSessionConfiguration 객체를 사용해서 캐싱 동작을 설정할 수 있다.(필요할 때 알아봐야지..)
3. 우선 순위 설정
URLSessionTask의 priority 속성을 사용해서 요청의 우선 순위를 지정할 수 있다.
기본적으로 모든 URLSessionTask의 우선 순위는 0.5로 되어있다. 이 값을 변경하여 해당 작업을 다른 작업보다 더 빨리 처리할 수 있다.
이런 기능은 네트워크 요청을 세밀하게 제어하여 네트워크 요청의 성능을 개선하는데 도움을 주겠죠??
이제 직접 구현해보자.
"https://jsonplaceholder.typicode.com/posts/1"
위 url은 json 샘플 데이터를 제공하는 사이트다.
위 JSON데이터를 가져와보자.
먼저 데이터 모델 만들기.
1. URLSession Configuration 개체를 만들고, 필요한 상황에 맞게 속성을 구성한다.
let configuration = URLSessionConfiguration.default
URLSession Configuration은 3가지 유형이 있다고 했져?
기본인 default 유형을 사용해봅시다.
2. 구성한 개체를 사용하여 URLSession 인스턴스를 만들고, 필요한 경우 다양한 이벤트를 처리할 수 있는 delegate를 채택한다.
let configuration = URLSessionConfiguration.default
let session = URLSession(configuration: configuration)
3. 통신 할 URL과 Request 객체를 설정한다. (요청에 대한 추가 매개변수 또는 헤더를 만들어 줄 수 있음.)
let url = URL(string: "https://jsonplaceholder.typicode.com/posts/1")!
let request = URLRequest(url: url)
request는 따로 설정이 없으면 HTTP의 GET 메서드를 default로 사용한다.
4. URLRequest 및 URLSession 인스턴스를 통해 사용 할 Task를 결정하고, 그에 맞는 Completion Handler나 Delegate 메서드를 작성한다.
let task = session.dataTask(with: request) { data, response, error in
//Error 다루기
if let error = error {
completion(.failure(error))
return
}
//URLResponse 다루기
//NSError - 사용자 지정 오류 개체를 만들어서 보다 구체적인 오류 정보를 제공하는데 사용.
//domain - 일반적으로 오류를 분류하려면 고유 식별자를 제공해야 함. 오류를 처리할 때 원인 등을 식별하는데 도움이 된다.
//code - 특정 오류를 나타내기 위해 오류 코드가 제공된다.
//userInfo - 오류에 대한 추가 정보가 들어 있는 dictionary
guard let httpResponse = response as? HTTPURLResponse else{
let error = NSError(domain: "", code: -2, userInfo: [NSLocalizedDescriptionKey : "invalid response"])
completion(.failure(error))
return
}
//상태코드 확인
//요청이 성공했는지 상태코드(200)를 확인.
let statusCode = httpResponse.statusCode
if statusCode != 200 {
let error = NSError(domain: "", code: statusCode, userInfo: [NSLocalizedDescriptionKey : "Request failed with status code : \(statusCode)"])
completion(.failure(error))
return
}
//Data 다루기
guard let data = data else{
let error = NSError(domain: "", code: -3, userInfo: [NSLocalizedDescriptionKey : "Data Not Found"])
completion(.failure(error))
return
}
do {
let post = try JSONDecoder().decode(Post.self, from: data)
completion(.success(post))
}catch {
completion(.failure(error))
}
}
URLSessionDataTask는 GET이나 POST 메서드를 가지고 원격 서버와 통신할 때 좋고, 일반적으로 JSON같은 소량의 데이터를 가져오는데 사용된다고 했죠?
그래서 URLSession 인스턴스인 session을 통해 URLSessionDataTask를 만들어주었다.
dataTask 메소드의 정의를 보자.
1. request: URLRequest
URL 및 HTTP 메서드, 헤더 및 본문과 같은 기타 요청 속성을 포함하는 URLRequest 개체를 받음.
2. completionHandler
작업이 완료되면 호출되는 메서드로 매개변수로 Data, URLResponse, Error를 받고 있다.
Data? - 서버에 요청한 데이터를 가져옴. 에러가 발생하거나 사용할 수 있는 데이터가 없는 경우 nil을 반환.
URLResponse? - 상태 코드, 헤더 등과 같은 HTTP 응답에 대한 메타데이터를 포함하는 개체.
Error? - 요청 중에 발생하는 네트워크 또는 구문 분석 에러를 설명해주는 개체.
따라서 우리가 task를 실행하면 네트워킹을 하고 난 후 응답받는 결과에 맞게 completionHandler 내부 코드가 실행된다.
5. 해당 Task 실행 (.resume)
task.resume()
resume()은 일시 중단된 URLSession 작업을 다시 시작하거나, 작업을 시작할 때 사용되는 메서드다.
URLSession의 컨텍스트에서 작업은 기본적으로 일시 중단된 상태로 만들어진다. 즉, URLSession을 사용하여 데이터 작업을 만들거나, 작업을 다운로드하거나, 업로드할 때 자동으로 시작되는 것이 아니라 resume() 메서드를 통해 작업을 시작하라고 알려줘야 함.
6. Task 완료 후 Completion Handler가 실행된다.
fetchPosts { result in
switch result {
case .success(let post):
print("userId : \(post.userId), id : \(post.id), title : \(post.title), body : \(post.body),")
case .failure(let error):
print("Error : \(error.localizedDescription)")
}
}
위 코드와 같이 fetchPosts() 를 실행시킬 때, 전달인자로 클로저를 넣어준다.
데이터가 모두 처리되면 그 결과물을 가지고 클로저 내부 식을 실행시킴.
결과는?
전체코드
struct Post : Codable {
let userId : Int
let id : Int
let title : String
let body : String
}
func fetchPosts(completion : @escaping (Result<Post, Error>) -> Void) {
guard let url = URL(string: "https://jsonplaceholder.typicode.com/posts/1") else {
let error = NSError(domain: "", code: -1, userInfo: [NSLocalizedDescriptionKey : "invalid URL"])
completion(.failure(error))
return
}
let request = URLRequest(url: url)
let configuration = URLSessionConfiguration.default
let session = URLSession(configuration: configuration)
let task = session.dataTask(with: request) { data, response, error in
//Error 다루기
if let error = error {
completion(.failure(error))
return
}
//URLResponse 다루기
guard let httpResponse = response as? HTTPURLResponse else{
let error = NSError(domain: "", code: -2, userInfo: [NSLocalizedDescriptionKey : "invalid response"])
completion(.failure(error))
return
}
//상태코드 확인
//요청이 성공했는지 상태코드(200)를 확인.
let statusCode = httpResponse.statusCode
if statusCode != 200 {
let error = NSError(domain: "", code: statusCode, userInfo: [NSLocalizedDescriptionKey : "Request failed with status code : \(statusCode)"])
completion(.failure(error))
return
}
//Data 다루기
guard let data = data else{
let error = NSError(domain: "", code: -3, userInfo: [NSLocalizedDescriptionKey : "Data Not Found"])
completion(.failure(error))
return
}
do {
let post = try JSONDecoder().decode(Post.self, from: data)
completion(.success(post))
}catch {
completion(.failure(error))
}
}
task.resume()
}
fetchPosts { result in
switch result {
case .success(let post):
print("userId : \(post.userId), \nid : \(post.id), \ntitle : \(post.title), \nbody : \(post.body),")
case .failure(let error):
print("Error : \(error.localizedDescription)")
}
}
출처:
iOS URLSession 이해하기 - Eth Dev Post (hcn1519.github.io)
Swift, URLSession가 무엇인지, 어떻게 사용하는지 알아봅니다. - Home (devmjun.github.io)
'개린이 이야기' 카테고리의 다른 글
With Calendar 프로젝트를 하면서 (0) | 2023.01.09 |
---|---|
Local Notification(로컬 푸쉬 알림) (0) | 2022.12.07 |
함수형 프로그래밍에 관하여 (0) | 2022.10.09 |
ARC(Auto Reference Counting)에 관하여 (0) | 2022.09.27 |
메모리에 관하여 (0) | 2022.09.25 |