🍎 5주차 - 지도
후후 이번에는 직접 쓰려고 꽤나 노력을 햇슴다 ^^
그리고 나 이거 폰으로 안 뜨는데? ㅋ 뭔 오류나는디 > 일단 영상 있으니게,,, 그걸로 확인 ㄱ
CoreLocation 프레임 워크
위치 기반
- GPS로 현재 위치 파악
- 실시간 위치 업뎃
- 지오펜싱
- 방향 감지
____________________
MapKit
지도에 위치를 시각화하고 상호작용하는 기능
(지도 데이터 + 위치 시각화 + 사용자 인터렉션)
import SwiftUI
import MapKit
struct ContentView: View {
@State var region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 37.5665, longitude: 126.9780), span: MKCoordinateSpan(latitudeDelta: 0.05, longitudeDelta: 0.05))
var body: some View {
Map(initialPosition: .region(region))
}
}
#Preview {
ContentView()
}
- MKCoordinateRegion : 지도에서 보여줄 영역(중심 좌표 + 줌 정도)
- Map : SwiftUI의 지도 컴포넌트 ( UIKit의 MKMapView 역할) & Deprecated 조심하라는데?
- CLLocationCoordinate2D : 위도(latitude), 경도 (longitude)
+ Info.plist에 위치 권한 설정하라는데
( 앱이 위치 정보 사용하기 위해서는 반드시 사용자에게 권한 요청해야함 )
Info.plist 파일에 권한 사용 목적을 명시하는 문자열을 추가해야...
<key>NSLocationWhenInUseUsageDescription</key>
<string>이 앱은 현재 위치를 표시하기 위해 위치 정보가 필요합니다.</string>
(여기는 출처 유엠씨 학습자료? 암튼 그거)
[ 지도 사용 용어 정리(지도에서 사용되는 개념 자체 용어) ]
위도 (Latitude) | 지구를 가로로 나눈 좌표. 북위(N)와 남위(S)로 구분 | 서울: 37.5665°N |
경도 (Longitude) | 지구를 세로로 나눈 좌표. 동경(E)과 서경(W)로 구분 | 서울: 126.9780°E |
고도 (Altitude) | 해수면으로부터의 높이. 단위는 보통 미터(m) | 한라산 정상: 약 1,947m |
좌표 (Coordinate) | 위도 + 경도로 구성된 위치의 정확한 좌표 | CLLocationCoordinate2D(latitude: 37.5665, longitude: 126.9780) |
방향 (Heading) | 사용자가 보고 있는 방향. 나침반 각도(0~360°) | 북쪽: 0°, 동쪽: 90° |
줌 레벨 (Zoom Level) | 지도를 얼마나 확대/축소할지를 나타내는 값. 수치가 낮을수록 더 확대됨 | 지도 전체 보기: 줌 2, 건물 보기: 줌 18 |
지도 중심 (Map Center) | 현재 지도의 정중앙 좌표 | 서울역이 화면 가운데에 보일 경우: 중심 좌표는 서울역 |
지오펜싱 (Geofence) | 특정 반경 내에 사용자가 들어오거나 나갈 때 이벤트 발생 | 반경 100m 안으로 들어오면 알림 보내기 |
반경 (Radius) | 지오펜싱에서 중심에서 바깥쪽까지의 거리. 단위는 미터(m) | 반경 200m 설정 |
장소 (Place / POI) | 지도에 등록된 특정 장소 정보 (Point of Interest) | 스타벅스, 서울시청, CGV |
경로 (Route / Path) | 한 지점에서 다른 지점까지 이동하는 경로 | 집 → 학교까지 도보 경로 |
거리 (Distance) | 두 위치 사이의 직선 거리 또는 경로 거리 | 집과 마트 사이: 700m |
사용자 위치 (User Location) | 현재 사용자의 위치 (GPS로 측정) | 내 위치를 파란 점으로 표시 |
타겟 위치 (Target Location) | 사용자가 가려는 목적지 좌표 | “강남역으로 가는 길 안내” |
지도 스타일 (Map Style) | 지도에 보여지는 모양 또는 테마 | 일반 지도 / 위성 지도 / 하이브리드 |
터치 인터랙션 | 지도에서 사용자 터치에 따른 동작 | 핀 추가, 확대/축소, 위치 이동 |
영역 (Region) | 지도에서 보여주는 위도/경도 범위 | 중심: 강남, 반경: 500m |
스팬 (Span) | 화면에서 보여줄 위도/경도의 확장 범위 | latitudeDelta / longitudeDelta |
트래킹 모드 (Tracking Mode) | 사용자의 현재 위치를 따라갈지 여부 | follow / none / followWithHeading |
정확도 (Accuracy) | 위치 측정의 정밀도 (낮을수록 정확함) | GPS 모드: 수 미터 정확도 |
위치 권한 (Location Authorization) | 위치 서비스를 사용할 수 있는 권한 상태 | 항상 허용 / 사용 중에만 허용 / 거부됨 |
[ 지도 사용 용어 정리(코드적인 개념) ]
MapKit | 애플이 제공하는 지도 프레임워크. 지도를 화면에 표시하고, 핀을 추가하거나 위치를 추적할 수 있음 |
Map (SwiftUI) | SwiftUI에서 지도 뷰를 표시할 때 사용하는 뷰. Map(position:) 등의 형태로 사용 |
MKMapView (UIKit) | UIKit에서 지도 화면을 표시하는 뷰 클래스. SwiftUI의 Map과 대응되는 UIKit 버전 |
CLLocationManager | 사용자의 현재 위치를 감지하고 추적하는 객체. GPS나 Wi-Fi, 셀룰러 네트워크 등을 사용함 |
CLLocation | 위도, 경도, 고도 등의 정확한 위치 정보를 담고 있는 객체 |
CLLocationCoordinate2D | 위도(latitude)와 경도(longitude)로 구성된 2차원 위치 좌표 구조체 |
MapCameraPosition(SwiftUI) | SwiftUI 5에서 지도 카메라 위치를 제어할 때 사용하는 타입. .region(...), .automatic 등으로 설정 |
MKCoordinateRegion | 지도에서 보여줄 중심 좌표와 확대 수준(줌)을 지정하는 영역. 카메라 위치 설정에 사용 |
MKCoordinateSpan | 지도에서 가로/세로 방향으로 얼마나 확대해서 보여줄지를 정하는 값. 작을수록 확대됨 |
MapAnnotation | SwiftUI에서 지도에 커스텀 뷰를 추가할 수 있는 핀. 예: 텍스트나 이미지로 표시 가능 |
MapMarker | SwiftUI에서 기본 제공되는 핀 형태. 빨간색 마커로 표시됨. 간단한 마커가 필요할 때 사용 |
Annotation (UIKit) | 지도에 표시하는 마커를 총칭. MKPointAnnotation 등의 객체로 추가함 |
MapCamera (UIKit) | MKMapView에서 카메라 위치, 고도, 각도 등을 세밀하게 조정할 수 있는 객체 |
Region | 지도의 중심 좌표와 범위를 함께 나타내는 개념. MKCoordinateRegion으로 표현 |
Span | 지도를 얼마나 확대할지를 결정하는 값. latitudeDelta, longitudeDelta 값으로 줌 조절 |
Heading | 사용자의 기기 방향(나침반 방향). 동서남북 중 어디를 보고 있는지를 의미함 |
TrackingMode | 지도가 사용자의 위치를 따라갈지 말지를 설정하는 모드. 예: follow, none, followWithHeading 등 |
Geofence | 특정 위치를 중심으로 반경을 설정하고, 출입 여부를 감지하는 기술. 위치 기반 알림 등에 사용 |
Visit Monitoring | 사용자가 특정 장소에 일정 시간 머물렀다 떠날 때 자동으로 감지하는 기능. 저전력 기반 |
Significant Location Change | 수백 미터 이상 이동했을 때만 위치 업데이트를 받는 모드. 배터리 효율이 좋음 |
Location Authorization | 위치 권한 설정. WhenInUse, Always, Denied 등으로 나뉘며 Info.plist 설정이 필요함 |
(여기도 출처 유엠씨 학습자료? 암튼 그거)
8자리로 하라는 말씀이신가요 그러니까??? ㄴㄴ 4자리 이상인듯 암튼
- 일반적인 권장 정밀도입니다.
소수점 자리수위치 정확도
3자리 | 약 110m 정도 |
4자리 | 약 11m 정도 (건물 단위) |
5자리 | 약 1.1m 정도 (사람 크기) |
6자리 | 약 11cm (보행자 위치, 자율주행) |
7자리 | 약 1.1cm |
8자리 | 약 1mm ~ 1.1mm |
- 실전에서 자주 쓰는 소수점 자릿수
목적 | 권장 자리수 | 설명 |
일반적인 위치 표시 (핀, 지도 이동) | 5~6자리 | 1미터 이내 정확도 |
차량/보행자 위치 추적 | 6~7자리 | 고정밀 필요 |
고정된 장소 마킹 (카페, 관광지 등) | 5자리 | 충분함 |
실내 위치 / 자율주행 / 측량 | 7~8자리 | 고정밀 GPS 필요 |
서버와 작업하는데 프론트가 서버에 8자리로 위도 / 경도 보내고 어느 부분에 다시 위도 / 경도 값을 돌려 받는다고 할 때 서버가 소수점 4자리 이상으로 안 준다??
바로 몽둥이 🏏 휘두르면 됩니다.
ㄴ 뭐야?;
지도는 그리드
let xTile = floor((longitude + 180.0) / 360.0 * pow(2.0, z))
let yTile = floor((1.0 - log(tan(latitude * .pi / 180.0) + 1.0 / cos(latitude * .pi / 180.0)) / .pi) / 2.0 * pow(2.0, z))
위 수식 외우지 말라는디
계산된 X/Y 타일 번호를 서버에 요청
⬇️
서버로부터 블라블라.png 파일로 받아와 렌더링
⬇️
이 블라블라.png 타일이 지도위에 올라가서 배경이 그려지고 이 위에 현재 위치 마커 등등 인터페이스들을 갖춤
⬇️
근데 지도에 서울시청만 딱 하나 있을리가 없음
실제로 타일을 받아온다면, 보통 그 중심을 포함한 상하좌우 몇 장씩의 타일을 함께 불러와서 렌더링
지도 앱이 어쩔 때는 빠르고 어쩔 때는 느린지
- 타일은 개별 이미지다!! → 개별 이미지의 타일을 여러 장 요청해서 지도위에 그린다
- 이동하거나 확대하면??? → 또 다시 타일 이미지를 요청하고 렌더링
- 네트워크가 느리다면?? → 지도가 회색되거나 흰색되거나 등등 흐리게 뜬다
지도 위에 정보를 덧붙이는 방식인 '오버레이'
- 지도를 배경으로 하고, 그 위치에 추가적인 정보나 시각적 요소를 덧붙이는 방식
: 지도를 단순한 배경에서 의미 있는 정보 전달 도구의 역할을 하는 레이어
- 사용자가 검색한 장소에 마커를 띄우는 것
- 사용자가 지나간 경로를 선으로 그리는 것
- 반경 1km 이내에 매장 보기 기능에서 원이 표시되는 것
- 특정 지역을 음영 처리하는 것
종류 | 설명 | 예시 |
Marker / Annotation | 특정 좌표에 핀이나 마커 추가 | “서울역”에 마커 찍기 |
Polyline | 여러 점을 선으로 연결 | 길 안내, 이동 경로 표시 |
Polygon | 여러 점을 닫힌 선으로 연결한 면 | 구역 표시, 서비스 지역 표시 |
Circle | 중심과 반지름으로 정의된 원 | 반경 500m 표시, 지오펜싱 |
Custom Annotation View | 이미지, 텍스트 등 커스텀 뷰 표시 | 사용자 프로필 핀, 커스텀 UI |
Heatmap / Tile Overlay | 특정 데이터 밀도, 온도, 사용자 수 등 시각화 | 인구 밀도, 혼잡도 표시 등 |
우리는 지도에서 오버레이를 잘 활용해야 합니다.!
왜냐하면 오버레이는 지도를 단순한 배경에서 **의미 있는 정보 전달 도구**의 역할을 하는 레이어이기 때문이죠!
시각적으로 정보를 부각하고, 사용자의 인지를 돕고, 때론 빠른 판단까지 가능하게 해줍니다!
UX 관점에서 오버레이는 좌표를 의미로 바꾸는 레이어입니다.
지금 위치 근처 카페 5개 → 뭐로 표현하실래요?
: 각 카페 좌표에 마커 찍기
배달 가능 지역은 여기까지이다! → 뭐로 표현하실래요?
: 원형오버레이 (현재 내 위치를 중심으로 원으로 표시)
여기서 여기까지 이동 경로 → 뭐로 표현하실래요?
: 폴리라인으로 경로 그리기 (출발지 & 도착지 지정후 연결하여 이동 경로 )
3개의 질문에 대해 어떻게 표현하는 게 좋을지 📌 이번 주차에는 이런 내용을 학습했어요! 작성하세요
위치 정보는 GPS, 셀룰러, WiFi로 측정됨
SwiftUI에서 **CLLocationManager**를 ViewModel 또는 **ObservableObject(@Observable)**로 관리하는 것이 일반적입니다!! 일반적인 걸 넘어서서 그냥 필수입니다.!!
-> 뭔말인가
📍 좀 더 쉽게 풀어서
- CLLocationManager는 아이폰 GPS 위치 정보를 가져오는 기능이야.
- 근데 얘를 그냥 SwiftUI View 안에 막 넣어서 쓰면,
- 메모리 관리가 엉망,
- 위치 업데이트 알림 처리가 복잡,
- SwiftUI의 상태 변화를 제대로 못 따라가.
- 그래서 CLLocationManager를 ViewModel이나 ObservableObject로 따로 뽑아서 관리해야 해.
- 그래야 SwiftUI랑 “자동으로 바인딩” 되고,
- 뷰가 상태 변화를 깔끔하게 감지할 수 있어.
어 그래 지피티야 고맙다
LocationManager / MapViewModel / MapView
<세 가지로 나누는 이유>
책임 분리
- 각 객체가 하나의 일만 하도록 해서 가독성과 유지 보수 향상
재사용성
- ViewModel과 LocationManager는 여러 화면에서 재사용 가능
확장성
- 거리계산, 검색결과 반영 등 기능을 View에 영향 없이 확장 가능
생명 주기 독립적 유지
- 위치 추적이 끊기지 않고 연속성으로 가능, 객체 생명주기 명확히 관리 가능
1. LocationManager
디바이스의 GPS와 직접 통신해서 위치를 받아오는 객체
- 위치 추적 기능만 담당하는 별도의 객체로 분리
( 👨✈️ LocationManager: 현재 어디 있는지 알려주는 파일럿 )
예시 기능
- 위치 권한 요청
- 위치 정보 업데이트 감지
- 방향 정보 수진
2. MapViewModel
지도 화면이 어떻게 보여야 하는가?를 계산하고 결정하는 뇌
- 현재 위치, 특정 장소, 검색 결과 등 지도 이동을 제어하는 로직의 중심
( 🧭 MapViewModel: 지도를 어떻게 움직일지 계산하는 항법사 )
예시 기능
- 현재 위치로 지도 이동
- 특정 좌표로 줌 이동
- 마커 배열 관리
3. MapView
Map을 띄우고, ViewModel의 바인딩 상태를 보여주는 역할
- ViewModel의 데이터를 바인딩 해 뷰를 자동 업데이트
( 🗺️ MapView: 실제로 지도를 보여주는 모니터 )
예시 기능
- 지도 영역 바인딩
- Annotation 표시
- onChange, onAppear 등을 통한 이벤트 처리
LocationManager 생성 및 분석
import Foundation
import CoreLocation
import MapKit
import Observation
@Observable
class LocationManager: NSObject {
static let shared = LocationManager()
// MARK: - CLLocationManager
let locationManager = CLLocationManager()
// MARK: - Published Properties
var currentLocation: CLLocation?
var currentHeading: CLHeading?
var currentSpeed: CLLocationSpeed = 0
var currentDirection: CLLocationDirection = 0
var authorizationStatus: CLAuthorizationStatus = .notDetermined
var didEnterGeofence: Bool = false
var didExitGeofence: Bool = false
// MARK: - Init
override init() {
super.init()
locationManager.delegate = self // 위치 정보나 방향이 바뀔때 자신에게 알려달라는 의미
locationManager.desiredAccuracy = kCLLocationAccuracyBest // 위치 정보의 정확도 수준을 설정, 배터리 많이 소모
locationManager.headingFilter = kCLHeadingFilterNone // 방향(나침반) 업데이트 민감도 설정
// 아주 작은 변화도 모두 반영
// 사용자가 핸드폰을 살짝 돌려도 방향 정보가 업데이트 됨
requestAuthorization()
startUpdatingLocation()
//- 실시간 위치 추적 시작
//- 호출 이후 **`locationManager(_:didUpdateLocations:)`** 델리게이트 메서드가 주기적으로 호출됨
//- 위치 변경이 감지되면 자동으로 알려줌
startUpdatingHeading()
//- 기기의 나침반(방향 센서) 정보 수신 시작
//- 호출 이후 locationManager(_:didUpdateHeading:) 메서드 호출
}
// MARK: - 권한 요청
func requestAuthorization() {
locationManager.requestWhenInUseAuthorization()
locationManager.requestAlwaysAuthorization()
}
// MARK: - 위치 추적
func startUpdatingLocation() {
locationManager.startUpdatingLocation()
}
func stopUpdatingLocation() {
locationManager.stopUpdatingLocation()
}
// MARK: - 방향 추적
func startUpdatingHeading() {
locationManager.startUpdatingHeading()
}
func stopUpdatingHeading() {
locationManager.stopUpdatingHeading()
}
// MARK: - Significant Location Change
func startMonitoringSignificantLocationChanges() {
locationManager.startMonitoringSignificantLocationChanges()
}
func stopMonitoringSignificantLocationChanges() {
locationManager.stopMonitoringSignificantLocationChanges()
}
// MARK: - 방문 감지
func startMonitoringVisits() {
locationManager.startMonitoringVisits()
}
// MARK: - 지오펜싱
func startMonitoringGeofence(center: CLLocationCoordinate2D,
radius: CLLocationDistance,
identifier: String) {
let region = CLCircularRegion(center: center,
radius: radius,
identifier: identifier)
region.notifyOnEntry = true
region.notifyOnExit = true
locationManager.startMonitoring(for: region)
print("Monitoring regions: \(locationManager.monitoredRegions)")
}
func stopMonitoringAllGeofences() {
for region in locationManager.monitoredRegions {
locationManager.stopMonitoring(for: region)
}
}
}
// MARK: - CLLocationManagerDelegate
extension LocationManager: CLLocationManagerDelegate {
// 권한 변경 감지
//사용자가 위치 권한을 승인/거부하거나 설정을 변경했을 때 호출
func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
authorizationStatus = manager.authorizationStatus
}
// 위치 업데이트 감지 (기본 위치 추적 + Significant Change)
// didUpdateLocations : startUpdatingLocation() 호출 후, 위치가 변경될 때마다 반복적으로 호출됨
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let latest = locations.last {
DispatchQueue.main.async {
self.currentLocation = latest
self.currentSpeed = max(latest.speed, 0)
}
}
}
// 방향 감지
// didUpdateHeading : startUpdatingHeading()을 호출하면 방향(나침반) 값이 바뀔 때마다 호출됨
func locationManager(_ manager: CLLocationManager, didUpdateHeading newHeading: CLHeading) {
DispatchQueue.main.async {
self.currentHeading = newHeading
self.currentDirection = newHeading.trueHeading > 0 ? newHeading.trueHeading : newHeading.magneticHeading
}
}
// 방문 감지 (visit monitoring)
// didVisit : - 사용자가 어떤 장소에 도착해서 일정 시간 머무르고 떠났을 때 호출
// : - 보통은 머무른 시간이 수 분 이상이고, 이동이 명확하게 감지될 경우 호출
// : - iOS 시스템이 자동으로 의미 있는 장소를 인식해서 감지함
func locationManager(_ manager: CLLocationManager, didVisit visit: CLVisit) {
print("방문 감지됨 - 좌표: \(visit.coordinate), 도착: \(visit.arrivalDate), 출발: \(visit.departureDate)")
}
// 지오펜싱: 진입
// didEnterRegion : 사용자가 지정한 영역(CLCircularRegion) 안으로 들어갔을 때
func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
DispatchQueue.main.async {
self.didEnterGeofence = true
self.didExitGeofence = false
}
}
// 지오펜싱: 이탈
// didExitRegion : 사용자가 설정한 영역 밖으로 나갔을 때
func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) {
DispatchQueue.main.async {
self.didEnterGeofence = false
self.didExitGeofence = true
}
}
// 오류 처리
// didFailWithError : 위치 정보 요청 중 문제가 발생했을 때 호출됨
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("위치 오류: \(error.localizedDescription)")
}
}
Annotation
Marker 모델 → MapViewModel에 데이터 저장 → MapView에서 Map+Annotation으로 지도에 핀 뿌림
지피티 정리 굿
📌 @Namespace란 무엇인가요?
- @Namespace는 **SwiftUI에서 뷰 간 애니메이션(특히 일관된 전환)**을 할 때 사용하는 속성 래퍼입니다.
- 쉽게 말하면, “뷰들 사이에 연결 고리(공통된 공간)” 를 만들어주는 거예요.
- 이 공간을 이용하면, 다른 뷰로 이동하거나 변할 때 부드럽게 연결된 애니메이션을 만들 수 있습니다.
→ “이 뷰랑 저 뷰는 같은 그룹이야!”
→ SwiftUI가 알아서 자연스럽게 이어주는 개념
📌 Map에서 @Namespace를 왜 사용할까요?
- SwiftUI의 MapKit 컴포넌트(Map) 는 내부적으로 복잡한 상태를 관리합니다.
- 지도 안의 여러 요소들(마커, 유저 위치 버튼 등)을 조작할 때,
- 그냥 연결만 하면 오작동하거나 스코프를 제대로 인식 못할 수 있어요.
- 그래서 @Namespace로 하나의 “공통 스코프(mapScope)” 를 만들어줘서,
- 지도 안에 있는 여러 요소들 (Map, UserAnnotation, MapUserLocationButton)이
- 서로 같은 범위(scope) 안에서 동기화되게 작동하도록 하는 거예요.
간단히:
“지도와 지도 위 버튼이 서로 같은 공간(mapScope)을 공유해야 제대로 동작해!”
@Namespace는 여러 SwiftUI 뷰가 “같은 공간” 안에 있다는 걸 표시해서, 부드럽고 일관된 상태 변화나 애니메이션을 가능하게 해준다!
지오펜스
지도 위에 가상의 원(지역) 을 설정해놓고, 사용자가 그 안에 들어오거나 나가면 특정 이벤트를 발생시키는 기능
// MARK: - 지오펜싱
func startMonitoringGeofence(center: CLLocationCoordinate2D,
radius: CLLocationDistance,
identifier: String) {
let region = CLCircularRegion(center: center,
radius: radius,
identifier: identifier)
region.notifyOnEntry = true
region.notifyOnExit = true
locationManager.startMonitoring(for: region)
print("Monitoring regions: \(locationManager.monitoredRegions)")
}
특정 좌표(center) 와 반경(radius) 로 지오펜스(가상 울타리, 원) 를 만들어서 locationManager가 이 영역을 모니터링하도록 등록하는 함수
func stopMonitoringAllGeofences() {
for region in locationManager.monitoredRegions {
locationManager.stopMonitoring(for: region)
}
}
현재 등록된 모든 지오펜스를 하나하나 꺼버리는 함수
<지오펜스 진입/이탈을 감지하는 부분>
// 지오펜싱: 진입
func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
DispatchQueue.main.async {
self.didEnterGeofence = true
self.didExitGeofence = false
}
}
// 지오펜싱: 이탈
func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) {
DispatchQueue.main.async {
self.didEnterGeofence = false
self.didExitGeofence = true
}
}
- 사용자가 지오펜스 영역에 들어오면(didEnterRegion) → didEnterGeofence = true
- 사용자가 지오펜스 영역을 벗어나면(didExitRegion) → didExitGeofence = true
- SwiftUI 화면이나 다른 로직에서 이 값을 읽어서 특정 행동(알림, 화면 전환 등)을 할 수 있
지오코딩 vs 역지오코딩
지오코딩
: 주소 → 위도 / 경도 좌표로 변환하는 과정
사용자가 “서울특별시 관악구 흑석로 84”라는 텍스트를 입력했을 때
이걸 지도에 표시하려면 해당 주소가 정확히 어떤 지점인지 좌표로 바꿔야 합니다.
즉, 사람이 읽을 수 있는 주소를 지도가 이해할 수 있는 좌표로 바꾸는 과정을 말합니다.
역지오코딩
: 위도 / 경도 좌표 → 주소 정보로 변환하는 과정
사용자가 지도에서 아무 곳이나 꾹 눌렀을 때
그 위치에 해당하는 주소가 무엇인지 보여줘야 할 수 있습니다.
MLLocalSearch
: 사용자가 입력한 키워드에 해당하는 장소를 지도에서 검색하고 결과를 가져오는 도구
// MKLocalSearch.Request :어떤 키워드로, 어떤 위치 기준으로 검색할지 설정
let request = MKLocalSearch.Request()
request.naturalLanguageQuery = "카페"
request.region = MKCoordinateRegion(...)
// MKLocalSearch : 실제로 검색을 실행하는 객체
let search = MKLocalSearch(request: request)
// .start() : 비동기로 검색을 실행, 결과를 클로저로 변환
search.start { response, error in
if let mapItems = response?.mapItems {
// mapItems: [MKMapItem]
// 각 장소의 위치, 이름, 주소 등 포함
}
}
음 방향 전환 음음