Search
Duplicate

네이버 검색 API + 네이버맵 API 연동해서 식당 찾기

생성일
2024/07/12 02:12
태그
지혜로운 회사생활

네이버 검색 API + 네이버맵 API 연동해서 식당 찾기

우선 Store 모델 정의
// // Store.swift // EvMaps // // Created by Hyungjun KIM on 7/11/24. // import Foundation struct StoreResponse: Codable { let items: [Store] } struct Store: Codable { let name: String let latitude: Double? let longitude: Double? let address: String enum CodingKeys: String, CodingKey { case name = "title" case latitude = "mapy" case longitude = "mapx" case address = "address" } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) name = try container.decode(String.self, forKey: .name) if let latitudeString = try? container.decode(String.self, forKey: .latitude), let latitude = Double(latitudeString) { self.latitude = latitude / 10_000_000.0 // 위도 값을 올바르게 변환 } else { self.latitude = nil } if let longitudeString = try? container.decode(String.self, forKey: .longitude), let longitude = Double(longitudeString) { self.longitude = longitude / 10_000_000.0 // 경도 값을 올바르게 변환 } else { self.longitude = nil } address = try container.decode(String.self, forKey: .address) } }
Swift
복사
그리고 ViewController
// // ViewController.swift // EvMaps // // Created by Hyungjun KIM on 7/11/24. // import UIKit import NMapsMap class ViewController: UIViewController { var mapView: NMFMapView! var naverAPIManager = NaverAPIManager() var searchTextField: UITextField! var markers: [NMFMarker] = [] var activityIndicator: UIActivityIndicatorView! override func viewDidLoad() { super.viewDidLoad() setupMapView() setupSearchTextField() setupActivityIndicator() } func setupMapView() { mapView = NMFMapView(frame: view.frame) view.addSubview(mapView) mapView.positionMode = .direction let cameraUpdate = NMFCameraUpdate(scrollTo: NMGLatLng(lat: 37.5665, lng: 126.9780)) mapView.moveCamera(cameraUpdate) } func setupSearchTextField() { searchTextField = UITextField(frame: CGRect(x: 20, y: 50, width: view.frame.width-40, height: 40)) searchTextField.placeholder = "Search" searchTextField.borderStyle = .roundedRect searchTextField.backgroundColor = .white searchTextField.delegate = self view.addSubview(searchTextField) } func setupActivityIndicator() { activityIndicator = UIActivityIndicatorView(style: .large) activityIndicator.center = view.center activityIndicator.hidesWhenStopped = true view.addSubview(activityIndicator) } func startLoading() { activityIndicator.startAnimating() } func stopLoading() { activityIndicator.stopAnimating() } } extension ViewController: UITextFieldDelegate { func textFieldShouldReturn(_ textField: UITextField) -> Bool { textField.resignFirstResponder() if let searchText = textField.text { print("Searching for: \(searchText)") startLoading() performSearch(query: searchText) } return true } func performSearch(query: String) { naverAPIManager.fetchStores(query: query) { [weak self] stores in guard let self = self else { print("Self is nil") self?.stopLoading() return } guard let stores = stores else { print("No stores found") DispatchQueue.main.async { self.stopLoading() } return } print("Found \(stores.count) stores") DispatchQueue.main.async { self.addMarkers(for: stores) self.stopLoading() } } } func addMarkers(for stores: [Store]) { // Clear existing markers for marker in markers { marker.mapView = nil } markers.removeAll() // Add new markers for store in stores { if let lat = store.latitude, let lng = store.longitude { let marker = NMFMarker() marker.position = NMGLatLng(lat: lat, lng: lng) marker.captionText = store.name marker.mapView = self.mapView print("Marker added for: \(store.name) at Lat: \(lat), Lng: \(lng), Address: \(store.address)") } else { print("Failed to convert coordinates to Double for store: \(store.name)") } } } }
Swift
복사
// // NaverAPIManager.swift // EvMaps // // Created by Hyungjun KIM on 7/12/24. // import Foundation class NaverAPIManager { let clientId = "클라이언트ID" let clientSecret = "클라이언트Secret" func fetchStores(query: String, completion: @escaping ([Store]?) -> Void) { let urlString = "https://openapi.naver.com/v1/search/local.json?query=\(query)&display=20" guard let url = URL(string: urlString) else { print("Invalid URL") completion(nil) return } var request = URLRequest(url: url) request.addValue(clientId, forHTTPHeaderField: "X-Naver-Client-Id") request.addValue(clientSecret, forHTTPHeaderField: "X-Naver-Client-Secret") let task = URLSession.shared.dataTask(with: request) { data, response, error in if let error = error { print("Error fetching data: \(error)") completion(nil) return } guard let data = data else { print("No data received") completion(nil) return } do { let storeResponse = try JSONDecoder().decode(StoreResponse.self, from: data) completion(storeResponse.items) } catch { print("Error decoding data: \(error)") completion(nil) } } task.resume() } }
Swift
복사
여기서 performSearch() 함수를 통해서 검색한 값을 쿼리문에 넣어서 가져온 정보를
네이버 맵 지도에 마커로 표시한다!
그러면 이렇게 키워드에 맞는 장소를 검색할 수 있다.
이제 내 현재위치에서 시작하고 싶다면
1.
CoreLocation 프레임워크 추가
a.
ViewController에서 현재위치를 가져오기 위해 CoreLocation 프레임워크 추가한다.
import CoreLocation
Swift
복사
2.
CLLocationManager 설정
a.
위치 권한을 요청하고, 위치 업데이트를 받기 위해 CLLocationManager 를 설정한다.
3.
현재위치로 지도를 초기화
a.
viewDidLoad에 현재위치를 가져온 후 지도 위치를 업데이트
// // ViewController.swift // EvMaps // // Created by Hyungjun KIM on 7/11/24. // import UIKit import NMapsMap import CoreLocation class ViewController: UIViewController, CLLocationManagerDelegate { var mapView: NMFMapView! var naverAPIManager = NaverAPIManager() var searchTextField: UITextField! var markers: [NMFMarker] = [] var activityIndicator: UIActivityIndicatorView! var locationManager: CLLocationManager! override func viewDidLoad() { super.viewDidLoad() setupMapView() setupSearchTextField() setupActivityIndicator() setupLocationManager() } func setupLocationManager() { locationManager = CLLocationManager() locationManager.delegate = self locationManager.requestWhenInUseAuthorization() locationManager.startUpdatingLocation() } func setupMapView() { mapView = NMFMapView(frame: view.frame) mapView.positionMode = .direction view.addSubview(mapView) } func setupSearchTextField() { searchTextField = UITextField(frame: CGRect(x: 20, y: 50, width: view.frame.width-40, height: 40)) searchTextField.placeholder = "Search" searchTextField.borderStyle = .roundedRect searchTextField.backgroundColor = .white searchTextField.delegate = self view.addSubview(searchTextField) } func setupActivityIndicator() { activityIndicator = UIActivityIndicatorView(style: .large) activityIndicator.center = view.center activityIndicator.hidesWhenStopped = true view.addSubview(activityIndicator) } func startLoading() { activityIndicator.startAnimating() } func stopLoading() { activityIndicator.stopAnimating() } func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { if let location = locations.first { updateMapViewLocation(location: location) locationManager.stopUpdatingLocation() } } func updateMapViewLocation(location: CLLocation) { let cameraUpdate = NMFCameraUpdate(scrollTo: NMGLatLng(lat: location.coordinate.latitude, lng: location.coordinate.longitude)) mapView.moveCamera(cameraUpdate) } } extension ViewController: UITextFieldDelegate { func textFieldShouldReturn(_ textField: UITextField) -> Bool { textField.resignFirstResponder() if let searchText = textField.text { print("Searching for: \(searchText)") startLoading() performSearch(query: searchText) } return true } func performSearch(query: String) { naverAPIManager.fetchStores(query: query) { [weak self] stores in guard let self = self else { print("Self is nil") self?.stopLoading() return } guard let stores = stores else { print("No stores found") DispatchQueue.main.async { self.stopLoading() } return } print("Found \(stores.count) stores") if stores.count < 20 { print("Expected more results. Received only \(stores.count) results") } DispatchQueue.main.async { self.addMarkers(for: stores) self.stopLoading() } } } func addMarkers(for stores: [Store]) { // Clear existing markers for marker in markers { marker.mapView = nil } markers.removeAll() // Add new markers for store in stores { if let lat = store.latitude, let lng = store.longitude { let marker = NMFMarker() marker.position = NMGLatLng(lat: lat, lng: lng) marker.captionText = store.name marker.mapView = self.mapView print("Marker added for: \(store.name) at Lat: \(lat), Lng: \(lng), Address: \(store.address)") } else { print("Failed to convert coordinates to Double for store: \(store.name)") } } } }
Swift
복사
// // NaverAPIManager.swift // EvMaps // // Created by Hyungjun KIM on 7/12/24. // import Foundation class NaverAPIManager { let clientId = "클라이언트ID" let clientSecret = "클라이언트Secret" func fetchStores(query: String, completion: @escaping ([Store]?) -> Void) { let urlString = "https://openapi.naver.com/v1/search/local.json?query=\(query)&display=20" guard let url = URL(string: urlString) else { print("Invalid URL") completion(nil) return } print("Request URL: \(urlString)") var request = URLRequest(url: url) request.addValue(clientId, forHTTPHeaderField: "X-Naver-Client-Id") request.addValue(clientSecret, forHTTPHeaderField: "X-Naver-Client-Secret") let task = URLSession.shared.dataTask(with: request) { data, response, error in if let error = error { print("Error fetching data: \(error)") completion(nil) return } guard let data = data else { print("No data received") completion(nil) return } do { let storeResponse = try JSONDecoder().decode(StoreResponse.self, from: data) completion(storeResponse.items) } catch { print("Error decoding data: \(error)") completion(nil) } } task.resume() } }
Swift
복사
이렇게 현재위치가 나온다.

