|
| 1 | +import { useEffect, useState, useRef } from 'react'; |
| 2 | + |
| 3 | +interface UseGeolocationReturnType extends Partial<GeolocationCoordinates> { |
| 4 | + loading: boolean; |
| 5 | + error: GeolocationPositionError | null; |
| 6 | + timestamp: EpochTimeStamp | undefined; |
| 7 | +} |
| 8 | + |
| 9 | +/** |
| 10 | + * 사용자의 위치 정보를 가져오는 커스텀 훅 |
| 11 | + * |
| 12 | + * @param {PositionOptions} options - 위치 정보를 가져오는 옵션 |
| 13 | + * @param {boolean} options.enableHighAccuracy - 위치 정보를 높은 정확도로 수집할지 여부를 지정 (기본값은 false) |
| 14 | + * @param {number} options.timeout - 위치 정보를 가져오기 위해 대기할 최대 시간 (밀리초 단위) |
| 15 | + * @param {number} options.maximumAge - 위치 정보를 캐싱할 최대 시간 (밀리초 단위) |
| 16 | +
|
| 17 | +* @returns {UseGeolocationReturnType} 위치 정보와 상태를 포함하는 객체를 반환 |
| 18 | + * @returns {boolean} UseGeolocationReturnType.loading - 위치 정보를 가져오는 중인지 여부 |
| 19 | + * @returns {GeolocationPositionError | null} UseGeolocationReturnType.error - 위치 정보를 가져오는 중에 발생한 에러 |
| 20 | + * @returns {EpochTimeStamp | undefined} UseGeolocationReturnType.timestamp - 위치 정보의 타임스탬프 |
| 21 | + * @returns {number | undefined} UseGeolocationReturnType.latitude - 위도 정보 |
| 22 | + * @returns {number | undefined} UseGeolocationReturnType.longitude - 경도 정보 |
| 23 | + * @returns {number | undefined} UseGeolocationReturnType.altitude - 고도 정보 |
| 24 | + * @returns {number | undefined} UseGeolocationReturnType.accuracy - 위치 정보의 정확도 |
| 25 | + * @returns {number | undefined} UseGeolocationReturnType.altitudeAccuracy - 고도 정보의 정확도 |
| 26 | + * @returns {number | undefined} UseGeolocationReturnType.heading - 방향 정보 |
| 27 | + * @returns {number | undefined} UseGeolocationReturnType.speed - 속도 정보 |
| 28 | + */ |
| 29 | +const useGeolocation = ( |
| 30 | + options: PositionOptions = {} |
| 31 | +): UseGeolocationReturnType => { |
| 32 | + const isMounted = useRef(true); |
| 33 | + |
| 34 | + const [loading, setLoading] = useState(true); |
| 35 | + const [error, setError] = useState<GeolocationPositionError | null>(null); |
| 36 | + const [position, setPosition] = useState<GeolocationPosition | null>(null); |
| 37 | + |
| 38 | + const { enableHighAccuracy, timeout, maximumAge } = options; |
| 39 | + |
| 40 | + useEffect(() => { |
| 41 | + const handleSuccess = (position: GeolocationPosition) => { |
| 42 | + if (isMounted.current) { |
| 43 | + setPosition(position); |
| 44 | + setLoading(false); |
| 45 | + } |
| 46 | + }; |
| 47 | + |
| 48 | + const handleError = (err: GeolocationPositionError) => { |
| 49 | + if (isMounted.current) { |
| 50 | + setError(err); |
| 51 | + setLoading(false); |
| 52 | + } |
| 53 | + }; |
| 54 | + |
| 55 | + const handleReset = () => { |
| 56 | + setLoading(true); |
| 57 | + setError(null); |
| 58 | + setPosition(null); |
| 59 | + }; |
| 60 | + |
| 61 | + const watchId = navigator.geolocation.watchPosition( |
| 62 | + handleSuccess, |
| 63 | + handleError, |
| 64 | + options |
| 65 | + ); |
| 66 | + |
| 67 | + return () => { |
| 68 | + handleReset(); |
| 69 | + isMounted.current = false; |
| 70 | + navigator.geolocation.clearWatch(watchId); |
| 71 | + }; |
| 72 | + }, [enableHighAccuracy, timeout, maximumAge]); |
| 73 | + |
| 74 | + const { |
| 75 | + latitude, |
| 76 | + longitude, |
| 77 | + altitude, |
| 78 | + accuracy, |
| 79 | + altitudeAccuracy, |
| 80 | + heading, |
| 81 | + speed, |
| 82 | + } = position?.coords || {}; |
| 83 | + |
| 84 | + const timestamp = position?.timestamp ?? undefined; |
| 85 | + |
| 86 | + return { |
| 87 | + latitude, |
| 88 | + longitude, |
| 89 | + altitude, |
| 90 | + accuracy, |
| 91 | + altitudeAccuracy, |
| 92 | + heading, |
| 93 | + speed, |
| 94 | + timestamp, |
| 95 | + error, |
| 96 | + loading, |
| 97 | + }; |
| 98 | +}; |
| 99 | + |
| 100 | +export default useGeolocation; |
0 commit comments