본문 바로가기
iOS/swiftUI

[SwiftUI] 네이버 Reverse Geocoding API 활용하기 (CLGeocoder)

by 개발하는 감자입니다 2024. 3. 15.
728x90

 

 

안녕하세요, 개발감자입니다!

오늘은 사용자의 현재 위치를 기반으로 주변의 맛집을 추천하거나 커뮤니티에 공유하는 기능을 구현하는 과정에서 마주친 방해물을 해결과하는 과정에 대해서 이야기해볼 예정입니다. 

 

현재 구현하고 있는 서비스는 사용자의 현재 위치를 기반으로 맛집을 추천해주거나 커뮤니티를 제공합니다. 그래서 현재 사용자가 어떤 위치에 있는지 앱에서 파악하는 게 굉장히 중요합니다. 처음에는 역지코딩이 잘 되었지만, 무슨 이유인지 모르게 지번과 도로명이 랜덤으로 나왔습니다.도로명이더라도 동/읍/면은 잘 나오지만 시/구가 나오지 않았습니다. 그래서 항상 일관적으로 사용자의 위치 데이터를 관리하기 위하여 네이버에서 제공하는 Reverse Geocoding API 를 활용하여 구현해볼 예정입니다.

 

 

Reverse Geocoding 개요

 

api.ncloud-docs.com

 

 

1. 이전에 구현한 방법 : CLGeocoder를 사용한 역지오코딩

CLGeocoder는 iOS에서 제공하는 역지오코딩 기능을 수행하는 클래스입니다.

reverseGeocodeLocation 메서드를 사용하여 주어진 CLLocation 객체의 좌표를 주소로 변환합니다.

  • placemarks: 역지오코딩 결과로 얻은 주소 정보가 담긴 CLPlacemark 객체의 배열입니다. 배열의 첫 번째 요소를 사용하여 주요 주소 정보를 얻습니다.
  • error: 역지오코딩 과정에서 발생한 오류를 나타냅니다. 오류가 발생하면 콘솔에 오류 메시지를 출력하고 함수를 반환합니다.

주소 정보를 성공적으로 얻은 경우, placemark 객체에서 administrativeArea (시, 도), locality (시, 구), subLocality (동, 읍, 면) 정보를 추출하고, 이를 UserDefaults에 저장하여 앱 전반에서 사용할 수 있게 합니다. 또한, 얻은 주소 정보를 문자열로 조합하여 self.address 변수에 저장하고, 이를 사용하여 사용자 인터페이스에 주소 정보를 표시할 수 있습니다.

    func reverseGeocodeLocation(location: CLLocation) {
        reverseGeocodeLocationNaver(location: location)
        let geocoder = CLGeocoder()
        geocoder.reverseGeocodeLocation(location) { (placemarks, error) in
            if let error = error {
                print("역지오코딩 에러: \(error.localizedDescription)")
                return
            }
            
            if let placemark = placemarks?.first {
                
                let administrativeArea = placemark.administrativeArea ?? "" // 시, 도
                UserDefaults.standard.set(administrativeArea, forKey: "administrativeArea")

                let locality = placemark.locality ?? "" // 시, 구
                UserDefaults.standard.set(locality, forKey: "locality")

                let subLocality = placemark.subLocality ?? "" // 동, 읍, 면
                UserDefaults.standard.set(subLocality, forKey: "subLocality")
                
                print("주소: \(administrativeArea), \(locality), \(subLocality)")
                DispatchQueue.main.async {
                    self.address = "\(administrativeArea) \(locality) \(subLocality)"
                    
                }
                // 예: "서울특별시, 강남구, 역삼동"
            }
        }
    }

 

2. 변경한 방법 :  네이버 Reverse Geocoding API 

이 코드는 네이버의 Reverse Geocoding API를 활용하여 주어진 위치(위도와 경도)에 대한 주소 정보(시/도, 시/구, 동/읍/면)를 조회하는 함수입니다. 다음 단계로 나누어 설명하겠습니다:

2-1. API 인증 정보 설정

let clientId = "RGid" let clientSecret = "RGsecret"

 

 

네이버 API를 사용하기 위해 필요한 클라이언트 ID(clientId)와 클라이언트 비밀번호(clientSecret)를 설정합니다. 이 값들은 네이버 클라우드 플랫폼에서 API를 등록할 때 제공받습니다. (이 코드에서 "RGid""RGsecret"는 예시 값으로, 실제 사용 시에는 발급받은 값을 사용해야 합니다.)

 

2-2. 좌표 설정

