티스토리 뷰

POSTMAN

서버가 구현한 API를 테스트 할 수 있는 플랫폼
포스트맨은 서버 데이터가 현재 어떻게 나오고 있는지 파악하기 위해서 테스트 용도로 사용!
혹은 response body 확인
-> 멀쩡하게 이미지가 출력이 되던 부분이 갑자기 출력이 안될 때
포스트맨에서 해당 URL 찍어서 현재 데이터가 어떻게 넘어오고 있는지 파악!

Alamofire

pod init
vi Podfile
pod install

"Allow Arbitary Loads" 해당 속성을 YES로 설정해야 빌드시에 네트워크 통신이 가능하다!

1. NetworkResult.swift

import Foundation enum NetworkResult<T> { case success(T) -> 서버 통신 성공했을 때 case requestErr(T) -> 요청 에러 발생했을 때 case pathErr -> 경로 에러 발생했을 때 case serverErr -> 서버의 내부적 에러가 발생했을 때 case networkFail -> 네트워크 연결 실패했을 때 }

서버 통신 결과를 처리하기 위해 파일을 만들어줌 !

success(T) -> 서버 통신 성공했을 때
requestErr(T) -> 요청 에러 발생했을 때
pathErr -> 경로 에러 발생했을 때
serverErr -> 서버의 내부적 에러가 발생했을 때
networkFail -> 네트워크 연결 실패했을 때

<T> 의 의미?
타입 파라미터로 지금 당장 타입을 정해 놓지 않겠다! 라는 뜻
해당 자리에는 Int, String, Bool 등 다양한 타입이 들어갈 수 있다.

2. PersonDataModel.swift

import Foundation // MARK: - PersonDataModel struct PersonDataModel: Codable { let status: Int let success: Bool let message: String let data: Person } // MARK: - Person struct Person: Codable { let name, profileMessage: String enum CodingKeys: String, CodingKey { case name case profileMessage = "profile_message" } }

* CodingKeys 와 Codable 개념 적용 !
2021.08.19 - [Sopt 28th 세미나 - iOS] - <4주차 세미나> Encode / Decode

<4주차 세미나> Encode / Decode

HTTP 프로토콜 서버와 클라이언트는 "정해진 형태"로 "요청"과 "응답"을 주고 받는다. 클라이언트 -> 서버에게 HTTP 방식으로 요청할 때에는 여러가지 방법이 존재! GET -> 데이터를 얻고 싶을 때 요청

seungchan.tistory.com

3. GetPersonDataService.swift

// GetPersonDataService.swift import Foundation import Alamofire struct GetPersonDataService { static let shared = GetPersonDataService() func getPersonInfo(completion : @escaping (NetworkResult<Any>) -> Void) { // completion 클로저를 @escaping closure로 정의합니다. let URL = "https://mocki.io/v1/e5b82f33-832c-43ae-83c8-c3e053a4ead7" let header : HTTPHeaders = ["Content-Type": "application/json"] let dataRequest = AF.request(URL, method: .get, encoding: JSONEncoding.default, headers: header) dataRequest.responseData { dataResponse in switch dataResponse.result { case .success: guard let statusCode = dataResponse.response?.statusCode else {return} guard let value = dataResponse.value else {return} let networkResult = self.judgeStatus(by: statusCode, value) completion(networkResult) case .failure: completion(.pathErr) } } } private func judgeStatus(by statusCode: Int, _ data: Data) -> NetworkResult<Any> { switch statusCode { case 200: return isValidData(data: data) case 400: return .pathErr case 500: return .serverErr default: return .networkFail } } private func isValidData(data : Data) -> NetworkResult<Any> { let decoder = JSONDecoder() guard let decodedData = try? decoder.decode(PersonDataModel.self, from: data) else { return .pathErr} // 우선 PersonDataModel 형태로 decode(해독)을 한번 거칩니다. 실패하면 pathErr // 해독에 성공하면 Person data를 success에 넣어줍니다. return .success(decodedData.data) } }

위의 코드를 쪼개서 살펴보겠다 !

1. static let shared = GetPersonDataService()

싱글턴 패턴의 적용 !
static 을 활용해서 shared 라는 이름으로, GetPersonDataService 싱글턴 인스턴스를 만들었다!
이렇게 선언해놓으면 여러 뷰컨에서도 shared로 접근하면 같은 인스턴스에 접근 할 수 있다.

