UIkit Basic 7 (URLSession, Alamofire)
Updated:
🔷 웹 통신과 Protocol
-
인터넷 상에서의 통신을 말합니다
-
많은 정보들이 주고 받기에 인터넷에는 엄격한 규약이 존재하는데 이것을 protocol 이라고 부릅니다.
-
예를들어 메일을 주고 받기 위한 SMTP protocol, 파일전송의 FTP protocol, 브라우저와 웹서버와 통신하기 위해서 HTTP protocol 등이 있습니다.
🔷 HTTP(Hyper Text Transfer Protocol)
-
Hyper Text 전송하기 위한 protocol
-
HTTP 는 요청의 request 와 응답의 response 가 있습니다. Client 에서 server 로 요청하는것이 하면 서버에서 그에 맞는 응답 정보를 보내 줍니다
HTTP 패킷
-
HTTP 응답을 받을 때 그 정보를 패킷에 담겨서 넣는데, 패킷은 header 와 body 로 이루워져 있습니다.
-
Header 에는 주로 보내는 사람, 받는 사람의 주소, 패킷의 lifecycle
-
body 에는 user에서 전하는 실제 내용이 담겨 있습니다
HTTP Method
-
GET: 클라이언트가 서버에 리소스를 요청할 때 사용 (일반적으로 정보를 받아 올때 사용)
-
POST: 클라이언트가 서버의 리소스를 새로 만들때 사용 (로그인, 개시글 정보 등 data 를 담아서 보내야 할때 사용)
-
PUT: 클라이언트가 서버의 리소스를 전체 수정 할 때 사용 (회원정보를 전체 수정할때 주로 사용)
-
PATCH: 클라이언트가 서버의 리스소를 일부 수정 할 때 사용 (회원정보를 일부 수정할때 사용)
-
DELETE: 클라이언트가 서버의 리소스를 삭제 할 때 사용
-
HEAD: 클라이언트가 서버의 정상 작동 여부를 확인 할 때 사용
-
OPTIONS: 클라이언트가 서버에서 해당 URL 이 어떤 메소드를 지원하는지 확인 할 때 사용
-
CONNECT: 클라이언트가 프록시를 통하여 서버와 SSL 통신을 하고자 할 때 사용
-
TRACE: 클라이언트와 서버간 통신 관리 및 디버깅을 할 때 사용
HTTP Status
-
100번 대 Informational: 요청 정보를 처리 중
-
200번 대 Success: 요청을 정상적으로 처리함
-
300번 대 Redirection: 요청을 완료하기 위해 추가 동작 필요
-
400번 대 Client Error: 서버가 요청을 이해하지 못함
-
500번대 Server Error: 서버가 요청 처리를 실패함
🔷 URLSession
-
Apple 에서 HTTP / HTTPS 와 통신하게 만든 것으로 특정한 url을 이용하여 데이터를 다운로드하고 업로드 하기 위한 API 각 Session 내에서 URL 은 요청을 나타 내는 일련의 작업을 추가 합니다. URLSession 은 request 와 response 를 기본 구조를 가지고 있습니다
-
request 는 server로 요청을 보낼때, 어떤 http method 를 사용할 것인지, cashing 정책을 어떻데 할것인지에 대한 설정을 할 수 있습니다.
-
response 는 url 의 요청에 응답을 나타내는 객체 입니다.
URLSessionConfiguration, URLSessionTask
-
URLSessionConfiguration 을 통해서 URLSession 을 생성 할 수 있으며, 생성된 URLSession 은 한개 이상의 URLSessionTask 을 생성할 수 있습니다
-
URLSession 에 담겨있는 URLSessionTask 을 통해서 실제 서버와 통신 할 수 있습니다.
-
URLSession API 는 여러가지 유형의 session 을 제공하는데, URLSessionConfiguration 의 객체에 의해서 session property 를 구성 할 수 있습니다.
URLSession 의 종류
Shared Session (URLSession.shared()) - 공유 세션
- singleton 으로 사용할 수 있으며, 기본요청을 하기 위한 session 입니다. custom 설정은 할 수 없지만, 쉽게 만들어 사용할 수 있습니다
Default Session (URLSession(configuration: .default)) - 기본 세션
-
shared session 과 유사하게 작동 하지만, 직접 원하는 설정을 할 수 있으며, cash, 쿠키 등을 직접 disk 에 저장 할 수 있습니다.
-
순차적으로 data 를 처리하기 위해 delegate 를 지정할 수 있습니다
Ephemeral Session (URLSession(configuration: .ephemeral)) - 임시 세션
- shared session 과 비슷 하지만, cash, 쿠키, 사용자 정보등을 disk 에 저장하지 않습니다. 메모리에 올려서 session 을 연결하고, session 만료시 data 가 사라 집니다
Background Session (URLSession(configuration: .background)) - 백그라운드 세션
- 앱이 실행되지 않는 동안 background 에서 컨텐츠 업로드 및 다운로드를 수행 할 수 있는 session 입니다
URLSessionTask 의 종류
URLSessionDataTask
- data 객체를 사용하여, 데이터를 요청하고 응답 받습니다. 주로 짧고 빈번하게 요청하는 경우에 주로 사용 됩니다
URLSessionUploadTask
- data 객체, 파일 형태의 data 를 업로드 하는 작업을 수행 합니다. App 이 실행되지 않았을 때, background upload 를 지원 합니다.
URLSessionDownloadTask
- data를 받아서 파일 형태로 저장하는 작업을 말합니다. App 이 실행되지 않았을 때, background upload 를 지원 합니다.
URLSessionStreamTask
- TCP/IP 을 연결 할 때 생성해서 사용하는 task
URLSessionWebSocketTask
- WebSocket 표준을 통해 통신하는 task
URLSession Life Cycle
-
Session Configuration 을 결정하고, Session 을 생성
-
통신할 URL 과 Request 객체를 설정
-
사용할 Task 를 결정하고 그에 맞는 Completion Handler 나 Delegate 메소들을 작성
-
해당 Task 를 실행
-
Task 완료 후 Completion Handler 클로저 호출됨
예제 코드
// URLSession 을 이용해서 currentWeather API를 호출하기
func getCurrentWeather(cityName: String) {
guard let url = URL(string: "https://api.openweathermap.org/data/2.5/weather?q=\(cityName)&units=metric&lang=kr&appid=0fb8463dce1de96897cba0b1eff08e18") else { return }
// session 을 default session 으로 설정
let session = URLSession(configuration: .default)
// compression handler 로써 closure 매개 변수에 data(서버에서 응답 받은 data), response(HTTP header 나 상태 코드의 metaData), error(error 코드 반환)
session.dataTask(with: url) { [weak self] data, response, error in
// 응답받은 response (json data)를 weatherInfo struct 에 decoding 되게 하는 logic
let successRange = (200..<300)
guard let data = data, error == nil else { return }
let decorder = JSONDecoder()
// 응답받은 data 의 statusCode 가 200번대 (200 ~ 299) 일때
if let response = response as? HTTPURLResponse, successRange.contains(response.statusCode) {
guard let weatherInfo = try? decorder.decode(WeatherInfo.self, from: data) else { return }
// debugPrint(weatherInfo)
// 받아온 데이터를 UI 에 표시하기 위해서는 main thread 에서 작업을 진행 햐여 됩
DispatchQueue.main.async {
self?.weatherStackView.isHidden = false
self?.configureView(weatherInfo: weatherInfo)
}
} else { // status code 가 200 번대가 아니면 error 상태 이니까 error message 생성 logic
guard let errorMessage = try? decorder.decode(ErrorMessage.self, from: data) else { return }
// debugPrint(errorMessage)
// main thread 에서 alert 이 표시되게 해야됨
DispatchQueue.main.async {
self?.showAlert(message: errorMessage.message)
}
}
}.resume() // app 이 실행되게 함
}
🔷 Alamofire
- Alamofire 는 Swift 기반의 HTTP 네트워킹 라이브러리 입니다. URLSession 을 기반으로 한 라이브러리로서, 네트워킹 작업을 단순히 하고 네트워킹을 위한 다양한 method, json parsing 등을 제공 합니다.
Alamofire 주요 특징
- 연결 가능한 request, response method 를 제공하며, URL json parameter encoding 을 지원 합니다. 파일 데이터 스트리밍, multi part form date 등 upload 기능을 제공하며, HTTP response 검증과 광범위한 Unit Test, 통합 Test 등을 지원합니다
URLSession 대신 Alamofire 을 사용하는 이유
- 코드 간소화, 가독성 측면에서 도움을 주고 여러기능을 직접 구축하지 않아도 쉽게 사용할 수 있음
-
위의 code 는 GET 방식의 코드 작성의 같은 로직인데, URLSession 은 URL 생성, status code error 처리 등, 많은코드를 작성해야 되지만, Alamofire 를 사용하게 되면 더 적은 code 로 똑같은 기능을 구현할 수 있습니다
-
URLSession 은 호출할 API 의 url 을 생성하고, quayParameter 가 있다면 URL 을 mapping 해줘야 하지만, Alamofire는 요청을 생성할때, method parameter 의 url 을 넘겨 주면 내부에서 자동으로 url 의 parameter 를 mapping 시켜 줍니다.
-
유효성 검사의 경우에도, URLSession은 response source 를 URLResponse 로 downCasting 하여 status code property 에 접근해서 200 번대인지 번호 대를 직접 작성해줘야 하지만 Alamofire 는 validation method 만 호출 하면 정상 status code 범위에서만 200번대 상태 코드만 허용하게 만들어 줄 수 있습니다.
Alamofire Request
- request method을 이용하여, HTTP 를 사용 할 수 있는데, method parameter로 URL과 HTTP method, parameter 등 요청에 필요한 것을 설정 할 수 있습니다.
Alamofire HTTP Method
- Alamofire HTTP method 또한 지원을 하는데, 대표적인 HTTP method 는 REST API 에서 이용되는 GET, POST, PUT, PATCH, DELETE 이 정의 되어 있으며, 요청을 만들때, request parameter 에 HTTP method 들을 전달 할 수 있습니다.
Alamofire Response
- Alamofire 는 요청에 대한 응답을 response method 을 이용하여 handling 하는데, 위의 그림과 같이 6개의 서로 다른 response code 가 정의 되어 있으며, 응답이 완료되면 completionHandler 를 호출해서 처리 합니다
- 위와 같이 request method 를 changing 하여 사용되게 됩니다
🔷 Cocoapods
-
Apple platform 에서 개발을 할 때, 외부 라이브를 관리하기 쉽도록 도와주는 의존성 관리도구 입니다
-
프로젝트에서 필요한 외부 라이브러리를 쉽게 관리하고, 사용할 수 있습니다.
Cocoapods official site - https://cocoapods.org/
Cocoapods 설치
$ sudo gem install cocoapods
Cocoapods 프로젝트에 적용
- xcode 로 새로운 project 를 생성하면 그 경로에 terminal 로 가서 Podfile 생성 합니다
pod init
- Podfile 을 수정하게 되면 외부 라이브러리를 가져오거나, 수정 할 수 있습니다
Alamofire 설치
- 설치된 Podfile 에서 alamofire 를 추가 시킵니다
# Uncomment the next line to define a global platform for your project
# platform :ios, '9.0'
target '07_covid_app' do
# Comment the next line if you don't want to use dynamic frameworks
use_frameworks!
pod 'Alamofire', '~> 5.5'
# Pods for 07_covid_app
end
-
Podfile 저장 후에, terminal 에서
pod install
해주면 package 설치 완료 -
설치한 라이브러리를 사용하기 위해서는 원래 xcode file 이 아닌, .xcworkspace 확장자를 가진 새로운 파일이 생성 되는데 그것을 실행해야지만, 외부 라이브러리를 사용할 수 있습니다
completionHandler 의 escaping closure 선언 이유
-
func 에서 escape 선언은 함수의 scope 를 벗어 나서도 변수가 참조 될 수 있게 하는 클로져 임. 즉, 함수의 인자로 closure 가 전달되지만, 반환된 후에도 실행되는 것을 의미합니다.
-
escaping closure 를 사용하는 대표적인 경우는 비동기 작업을 하는 경우 completionHandler 로서 escaping closure 를 많이 사용합니다. 보통 네트워킹 통신은 비동기 작업으로 처리 되는데,
.responseData
에 정의된completionHandler closure
는 fetch data 가 반환 된 후에, 호출이 됩니다. 왜냐하면, 서버에서 데이터를 언제 응답 해줄지 모르고, 응답시간이 로딩 되기 때문에 server 에서 비동기로 응답 받기 전에 즉, .responseData 에 전달한 parameter 가 completionHandler가 호출되기 전에 함수가 종료되서 서버의 응답을 받아도 동작하지 않게 됩니다 -
그래서, 비동기 작업을 completionHandler 로 callback 을 시켜줘야 한다면, escaping closure 를 사용하여 함수가 return 된 후에도, 실행 시켜줘야 합니다
// in viewDidLoad.swift
// fetch data SearchCovideOverview
func fetchCovidOverview(
// API 를 통해서 sever에서 json dat 를 받거나, 요청에 실패 하였을때 completionHandler 를 호출해서 해당 closure 를 정의하는 곳에 응답받은 data를 전달 해야 합니다
// completionHandler 를 @escaping closure 가 되게 설정
completionHandler: @escaping (Result<CityCovidOverView, Error>) -> Void
) {
let url = "https://api.corona-19.kr/korea/country/new/"
let param = [
"serviceKey": "16KIXAdhg7tk93ivjzsHFCQ8oOLyNSUuE"
]
// Alamofire 를 통해서 API 호출
AF.request(url, method: .get, parameters: param)
.responseData(completionHandler: { response in
switch response.result {
case let .success(data):
do {
let decoder = JSONDecoder()
let result = try decoder.decode(CityCovidOverView.self, from: data)
completionHandler(.success(result))
} catch { // error code 처리
completionHandler(.failure(error))
}
case let .failure(error):
completionHandler(.failure(error))
}
})
}
🔶 🔷 📌 🔑 👉
🗃 Reference
weatherApp-iOS-practice code - https://github.com/jacobkosmart/weatherApp-iOS-practice.git
나른한 코딩 - https://nareunhagae.tistory.com/44
codewithchrist - https://codewithchris.com/alamofire/
fastcampus - https://fastcampus.co.kr/dev_online_iosappfinal
Leave a comment