어떤 서비스를 이용할때 내 위치 정보에 접근해도 되는지 묻는 창이 나올때가 있다.
동의 버튼을 누르면 내 위치 기반 데이터가 화면에 나타나는데, 이를 지원해주는 API 가 바로 Geolocation API 이다.
나는 사이트에 접속하면 내 위치 기반 주변 음식점을 불러오도록 구현하고 싶었기에 Geolocation API 를 사용하기로 했다.
Geolocation API 는 navigator.geolocation 을 통해 접근할 수 있다.
이때 브라우저는 사용자에게 위치 정보 접근 권한을 요청하게 되고, 사용자가 동의하는 경우 위치 정보를 가져온다.
이때 정보를 가져오는 방법으로는 두가지가 있다.
두 메서드는 모두 세개의 매개변수를 받는다.
콜백함수
콜백함수
옵션값
navigator.geolocation.getCurrentPosition((position) => {
// 성공했을 때 실행될 콜백함수
const { latitude, longitude } = position.coords;
},
// 실패했을 때 실행될 콜백함수
(err) => console.error(err),
// 옵션값
{ enableHighAccuracy: false, timeout: 5000, maximumAge: 0 }
)
옵션 값으로는 아래와 같다.
{
enableHighAccuracy: boolean // 높은 정확도의 위치 정보를 구할지 여부
timeout: number // 위치 정보 읽기 타임아웃(밀리초)
maximumAge: number // 위치 정보의 캐시 기한 (0이면 캐시안함)
}
나는 위치를 불러오는 로직을 useGeoLocation 훅으로 작성했다.
위치를 불러오는데 성공할 경우에는 위치에서 경도와 위도를 불러와 setLocation 값에 담아준다.
근데 만약에 사용자가 위치 정보를 제공하는 것을 거부한다면 어떻게 처리해야 할까?
'use client';
import { useCallback, useEffect, useState } from 'react';
type LocationType = {
latitude: number;
longitude: number;
};
export const useGeoLocation = () => {
const [location, setLocation] = useState<LocationType>();
useEffect(() => {
const { geolocation } = navigator;
if (!geolocation) return;
geolocation.getCurrentPosition(
(position) => {
const { latitude, longitude } = position.coords;
setLocation({
latitude,
longitude,
});
},
(err) => console.log(err)
{ enableHighAccuracy: true, timeout: 5000, maximumAge: 0 }
);
}, []);
return {
curLocation: location,
};
};
만약 사용자가 위치 정보를 동의하는 것을 거부하거나 혹은 현재 위치가 조회가 안되는 곳이라면 두번째 매개변수인 에러 콜백을 통해 처리하면 되는데, 이 부분을 에러처리 로직으로 수정해보자.
두번째 콜백 인자로는 PositionError 객체를 받는다.
PositionError 객체의 주요 속성으로는 code 가 있는데, 이를 활용해 swtich 문으로 작성해줬다.
근데 또 다른 고민이 생겼다.
만약 사용자가 위치 정보를 제공하는 것을 거부할 경우, 화면에는 어떠한 데이터도 보여지지 않게 된다. 그럴 경우엔 기본 위치를 제공하여 빈 화면이 보여지는 것을 막을 수 있다.
const showError = useCallback((error: GeolocationPositionError) => {
switch (error.code) {
case error.PERMISSION_DENIED:
console.log('사용자가 위치 정보를 제공하는 것을 거부했습니다.');
break;
case error.POSITION_UNAVAILABLE:
console.log('위치 정보를 사용할 수 없습니다.');
break;
case error.TIMEOUT:
console.log('위치 정보를 가져오는 요청이 시간 초과되었습니다.');
break;
default:
console.log('알 수 없는 오류가 발생했습니다.');
break;
}
}, []);
useEffect(() => {
const { geolocation } = navigator;
if (!geolocation) return;
geolocation.getCurrentPosition(
(position) => {
const { latitude, longitude } = position.coords;
setLocation({
latitude,
longitude,
});
},
(err) => showError(err)
{ enableHighAccuracy: true, timeout: 5000, maximumAge: 0 }
);
}, []);
만약 사용자가 위치 동의를 거부할 경우, 나는 대신 화면에 띄어질 기본 위치를 미리 설정해줬다.
그리고 이 함수를 showError 함수에서 사용자가 위치 정보 제공을 거부할경우, 호출 되도록 swtich 문 안에 작성해줬다.
const setDefaultLocation = () => {
const defaultLatitude = 37.579293849225756;
const defaultLongitude = 126.97798076343491;
setLocation({
latitude: defaultLatitude,
longitude: defaultLongitude,
});
};
const showError = useCallback((error: GeolocationPositionError) => {
switch (error.code) {
case error.PERMISSION_DENIED:
console.log('사용자가 위치 정보를 제공하는 것을 거부했습니다.');
setDefaultLocation(); // 여기에서 호출!
break;
case error.POSITION_UNAVAILABLE:
console.log('위치 정보를 사용할 수 없습니다.');
break;
case error.TIMEOUT:
console.log('위치 정보를 가져오는 요청이 시간 초과되었습니다.');
break;
default:
console.log('알 수 없는 오류가 발생했습니다.');
break;
}
}, []);
'use client';
import { useCallback, useEffect, useState } from 'react';
type LocationType = {
latitude: number;
longitude: number;
};
export const useGeoLocation = () => {
const [location, setLocation] = useState<LocationType>();
const [isLoading, setIsLoading] = useState<boolean>(false);
const [errorMsg, setErrorMsg] = useState<string>('');
const setDefaultLocation = () => {
const defaultLatitude = 37.579293849225756;
const defaultLongitude = 126.97798076343491;
setLocation({
latitude: defaultLatitude,
longitude: defaultLongitude,
});
};
const showError = useCallback((error: GeolocationPositionError) => {
switch (error.code) {
case error.PERMISSION_DENIED:
setErrorMsg('사용자가 위치 정보를 제공허는 것을 거부했습니다. ');
setDefaultLocation();
break;
case error.POSITION_UNAVAILABLE:
setErrorMsg('위치 정보를 사용할 수 없습니다.');
break;
case error.TIMEOUT:
setErrorMsg('위치 정보를 가져오는 요청이 시간 초과되었습니다.');
break;
default:
setErrorMsg('알 수 없는 오류가 발생했습니다.');
break;
}
setIsLoading(false);
}, []);
useEffect(() => {
const { geolocation } = navigator;
if (!geolocation) return;
setIsLoading(true);
geolocation.getCurrentPosition(
(position) => {
const { latitude, longitude } = position.coords;
setLocation({
latitude,
longitude,
});
setIsLoading(false);
},
(err) => showError(err),
{ enableHighAccuracy: true, timeout: 5000, maximumAge: 0 }
);
}, [showError]);
return {
curLocation: location,
isLoading,
errorMsg,
};
};