api키를 발급받고
https://api.corona-19.kr/korea/country/new/?serviceKey={api 키} 입력
중괄호는 지워야함.
//시도별 현황 api response JSON. (2022년 2월 22일자)
{
"resultCode": "0",
"resultMessage": "정상 처리되었습니다.",
"korea": {
"countryName": "합계",
"newCase": "99,573",
"totalCase": "2,157,734",
"recovered": "936,891",
"death": "7,508",
"percentage": "4179",
"newCcase": "99,444",
"newFcase": "129"
},
"seoul": {
"countryName": "서울",
"newCase": "21,807",
"totalCase": "561,744",
"recovered": "274,931",
"death": "2,307",
"percentage": "5907",
"newCcase": "21,769",
"newFcase": "38"
},
"busan": {
"countryName": "부산",
"newCase": "6,140",
"totalCase": "115,860",
"recovered": "45,169",
"death": "406",
"percentage": "3458",
"newCcase": "6,137",
"newFcase": "3"
},
"daegu": {
"countryName": "대구",
"newCase": "4,158",
"totalCase": "92,836",
"recovered": "59,172",
"death": "448",
"percentage": "3892",
"newCcase": "4,158",
"newFcase": "0"
},
"incheon": {
"countryName": "인천",
"newCase": "7,772",
"totalCase": "150,084",
"recovered": "71,128",
"death": "389",
"percentage": "5090",
"newCcase": "7,771",
"newFcase": "1"
},
"gwangju": {
"countryName": "광주",
"newCase": "3,247",
"totalCase": "51,947",
"recovered": "13,667",
"death": "110",
"percentage": "3603",
"newCcase": "3,239",
"newFcase": "8"
},
"daejeon": {
"countryName": "대전",
"newCase": "2,849",
"totalCase": "53,491",
"recovered": "16,333",
"death": "216",
"percentage": "3683",
"newCcase": "2,848",
"newFcase": "1"
},
"ulsan": {
"countryName": "울산",
"newCase": "1,941",
"totalCase": "30,482",
"recovered": "18,134",
"death": "72",
"percentage": "2718",
"newCcase": "1,941",
"newFcase": "0"
},
"sejong": {
"countryName": "세종",
"newCase": "648",
"totalCase": "11,748",
"recovered": "6,667",
"death": "5",
"percentage": "3159",
"newCcase": "648",
"newFcase": "0"
},
"gyeonggi": {
"countryName": "경기",
"newCase": "29,562",
"totalCase": "655,560",
"recovered": "230,244",
"death": "2,359",
"percentage": "4833",
"newCcase": "29,558",
"newFcase": "4"
},
"gangwon": {
"countryName": "강원",
"newCase": "2,057",
"totalCase": "41,182",
"recovered": "23,554",
"death": "139",
"percentage": "2677",
"newCcase": "2,055",
"newFcase": "2"
},
"chungbuk": {
"countryName": "충북",
"newCase": "2,187",
"totalCase": "45,772",
"recovered": "28,130",
"death": "149",
"percentage": "2865",
"newCcase": "2,181",
"newFcase": "6"
},
"chungnam": {
"countryName": "충남",
"newCase": "3,337",
"totalCase": "71,505",
"recovered": "22,372",
"death": "240",
"percentage": "3374",
"newCcase": "3,333",
"newFcase": "4"
},
"jeonbuk": {
"countryName": "전북",
"newCase": "2,463",
"totalCase": "52,300",
"recovered": "28,868",
"death": "163",
"percentage": "2927",
"newCcase": "2,456",
"newFcase": "7"
},
"jeonnam": {
"countryName": "전남",
"newCase": "2,109",
"totalCase": "40,015",
"recovered": "6,933",
"death": "54",
"percentage": "2183",
"newCcase": "2,106",
"newFcase": "3"
},
"gyeongbuk": {
"countryName": "경북",
"newCase": "3,127",
"totalCase": "66,949",
"recovered": "21,647",
"death": "288",
"percentage": "2549",
"newCcase": "3,114",
"newFcase": "13"
},
"gyeongnam": {
"countryName": "경남",
"newCase": "4,839",
"totalCase": "88,211",
"recovered": "49,583",
"death": "133",
"percentage": "2662",
"newCcase": "4,829",
"newFcase": "10"
},
"jeju": {
"countryName": "제주",
"newCase": "1,301",
"totalCase": "18,482",
"recovered": "11,681",
"death": "14",
"percentage": "2731",
"newCcase": "1,301",
"newFcase": "0"
},
"quarantine": {
"countryName": "검역",
"newCase": "29",
"totalCase": "9,566",
"recovered": "8,678",
"death": "16",
"percentage": "-",
"newCcase": "0",
"newFcase": "29"
}
}
모든 데이터는 아래에 해당하는 값을 갖고있으므로 구조체를 선언한다.
struct CovidOverview: Codable{
let countryName: String
let newCase: String
let totalCase: String
let recovered: String
let death: String
let percentage: String
let newCcase: String
let newFcase: String
}
모든 지역별 수치는 해당값을 갖고있으므로 프로퍼티 이름은 지역명, 자료형은 CovidOverview로 정의한다.
struct CityCovidOverview: Codable{
let korea: CovidOverview
let seoul: CovidOverview
let busan: CovidOverview
let daegu: CovidOverview
let incheon: CovidOverview
let gwangju: CovidOverview
let daejeon: CovidOverview
let ulsan: CovidOverview
let sejong: CovidOverview
let gyeonggi: CovidOverview
let gangwon: CovidOverview
let chungbuk: CovidOverview
let chungnam: CovidOverview
let jeonbuk: CovidOverview
let jeonnam: CovidOverview
let gyeongbuk: CovidOverview
let gyeongnam: CovidOverview
let jeju: CovidOverview
}
Alamofire를 활용하여 API를 호출할 메서드를 정의해준다.
completionHaner클로저를 전달받는다. API를 요청하고 서버에서 JSON데이터를 응답받거나 요청에 실패하였을 때 completionHaner클로저를 호출하여 해당클로저를 정의한 곳에 응답받을 데이터를 전달한다. 요청에 성공하면 CityCovidOverview 열거형 값을 전달하고, 요청에 실패하거나 에러상황일 경우 Error객체가 열거형 연관값으로 전달될 것이다.
func fetchCovidOverview(completionHandler: (Result<CityCovidOverview, Error>) -> Void)
{
}
Alamofire로 request를 한다
let url = "https://api.corona-19.kr/korea/country/new/"
let param = [
"serviceKey" : "API키"
]
AF.request(url, method: .get, parameters: param)
request메서드를 이용해 호출을 하였으면 응답데이터를 받을 수 있는 메서드를 체이닝을 해주어야함.
AF.request(url, method: .get, parameters: param)
.responseData(completionHandler: <#T##(AFDataResponse<Data>) -> Void#>)
completionHandler를 정의해주면 응답데이터가 클로저 파라미터로 전달되게 된다
요청에 대한 응답결과는 response.result로 알 수 있다.
요청결과가 .success이면 연관값으로 서버에서 전달받은 data가 전달되게 된다.
decoder를 생성하고 JSONDecoder 인스턴스를 대입 시켜준다
result 결과는 decoder가 decode를 한다는 메서드로 대충 이해.
첫 번째 파마니터는 매핑 시켜줄 객체 타입이며, from은 서버에서 전달받은 data를 넣어준다
completionHandler를 호출 하는데 success연관값에는 방금 정의해준 result를 넣어주며, 이 값은 CityCovidOverview 객체타입이다.
만약 JSON데이터가 CityCovidOverview매핑되는데 실패를 한다면 catch구문을 실행한다.
completionhandler를 실행하는데 .failure를 실행하는데 연관값에는 error가 실행됨.
.responseData { 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{
completionHandler(.failure(error))
}
}
}
fetchCovidOverview메서드 파라미터의 클로저를 이스케이핑 클로저로 선언을 한다
이스케이핑클로저는 클로저가 함수로 이스케이프(탈출한다) 함수의 인자로 클로저가 전달되지만 함수가 반환된 후에도 실행되는것을 의미
함수의 인자가 함수의 영역을 탈출하여 함수밖에서도 사용할 수 있는 개념은 기존에 우리가 알고있는 변수의 scope개념을 완전히 무시한다. 왜냐하면 함수에서 선언된 로컬변수가 로컬변수의 영역을 뛰어넘어 함수의 밖에서도 유효하기 때문
이스케이핑 함수를 사용하는 대표적인 예시로는 비동기작업을 하는경우, 컴플리션 핸들러로 이스케이핑 핸들러를 많이 사용함
보통 네트워킹통신은 비동기적으로 작업되기 때문에 코드에서와 같이 responseData메서드 파라미터에 정의한 completionHandler클로저는 fetchCovidOverview함수가 반환된 뒤에 호출이 된다.
그 이유는 서버에서 데이터를 언제 응답시켜줄지 모르기 때문이다.
그렇기 때문에 이스케이프 컴플리션 핸들러로 정의하지 않는다면 responseData메서드에 정의한 completionHandler가 호출되기 전에 함수가 종료되서 서버의 응답을 받아도 fetchCovidOverview함수에 정의한 completionHandler가 호출되지 않는다.
그렇기 때문에 함수 내에서 비동기 작업을 하고, 비동기 작업의 결과를 completionHander로 콜백을 해야 한다면, 이스케이핑클로저를 사용하여 함수가 반환된 후에도 실행되게 만들어주어야함.
클로저 정의 앞에 @escaping를 적어준다.
func fetchCovidOverview(completionHandler: @escaping (Result<CityCovidOverview, Error>) -> Void)
순환참조를 방지하기 위해 result 앞에 [weak self]를 적어주고
guard let self = self else { return } 을 통하여 일시적으로 self가 Strong reference가 되게 만들어준다,
fetchCovidOverview { [weak self] result in
guard let self = self else { return }
}
최종적으로 fetchCovidOverview가 API JSON data를 가져오는데 성공한다면 debugPrint로 콘솔창에 CityCovidOverview 객체가 나타나고, 실패한다면 error가 콘솔장에 나타나게 된다.
fetchCovidOverview { [weak self] result in
guard let self = self else { return }
switch result{
case let.success(result):
debugPrint("success \(result)")
case let .failure(error):
debugPrint("error \(error)")
}
}
}