let latitude = location.coordinate.latitude let longitude = location.coordinate.longitude let coords = "\(longitude),\(latitude)"

 

CLLocation 객체로부터 위도(latitude)와 경도(longitude)를 추출하고, 이를 "경도,위도"의 형식으로 문자열(coords)에 저장합니다. 이 문자열은 API 요청의 파라미터로 사용됩니다.

 

2-3. 요청 URL 구성

let urlString = "https://naveropenapi.apigw.ntruss.com/map-reversegeocode/v2/gc?request=coordsToaddr&coords=\(coords)&sourcecrs=epsg:4326&output=json&orders=addr"

let urlString = "https://naveropenapi.apigw.ntruss.com/map-reversegeocode/v2/gc?request=coordsToaddr&coords=\(coords)&sourcecrs=epsg:4326&output=json&orders=addr"

2-4. 요청 헤더 구성

URLRequest 객체를 생성하고, 네이버 API 사용을 위해 필요한 클라이언트 ID와 클라이언트 비밀번호를 헤더에 추가합니다.

var request = URLRequest(url: url) request.addValue(clientId, forHTTPHeaderField: "X-NCP-APIGW-API-KEY-ID") request.addValue(clientSecret, forHTTPHeaderField: "X-NCP-APIGW-API-KEY")

 

2-5. 네트워크 요청 및 응답 처리

구성한 요청을 이용해 네트워크 요청을 실행합니다. 응답이 돌아오면, 에러 여부를 확인하고, 성공적으로 데이터를 받았다면 JSON 형태로 파싱하여 주소 정보를 추출합니다.

URLSession.shared.dataTask(with: request) { data, response, error in ... }.resume()

 

 

 

2-6. JSON 데이터 파싱

응답 데이터(data)를 JSON 객체로 파싱하여 필요한 주소 정보를 추출합니다. 특히, results 배열 내의 region 객체에서 area1 (시/도), area2 (시/구), area3 (동/읍/면)의 이름을 각각 administrativeArea, locality, subLocality 변수에 저장합니다.

 

이 함수는 최종적으로 주어진 위치에 대한 상세 주소 정보를 콘솔에 출력합니다. 실제 앱에서는 이 정보를 UI에 표시하거나 다른 용도로 활용할 수 있습니다.

 

2-7.  전체 코드

 

func reverseGeocodeLocationNaver(location: CLLocation) {
    // API 인증 정보 설정 
    let clientId = "클라이언트 아이디"
    let clientSecret = "클라이언트 시크릿"
    
    // 좌표 설정
    let latitude = location.coordinate.latitude
    let longitude = location.coordinate.longitude
    let coords = "\(longitude),\(latitude)"
    
    // 요청 URL 구성
    let urlString = "https://naveropenapi.apigw.ntruss.com/map-reversegeocode/v2/gc?request=coordsToaddr&coords=\(coords)&sourcecrs=epsg:4326&output=json&orders=addr"
    
    guard let url = URL(string: urlString) else { return }

    // 요청 헤더 구성
    var request = URLRequest(url: url)
    request.addValue(clientId, forHTTPHeaderField: "X-NCP-APIGW-API-KEY-ID")
    request.addValue(clientSecret, forHTTPHeaderField: "X-NCP-APIGW-API-KEY")

    // 네트워크 요청 및 응답 처리
    URLSession.shared.dataTask(with: request) { data, response, error in
        // 에러 처리
        if let error = error {
            print("주소 검색 실패: \(error.localizedDescription)")
            return
        }

        // 응답 데이터 처리
        guard let data = data else {
            print("주소 정보를 받아올 수 없습니다.")
            return
        }

        // JSON 데이터 파싱
        if let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any],
           let results = json["results"] as? [[String: Any]],
           let region = results.first?["region"] as? [String: Any],
           let area1 = region["area1"] as? [String: Any],
           let area2 = region["area2"] as? [String: Any],
           let area3 = region["area3"] as? [String: Any] {
            
            let administrativeArea = area1["name"] as? String ?? "N/A"
            let locality = area2["name"] as? String ?? "N/A"
            let subLocality = area3["name"] as? String ?? "N/A"
            
            print("주소: \(administrativeArea) \(locality) \(subLocality)")
        }
    }.resume()
}

 

api 덕분에 손쉽게 기능을 구현할 수 있어 다행입니다. 이 포스팅이 저와 같은 문제를 겪는 분들께 도움이 되길 바랍니다.

개발감자였습니다!
728x90
반응형

'iOS > swiftUI' 카테고리의 다른 글

[SwiftUI] ChatGPT API를 활용한 서비스 구현하기  (0) 2024.03.14