Reverse Geocoding이란?
특정 위도와 경도를 이용해서 해당 위치에 대한 주소를 구하는 것을 말한다.
네이버 맵을 사용할 경우 네이버 API를 사용해 쉽게 구현할 수 있다.
Reverse Geocoding API

Reverse Geocoding 역시 get 메서드로 요청을 하며
Header에 클라이언트 ID와 시크릿 키를 넣어줘야 한다.
Reverse Geocoding은 좌표를 주소로 변환하는 API이기에
저번 글에서 만든 마커를 가운데에 고정시킨 지도 객체를 활용해 좌표값을 받아보겠다.
[iOS] NaverMap - 화면 가운데 고정된 마커 설정 및 좌표 반환
이번 글은 프로젝트를 진행하며 내가 알아내고 구현한 것 위주로 서술하였다. 다른 더 좋은 방법이 있을 수도 있다! 화면 가운데 마커 설정하기택시 호출 앱 등을 살펴보면 화면 정 가운데
d0ngurrrrrrr.tistory.com
코드
우선 네트워크 통신 메서드부터 만들어보자면
func callRequest(coords: String, completion: @escaping (Data) -> Void) {
guard var urlComponents = URLComponents(string: "https://naveropenapi.apigw.ntruss.com/map-reversegeocode/v2/gc") else {
print("URL Components Error")
return
}
let queryItemArray = [
URLQueryItem(name: "coords", value: coords),
URLQueryItem(name: "orders", value: "roadaddr"),
URLQueryItem(name: "output", value: "json")
]
urlComponents.queryItems = queryItemArray
guard let url = urlComponents.url else {
print("URL Error")
return
}
var urlRequest = URLRequest(url: url)
urlRequest.addValue("클라이언트 Id", forHTTPHeaderField: "X-NCP-APIGW-API-KEY-ID")
urlRequest.addValue("Secret 키", forHTTPHeaderField: "X-NCP-APIGW-API-KEY")
URLSession.shared.dataTask(with: urlRequest) { data, response, error in
guard let data = data, let response = response as? HTTPURLResponse, response.statusCode == 200 else {
print("status code error")
return
}
completion(data)
}.resume()
}
Geocoding과 비슷하게 만들 수 있다.
이때 차이점은 쿼리에 output과 coords, 즉 좌표값을 줘야 한다는 것이다.
output을 json으로 주지 않으면 xml로 오기 때문에 xml로 파싱을 할 것이 아니라면 json으로 할 것을 추천한다.
// 딜리게이트 메서드
func mapViewCameraIdle(_ mapView: NMFMapView) {
print(mapView.cameraPosition.target)
let coords = "\(mapView.cameraPosition.target.lng)" + "," + "\(mapView.cameraPosition.target.lat)"
callRequest(coords: coords) { data in
do {
let decodeData = try JSONDecoder().decode(ReverseGeocodingModel.self, from: data)
print(decodeData)
} catch {
print(error)
}
}
}
//모델
struct ReverseGeocodingModel: Decodable {
let results: [Address]
}
struct Address: Decodable {
let region: Region
let land: Land
}
struct Region: Decodable {
let area1: Area1
let area2, area3, area4: Area
}
struct Area1: Decodable {
let name, alias: String
}
struct Area: Decodable {
let name: String
}
struct Land: Decodable {
let number1, number2: String
let addition0: Addition0
let name: String?
}
struct Addition0: Decodable {
let type, value: String
}
이제 callRequest를 호출해야 하는데, 언제 이 메서드를 호출하느냐가 중요하다.
나는 mapViewCameraIdle 메서드에서 호출을 했다.
그 이유는 간단하다.
카메라 이동의 모든 움직임이 종료되었을 때, 호출을 해서 call 수를 줄일 수 있기 때문이다.
mapViewCameraIdle이 아니라 cameraIsChangingByReason, cameraDidChangeByReason에서 호출을 하게 되면
드래그를 하는 동안에도 여러 번 호출이 될 수 있다.
이 부분은 주의해서 호출해줘야 한다.
전체코드 보기
import UIKit
import NMapsMap
class CenterMarkerViewController: UIViewController {
let centerMarker = NMFMarker()
override func viewDidLoad() {
super.viewDidLoad()
let naverMap = NMFNaverMapView(frame: view.frame)
view.addSubview(naverMap)
let cameraUpdate = NMFCameraUpdate(scrollTo: NMGLatLng(lat: 37.479132, lng: 127.011770))
naverMap.mapView.moveCamera(cameraUpdate)
centerMarker.position = naverMap.mapView.cameraPosition.target
centerMarker.captionText = "마커"
centerMarker.captionAligns = [NMFAlignType.top]
centerMarker.mapView = naverMap.mapView
naverMap.mapView.addCameraDelegate(delegate: self)
}
func callRequest(coords: String, completion: @escaping (Data) -> Void) {
guard var urlComponents = URLComponents(string: "https://naveropenapi.apigw.ntruss.com/map-reversegeocode/v2/gc") else {
print("URL Components Error")
return
}
let queryItemArray = [
URLQueryItem(name: "coords", value: coords),
URLQueryItem(name: "orders", value: "roadaddr"),
URLQueryItem(name: "output", value: "json")
]
urlComponents.queryItems = queryItemArray
guard let url = urlComponents.url else {
print("URL Error")
return
}
var urlRequest = URLRequest(url: url)
urlRequest.addValue("클라이언트 Id", forHTTPHeaderField: "X-NCP-APIGW-API-KEY-ID")
urlRequest.addValue("Secret 키", forHTTPHeaderField: "X-NCP-APIGW-API-KEY")
URLSession.shared.dataTask(with: urlRequest) { data, response, error in
guard let data = data, let response = response as? HTTPURLResponse, response.statusCode == 200 else {
print("status code error")
return
}
completion(data)
}.resume()
}
}
extension CenterMarkerViewController: NMFMapViewCameraDelegate {
func mapView(_ mapView: NMFMapView, cameraIsChangingByReason reason: Int) {
centerMarker.position = mapView.cameraPosition.target
}
func mapView(_ mapView: NMFMapView, cameraDidChangeByReason reason: Int, animated: Bool) {
centerMarker.position = mapView.cameraPosition.target
}
func mapViewCameraIdle(_ mapView: NMFMapView) {
print(mapView.cameraPosition.target)
let coords = "\(mapView.cameraPosition.target.lng)" + "," + "\(mapView.cameraPosition.target.lat)"
callRequest(coords: coords) { data in
do {
let decodeData = try JSONDecoder().decode(ReverseGeocodingModel.self, from: data)
print(decodeData)
} catch {
print(error)
}
}
}
}
struct ReverseGeocodingModel: Decodable {
let results: [Address]
}
struct Address: Decodable {
let region: Region
let land: Land
}
struct Region: Decodable {
let area1: Area1
let area2, area3, area4: Area
}
struct Area1: Decodable {
let name, alias: String
}
struct Area: Decodable {
let name: String
}
struct Land: Decodable {
let number1, number2: String
let addition0: Addition0
let name: String?
}
struct Addition0: Decodable {
let type, value: String
}

지도를 드래그해서 중앙마커를 움직이고, 움직임을 멈출 때마다

ReverseGeocodingModel 형태로 데이터가 받아지는 것을 알 수 있다.
마지막 데이터를 보자면 '서울특별시 서초구 서초동 219 대법원 청사'로 주소값을 얻을 수 있다.
(데이터가 많기 때문에 본인이 필요한 데이터에 맞는 모델을 만들어야 한다.)
'# 개발 > Swift' 카테고리의 다른 글
[iOS] 왜 꼭 Main Thread에서만 UI를 그려야 할까? (0) | 2024.06.02 |
---|---|
[iOS] NaverMap API - Directions(네비게이션 루트 기능) (0) | 2024.05.31 |
[iOS] NaverMap API - Geocoding (0) | 2024.05.29 |
[iOS] SOLID 원칙 (feat. Swift로 알아보는..) (0) | 2024.05.27 |
[iOS] NaverMap - Clustering(클러스터링) (0) | 2024.05.24 |