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 로 요청하는것이 하면 서버에서 그에 맞는 응답 정보를 보내 줍니다

image

image

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

  1. Session Configuration 을 결정하고, Session 을 생성

  2. 통신할 URL 과 Request 객체를 설정

  3. 사용할 Task 를 결정하고 그에 맞는 Completion Handler 나 Delegate 메소들을 작성

  4. 해당 Task 를 실행

  5. 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 을 사용하는 이유

  • 코드 간소화, 가독성 측면에서 도움을 주고 여러기능을 직접 구축하지 않아도 쉽게 사용할 수 있음

image

  • 위의 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

image

  • Alamofire HTTP method 또한 지원을 하는데, 대표적인 HTTP method 는 REST API 에서 이용되는 GET, POST, PUT, PATCH, DELETE 이 정의 되어 있으며, 요청을 만들때, request parameter 에 HTTP method 들을 전달 할 수 있습니다.

Alamofire Response

image

  • Alamofire 는 요청에 대한 응답을 response method 을 이용하여 handling 하는데, 위의 그림과 같이 6개의 서로 다른 response code 가 정의 되어 있으며, 응답이 완료되면 completionHandler 를 호출해서 처리 합니다

image

  • 위와 같이 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

Categories:

Updated:

Leave a comment