2. func getPersonInfo(completion : @escaping (NetworkResult<Any>) -> Void)

getPersonInfo 라는 메서드를 만들었다.
@escape 키워드를 사용해 escape closure 형태로 completion 정의하고 있다.

getPersonInfo의 함수가 종료되든 말든 상관없이, completion은 탈출 클로저여서, 전달된다면 이후 외부에서도 사용 가능하다.

여기에서는 용도를! 해당 네트워크 작업이 끝날 때 ->
completion 클로저에 (네트워크 성공 / 서버에러 / 네트워크 에러...) 와 같은 네트워크의 결과를 담아서 호출!
담은 네트워크 결과는 이후에 ViewController 에서 꺼내서 처리!

3. let URL = "https://mocki.io/v1/e5b82f33-832c-43ae-83c8-c3e053a4ead7" let header : HTTPHeaders = ["Content-Type": "application/json"]

데이터를 받아오려는 주소를 정의! 필요한 헤더를 KEY - VALUE 형태로 작성!
보통 json 형태로 받아오기 위해서 해당 header를 작성하는데 나중에 서버 붙일 때, Header 정보가 필요한지 서버 문서를 통해 확인!

4. let dataRequest = AF.request(URL, method: .get, encoding: JSONEncoding.default, headers: header)

우리는 (주소)를 가지고, (GET) 방식을 통해, (JSONEncoding) 인코딩 방식으로, (헤더) 정보와 함께 Request를 보내기 위한 정보를 묶어서 dataRequest에 저장! -> 요청서 개념

