MapKit사용기 : 위치정보, 주변 검색 (1/3)

RYEOL·2024년 8월 5일

Swift

목록 보기
12/15

여러분, 안녕하세요! 오늘은 여러분과 함께 iOS 개발에서 중요한 기능 중 하나인 MapKit을 활용하여 지도를 구현하는 방법을 알아보려고 합니다. 지도 앱은 요즘 필수적인 기능 중 하나이며, 이를 통해 사용자는 근처 약국, 레스토랑, 공원 등을 손쉽게 찾을 수 있습니다.

혹시, 아이폰에서 지도를 구현하는 게 어렵다고 생각하시나요? 그렇지 않습니다! MapKitCoreLocation 프레임워크를 사용하면 비교적 쉽게 구현할 수 있습니다. 그럼 바로 시작해 볼까요?

MapKit의 기본 개념

MapKit은 iOS 앱 내에서 지도를 보여주고 조작할 수 있게 해주는 프레임워크입니다. 이를 통해 사용자는 현재 위치를 중심으로 주변을 검색하고, 특정 장소를 핀으로 표시하거나, 경로를 안내받을 수 있습니다.

사전 준비

먼저, 지도를 사용할 때는 위치 접근 권한을 요청해야 합니다. 이를 위해 Info.plist에 필요한 키 값을 추가해야 합니다.

<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>위치 접근을 위해 정보가 필요합니다.</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>위치 접근을 위해 정보가 필요합니다.</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>위치 접근을 위해 정보가 필요합니다.</string>

이제 본격적으로 코드를 작성해 보겠습니다.

1. 지도 뷰 초기화

먼저 지도 뷰와 위치 매니저를 초기화해 보겠습니다. 지도를 표시하기 위해서는 MKMapView를 사용하고, 사용자의 위치를 얻기 위해 CLLocationManager를 사용합니다.

import UIKit
import MapKit
import CoreLocation

class MapViewController: UIViewController {
    
    private var mapView: MKMapView!
    private var locationManager: CLLocationManager!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .systemBackground
        setupMapView()
        setupLocationManager()
    }
    
    private func setupMapView() {
        mapView = MKMapView()
        mapView.delegate = self // Self가 MKMapViewDelegate을 준수
        mapView.showsUserLocation = true
        mapView.userTrackingMode = .follow
        view.addSubview(mapView)
        
        mapView.snp.makeConstraints { make in
            make.edges.equalToSuperview()
        }
    }
    
    private func setupLocationManager() {
        locationManager = CLLocationManager()
        locationManager.delegate = self // Self가 CLLocationManagerDelegate을 준수
        locationManager.desiredAccuracy = kCLLocationAccuracyBest
        locationManager.requestWhenInUseAuthorization()
        locationManager.startUpdatingLocation()
    }
}

위 코드에서는 MKMapView를 초기화하고 위치 매니저를 설정했습니다. 사용자 위치가 변경되면 이를 지도에 반영하게 됩니다.

2. 위치 업데이트 및 약국 검색 기능

사용자의 현재 위치가 업데이트될 때마다 근처의 약국을 검색하는 기능을 구현해 보겠습니다. 이를 위해 MKLocalSearch를 사용하여 특정 지역 내의 약국을 검색하고, 결과를 지도에 핀으로 표시합니다.

private func searchForPharmacies(near coordinate: CLLocationCoordinate2D) {
    let request = MKLocalSearch.Request()
    request.naturalLanguageQuery = "약국" // 한국어로 '약국' 검색
    request.region = MKCoordinateRegion(center: coordinate, latitudinalMeters: 1000, longitudinalMeters: 1000)
    
    let search = MKLocalSearch(request: request)
    search.start { response, error in
        guard let response = response, error == nil else {
            print("Error searching for pharmacies: \(String(describing: error))")
            return
        }
        
        let mapItems = response.mapItems
        for item in mapItems {
            let annotation = MKPointAnnotation()
            annotation.title = item.name
            annotation.coordinate = item.placemark.coordinate
            self.mapView.addAnnotation(annotation)
        }
    }
}

위 코드에서는 MKLocalSearch를 사용하여 사용자의 현재 위치 주변에 있는 약국을 검색하고, 이를 지도에 핀으로 표시합니다.

3. 위치 매니저와 지도 뷰 델리게이트 구현

마지막으로, 위치 매니저와 지도 뷰 델리게이트 메서드를 구현하여 위치가 업데이트될 때마다 지도를 새로고침하고 검색 기능을 호출해야 합니다.

extension MapViewController: CLLocationManagerDelegate {
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        if let location = locations.first {
            let region = MKCoordinateRegion(center: location.coordinate, latitudinalMeters: 1000, longitudinalMeters: 1000)
            mapView.setRegion(region, animated: true)
            searchForPharmacies(near: location.coordinate)
        }
    }
    
    func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
        print("Failed to find user's location: \(error.localizedDescription)")
    }
}

extension MapViewController: MKMapViewDelegate {
    func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
        let identifier = "PharmacyAnnotation"
        var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: identifier)
        
        if annotationView == nil {
            annotationView = MKMarkerAnnotationView(annotation: annotation, reuseIdentifier: identifier)
            annotationView!.canShowCallout = true
            annotationView!.rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
        } else {
            annotationView!.annotation = annotation
        }
        
        return annotationView
    }
}

시뮬레이터 위치 설정

시뮬레이터로 위치 정보를 승인하고 지도를 확인하면 지금 제가 있는 위치하고 다른 곳이 나옵니다.
이런 상황이면 위처럼 MKLocalSearch를 사용해서 특정 지역 내의 장소를 검색했을 때, 원하는 결과 값인지 확인하는데 어려움이 있습니다.
아래의 순서를 따라해서 시뮬레이터 위치를 조절해봅시다!

  1. 내 위치 확인하기
    Naver 지도에 집 주소를 검색하면 상단 URL에 lng=, lat= 부분을 복사합니다.
  2. 시뮬레이터 위치 변경하기
    CustomLocation에서 Latitude를 위에서 찾은 lat= 이후 부분을 넣어주시고 longitude에 lng= 이후 부분을 넣어주시면 시뮬레이터 위치를 내 위치로 맞출 수 있습니다.

마무리

오늘은 MapKitCoreLocation 프레임워크를 사용하여 지도를 구현하고, 사용자의 현재 위치를 기반으로 주변의 약국을 검색하는 방법을 알아보았습니다. 지도 구현은 처음엔 복잡해 보일 수 있지만, 하나씩 차근차근 따라오니 한결 쉬워지지 않았나요?

MapKit 사용하기는 앞으로 2가지 포스팅을 더 준비할 예정이에요.
위치를 기반으로한 경로 탐색, 커스텀 어노테이션 등 다음에 더 좋은 글로 찾아오겠습니다!

profile
Flutter, Swift 모바일 개발자의 스타트업에서 살아남기

0개의 댓글