"내 주변" 기능을 붙이려다 위치 권한에서 한참 헤맸다. 좌표를 받는 것 자체는 몇 줄인데, 권한을 물어보는 타이밍과 거부됐을 때 처리를 대충 짜니까 앱이 계속 멈춰 있는 것처럼 보였다. Expo에서는 expo-location으로 이걸 정리할 수 있다.
설치와 권한 설정
npx expo install expo-location
위치는 민감 권한이라 app.json에 사용 목적 문구를 넣어야 한다. 안 넣으면 iOS 심사에서 바로 반려된다.
{
"expo": {
"plugins": [
[
"expo-location",
{ "locationWhenInUsePermission": "주변 매장을 찾기 위해 위치를 사용합니다." }
]
]
}
}
권한 먼저, 좌표는 그 다음
핵심은 순서다. 무조건 getCurrentPositionAsync부터 부르지 말고, 권한을 요청해서 승인됐을 때만 좌표를 받아야 한다.
import * as Location from 'expo-location';
import { useState } from 'react';
import { Button, Text, View } from 'react-native';
export function NearbyButton() {
const [address, setAddress] = useState<string | null>(null);
const [error, setError] = useState<string | null>(null);
const findMe = async () => {
const { status } = await Location.requestForegroundPermissionsAsync();
if (status !== 'granted') {
setError('위치 권한이 필요합니다. 설정에서 허용해 주세요.');
return;
}
const { coords } = await Location.getCurrentPositionAsync({
accuracy: Location.Accuracy.Balanced,
});
// 좌표를 사람이 읽을 주소로 변환
const [place] = await Location.reverseGeocodeAsync(coords);
setAddress(`${place.city} ${place.district ?? ''} ${place.street ?? ''}`.trim());
};
return (
<View>
<Button title="내 위치 찾기" onPress={findMe} />
{address && <Text>{address}</Text>}
{error && <Text>{error}</Text>}
</View>
);
}
reverseGeocodeAsync가 은근 유용하다. 위도·경도만 있으면 도시·구·도로명을 돌려줘서, 지도 없이 텍스트로만 "여기가 어디"를 보여줄 때 별도 API를 붙일 필요가 없다.
두 가지만 챙기면 된다
- 정확도는 상황에 맞게.
Accuracy.Highest는 GPS를 세게 돌려서 배터리를 먹는다. 매장 찾기 정도면Balanced로 충분하고, 응답도 더 빠르다. - 거부 상태를 항상 처리하자. 사용자가 한 번 거부하면 다음부터 시스템 팝업이 안 뜬다. 이럴 땐 앱 안에서 안내 문구를 띄우고
Linking.openSettings()로 설정 화면으로 보내주는 게 정석이다.
권한 요청 → 승인 확인 → 좌표 → 주소 변환. 이 순서만 지키면 위치 기능은 생각보다 금방 붙는다. 실시간 추적이 필요하면 watchPositionAsync로 넘어가면 되는데, 대부분의 "내 주변" 기능은 이 한 번의 조회면 끝난다.