5. dataRequest.responseData { dataResponse in

위에서 적어둔 요청서 (dataRequest)를 가지고 진짜 서버에 보내서 통신 Request를 하는 중!
통신이 완료되면 클로저를 통해 dataResponse라는 이름으로 결과가 도착!

6. switch dataResponse.result {

dataResponse가 도착했으니, 그 안에는 통신에 대한 결과물이 들어있다
중요한 몇개를 알아보자면

dataResponse.result

통신 성공했는지 실패했는지 여부

dataResponse.reponse?.statusCode

Response의 statusCode

dataResponse.value

Response의 결과 데이터
Reponse의 statusCode를 조금 더 자세히 살펴보면
200대: 클라이언트의 요청을 정상적으로 수행
400대: 클라이언트의 요청이 부적절한 경우
500대: 서버에 문제가 있을 경우

7. guard let statusCode = dataResponse.response?.statusCode else {return} guard let value = dataResponse.value else {return} let networkResult = self.judgeStatus(by: statusCode, value) completion(networkResult)

만약 해당 dataReponse가 성공이라면 -> 필요한 정보는 2가지
statusCode 값과 reponse 결과 데이터!
그래서 guard let 구문을 통해 안전하게 값을 statusCode 와 value에 저장한다.

그리고 judgeStatus 라는 함수에 statusCode와 reponse 결과 데이터를 실어 보낸다.

*** 절대 헷갈리면 안되는 것은, statusCode가 400대로 떨어졌을 때도 .success로 빠진다는 것!
dataReponse.result의 실패는 타임아웃과 경우/혹은 불가능한 상태로 통신 자체에 실패한 경우를 의미

400대로 빠진다는 것 자체가 일단 서버에서 값을 주긴 준다는 뜻!

 8. case .failure: completion(.pathErr)

통신 실패하면 가차없이 completion에다가 pathErr(통신 실패) 값을 담아서 뷰컨으로 날려준다.
-> 탈출 클로저니까 클로저가 밖으로 나가는 것이 가능!

9. private func judgeStatus(by statusCode: Int, _ data: Data) -> NetworkResult<Any> {

judgeStatus 라는 함수에서는 아까 받은 statusCode를 바탕으로 어떻게 결과값을 처리할지 정의!

10. switch statusCode { case 200: return isValidData(data: data) case 400: return .pathErr case 500: return .serverErr default: return .networkFail }

200대인 경우
-> 성공! 그러면 데이터를 가공해서 전달해줘야 하기 때문에 isValidData라는 함수로 데이터를 넘겨준다! (데이터를 여기서 하겠다는 뜻)
400대인 경우
-> 클라이언트의 요청이 부적절한 경우 / .pathErr를 리턴해준다.
500대인 경우
-> 서버에 문제가 있을 경우
기타
-> 네트워크 에러로 분기처리!

200대를 제외하고는 return값으로 NetworkResult 형을 반환한다.
여기서 반환된 값은 어디로 가냐면
networkResult로 저장이 된다.. 요 저장된 networkResult 값을 completion 클로저에 실어서 뷰컨으로 날림!
뷰컨에서는 그러면 networkResult 값을 받아서 분기를 나눠서 처리해주면 되겠다..!

11. private func isValidData(data : Data) -> NetworkResult<Any> {

isValidData 라는 함수에서는 200대로 떨어졌을 때 -> 데이터를 가공하기 위한 함수

12. let decoder = JSONDecoder() guard let decodedData = try? decoder.decode(PersonDataModel.self, from: data) else { return .pathErr} // 우선 PersonDataModel 형태로 decode(해독)을 한번 거칩니다. 실패하면 pathErr

JSON 데이터를 해독하기 위해 JSONDecoder 선언
data를 우리가 아까 만들어둔 PersonDataModel 형으로 decode 시킨다.
실패하면 .pathErr로 빼버리고
성공하면 decodedData에는 값이 담겨있다.

13. // 해독에 성공하면 Person data를 success에 넣어줍니다. return .success(decodedData.data)

성공적으로 decode까지 마치면...
success에다가 data부분을 담아서 completion 호출을 한다.
그렇게 되면 뷰컨에서 요 data를 빼서 사용이 가능!

ViewController.swift

import UIKit class SampleNetworkViewController: UIViewController { @IBOutlet weak var nameLabel: UILabel! @IBOutlet weak var messageLabel: UILabel! override func viewDidLoad() { super.viewDidLoad() } @IBAction func getButtonClicked(_ sender: Any) { GetPersonDataService.shared.getPersonInfo { (response) in switch(response) { case .success(let personData): if let data = personData as? Person { self.nameLabel.text = data.name self.messageLabel.text = data.profileMessage } case .requestErr(let message) : print("requestERR",message) case .pathErr : print("pathERR") case .serverErr: print("serverERR") case .networkFail: print("networkFail") } } } }

위의 코드도 쪼개서 살펴볼게요..

1. GetPersonDataService.shared.getPersonInfo { (response) in switch(response)

버튼을 누르면 통신을 진행하는 코드를 작성해보겟다.
아까 만들어둔 GetPersonDataService 구조체에서 shared라는 공용 인스턴스에 접근(싱글턴 패턴)
그리고 만들어둔 getPersonInfo 를 사용 !
아까 계속 completion 클로저에다가 NetworkResult형 enum 값을 넣어줬었는데
이제 그 값을 이용해서 분기처리를 하겠다.
서버 통신 성공 / 네트워크 오류 / 경고 오류 등 (success / networkFail / pathErr)
이미 모든 처리는 GetPersonDataService에서 다 처리를 해줬기 때문에
ViewController에서는 그 결과값에 따라서 분기처리만 하면 된다.

2. case .success(let personData): if let data = personData as? Person { self.nameLabel.text = data.name self.messageLabel.text = data.profileMessage }

성공했을 경우 <T>형으로 데이터를 하나 받아올 수 있다.
<T> 형은 Generic하게 아무 타입이 가능하기 때문에 클로저에서 넘어오는 데이터를 let personData라고 정의하겠다!

우린 personData가 Person 형이라는 것을 알고
그래서 if - let 구문을 통해 옵셔널 바인딩을 해주고, 정상적으로 값을 data에 담아두게 된다.

3. case .requestErr(let message) : print("requestERR",message) case .pathErr : print("pathERR") case .serverErr: print("serverERR") case .networkFail: print("networkFail")

실패했을 때, 분기처리는 다음 case 들에서 진행..!

'Sopt 28th 세미나 - iOS' 카테고리의 다른 글

[Swift] Alamofire (POST)  (0) 2021.08.20
[Swift] Alamofire (GET) 요약  (0) 2021.08.20
[Swift] Escaping Closure & Singleton Pattern  (0) 2021.08.20
[Swift] Encode / Decode  (0) 2021.08.19
[Swift] Animation  (0) 2021.08.16
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함