전체코드

// // ViewController.swift // EvMaps // // Created by Hyungjun KIM on 7/11/24. // import UIKit import NMapsMap import CoreLocation class ViewController: UIViewController, CLLocationManagerDelegate { var mapView: NMFMapView! var naverAPIManager = NaverAPIManager() var searchTextField: UITextField! var markers: [NMFMarker] = [] var activityIndicator: UIActivityIndicatorView! var locationManager: CLLocationManager! override func viewDidLoad() { super.viewDidLoad() setupMapView() setupSearchTextField() setupActivityIndicator() setupLocationManager() } func setupLocationManager() { locationManager = CLLocationManager() locationManager.delegate = self locationManager.requestWhenInUseAuthorization() locationManager.startUpdatingLocation() } func setupMapView() { mapView = NMFMapView(frame: view.frame) mapView.positionMode = .direction view.addSubview(mapView) } func setupSearchTextField() { searchTextField = UITextField(frame: CGRect(x: 20, y: 50, width: view.frame.width-40, height: 40)) searchTextField.placeholder = "Search" searchTextField.borderStyle = .roundedRect searchTextField.backgroundColor = .white searchTextField.delegate = self view.addSubview(searchTextField) } func setupActivityIndicator() { activityIndicator = UIActivityIndicatorView(style: .large) activityIndicator.center = view.center activityIndicator.hidesWhenStopped = true view.addSubview(activityIndicator) } func startLoading() { activityIndicator.startAnimating() } func stopLoading() { activityIndicator.stopAnimating() } func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { if let location = locations.first { updateMapViewLocation(location: location) locationManager.stopUpdatingLocation() } } func updateMapViewLocation(location: CLLocation) { let cameraUpdate = NMFCameraUpdate(scrollTo: NMGLatLng(lat: location.coordinate.latitude, lng: location.coordinate.longitude)) mapView.moveCamera(cameraUpdate) } } extension ViewController: UITextFieldDelegate { func textFieldShouldReturn(_ textField: UITextField) -> Bool { textField.resignFirstResponder() if let searchText = textField.text { print("Searching for: \(searchText)") startLoading() performSearch(query: searchText) } return true } func performSearch(query: String) { naverAPIManager.fetchStores(query: query) { [weak self] stores in guard let self = self else { print("Self is nil") self?.stopLoading() return } guard let stores = stores else { print("No stores found") DispatchQueue.main.async { self.stopLoading() } return } print("Found \(stores.count) stores") if stores.count < 20 { print("Expected more results. Received only \(stores.count) results") } DispatchQueue.main.async { self.addMarkers(for: stores) self.stopLoading() } } } func addMarkers(for stores: [Store]) { // Clear existing markers for marker in markers { marker.mapView = nil } markers.removeAll() // Add new markers for store in stores { if let lat = store.latitude, let lng = store.longitude { let marker = NMFMarker() marker.position = NMGLatLng(lat: lat, lng: lng) marker.captionText = store.name marker.mapView = self.mapView print("Marker added for: \(store.name) at Lat: \(lat), Lng: \(lng), Address: \(store.address)") } else { print("Failed to convert coordinates to Double for store: \(store.name)") } } } }
Swift
복사
// // NaverAPIManager.swift // EvMaps // // Created by Hyungjun KIM on 7/12/24. // import Foundation class NaverAPIManager { let clientId = Bundle.main.CLIENT_ID let clientSecret = Bundle.main.CLIENT_SECRET func fetchStores(query: String, completion: @escaping ([Store]?) -> Void) { let urlString = "https://openapi.naver.com/v1/search/local.json?query=\(query)&display=20" guard let url = URL(string: urlString) else { print("Invalid URL") completion(nil) return } print("Request URL: \(urlString)") var request = URLRequest(url: url) request.addValue(clientId, forHTTPHeaderField: "X-Naver-Client-Id") request.addValue(clientSecret, forHTTPHeaderField: "X-Naver-Client-Secret") let task = URLSession.shared.dataTask(with: request) { data, response, error in if let error = error { print("Error fetching data: \(error)") completion(nil) return } guard let data = data else { print("No data received") completion(nil) return } do { let storeResponse = try JSONDecoder().decode(StoreResponse.self, from: data) completion(storeResponse.items) } catch { print("Error decoding data: \(error)") completion(nil) } } task.resume() } }
Swift
복사