
학교 과제로 미세먼지를 보여주는 프로그램을 만들라기에 자신감 있게 iOS를 선택했습니다!
할 줄 아는게 iOS밖에 없어서...
과제도 이왕 만드는김에 제대로 만들어보자라고 생각해서 앱의 장점인 현재 위치를 받아와 주변 측정소의 미세먼지 값을 쓰기로 했어요.

앱 미리보기
미세먼지 값을 받아오려면 네트워크 통신이 필수적이었고, 현재 위치를 알아내야 했습니다.
그래서 서버와의 통신 전, 서버의 요청값에 보내줄 현재위치를 알아 내기 위해서 CoreLocation을 사용했어요.
![]()
info.plist에 먼저 위치정보 접근 허용을 받을때 이유를 설명해주기 위한 문자열을 추가한다.
// lazy로 선언하여 메모리 관리
lazy var locationManager = CLLocationManager().then {
// 10미터 이내의 정확도로 설정을 하여 배터리 관리 최적화
$0.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
// 거리 위치 변화를 감지하지 않음
$0.distanceFilter = kCLHeadingFilterNone
// 권한을 요청
$0.requestWhenInUseAuthorization()
}
그리고 locationMagager 인스턴스를 생성하고 몇가지 조건을 추가 해주었습니다.
// 정보를 담을 struct 설정
struct userLocation {
var latitude: Double!
var longitude: Double!
}
// 현재 좌표값 가져오기
var info = userLocation()
let coord = locationManager.location?.coordinate
info.latitude = coord?.latitude
info.longitude = coord?.longitude
그 후, 정보를 담을 struct를 만들고, 현재 좌표값을 받아 구조체에 넣어주었습니다.
그 후 문제가 생겼는데, 제가 사용할 API에서 넘겨줘야할 좌표값이 다르더라구요!
아래는 사용한 API주소인데, iOS에서 받아오는 좌표값은 WGS84 좌표값이었고, 근처 측정소를 찾기 위해선 TM좌표로 변환해야했습니다.
측정소 정보 : www.data.go.kr/tcs/dss/selectApiDataDetailView.do?publicDataPk=15073877
[공공데이터 포털
국가에서 보유하고 있는 다양한 데이터를『공공데이터의 제공 및 이용 활성화에 관한 법률(제11956호)』에 따라 개방하여 국민들이 보다 쉽고 용이하게 공유•활용할 수 있도록 공공데이터(Datase
www.data.go.kr](https://www.data.go.kr/)
미세먼지 값 정보 : www.data.go.kr/tcs/dss/selectApiDataDetailView.do?publicDataPk=15073861
[공공데이터 포털
국가에서 보유하고 있는 다양한 데이터를『공공데이터의 제공 및 이용 활성화에 관한 법률(제11956호)』에 따라 개방하여 국민들이 보다 쉽고 용이하게 공유•활용할 수 있도록 공공데이터(Datase
www.data.go.kr](https://www.data.go.kr/)
그래서 WGS84좌표값들을 변환해주기 위해 카카오 API를 사용했습니다.
이거 말고도 로컬로 변경할 수 있는 Extension이 있던데 이건 나중에 따로 공부해봐야 할 것 같네요...
developers.kakao.com/docs/latest/ko/local/dev-guide#trans-coord
[Kakao Developers
카카오 API를 활용하여 다양한 어플리케이션을 개발해보세요. 카카오 로그인, 메시지 보내기, 친구 API, 인공지능 API 등을 제공합니다.
developers.kakao.com](https://developers.kakao.com/)
// TM좌표로 변환
func TM(url: String, longitude: Double, latitude: Double, handler: @escaping(userLocation) -> Void) {
var result = userLocation()
let headers:HTTPHeaders = ["Authorization" : kakaoKey]
let parameters: Parameters = ["x" : longitude, "y" : latitude, "output_coord" : "TM"]
let alamo = AF.request(url, method: .get,parameters: parameters, encoding: URLEncoding.queryString ,headers: headers)
alamo.responseJSON() { response in
switch response.result {
case .success(let value):
let json = JSON(value)
let documents = json["documents"].arrayValue
result.longitude = documents[0]["x"].double
result.latitude = documents[0]["y"].double
handler(result)
case .failure(_):
let alert = UIAlertController(title: nil, message: "네트워크를 다시 확인해주세요", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "확인", style: .default, handler: nil))
self.present(alert, animated: true, completion: nil)
return
}
}
}
먼저 카카오 API를 통해 반환된 좌표값을 콜백 함수를 통해 빼내고, 근처 측정소를 받아오는 API에 값을 다시 넘겨주었습니다.
// 근처 측정소 받아오기
func getNearbyMsrstn(url: String, tmX: Double, tmY: Double, handler: @escaping(String) -> Void) {
let parameters: Parameters = [
"serviceKey" : airKoreaKey,
"tmX" : tmX,
"tmY" : tmY,
"returnType" : "json"
]
let alamo = AF.request(url, method: .get,parameters: parameters, encoding: URLEncoding.default)
alamo.responseJSON() { response in
switch response.result {
case .success(let value):
let json = JSON(value)
let stationName = json["response"]["body"]["items"][0]["stationName"].string!
locationInfo.shared.stationName = stationName
handler(stationName)
case .failure(_):
let alert = UIAlertController(title: nil, message: "네트워크를 다시 확인해주세요", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "확인", style: .default, handler: nil))
self.present(alert, animated: true, completion: nil)
return
}
}
}
그 후 측정소의 이름을 활용해 제가 필요한 정보(미세먼지 값)을 받아오는 API를 한번 더 요청해 주었어요.
// 미세먼지 값 받아오기
func getfinedust(url: String, stationName: String, handler: @escaping(String, String) -> Void) {
let parameters: Parameters = [
"serviceKey" : airKoreaKey,
"stationName" : stationName,
"dataTerm" : "DAILY",
"informCode" : "PM10",
"returnType" : "json"
]
let alamo = AF.request(url, method: .get,parameters: parameters, encoding: URLEncoding.default)
alamo.responseJSON() { response in
switch response.result {
case .success(let value):
let json = JSON(value)
let pm10Value = json["response"]["body"]["items"][0]["pm10Value"].string!
let dataTime = json["response"]["body"]["items"][0]["dataTime"].string!
locationInfo.shared.pmValue = pm10Value
locationInfo.shared.dataTime = dataTime
handler(pm10Value, dataTime)
case .failure(_):
let alert = UIAlertController(title: nil, message: "네트워크를 다시 확인해주세요", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "확인", style: .default, handler: nil))
self.present(alert, animated: true, completion: nil)
return
}
}
}
이번에 로딩화면을 활용해보고 싶어서 이러한 처리를 로딩화면에서 하게 만들어주었는데, 그렇게 좋지는 못한 것 같네요.
로딩화면에서 정보를 다 처리한 후에 싱글톤 패턴을 이용하여 값을 저장하고, 그 값을 메인 화면에 값을 넘겨 주었습니다.
import Foundation
class locationInfo {
static let shared = locationInfo()
var nowLocationName: String?
var longitude: Double?
var latitude: Double?
var pmValue: String?
var dataTime: String?
var stationName: String?
private init() { }
}
확실히 비효율적인 로직이 많아보이는데, 더 공부해야할 게 많아보이네요.
그래도 싱글톤 패턴이라던지 Escaping Closure, CoreLocation등 처음 써본것들이 많아서 신기했고, Alamofire를 활용하는 법을 조금 더 배운거 같아 의미있는 프로젝트였던 것 같습니다.