📱Mobile/React Native

[React Native] react-native-geolocation-service 라이브러리를 사용해보자

뉴발자 2024. 1. 18.
728x90

 

 

 

 

 

 

 

 

 

 

 

 

 

 

그림 1-1. React Native

 

 

개요

프로젝트를 진행하면서 위치 서비스를 사용하는 일이 생겼고 자연스럽게 Geofence에 대해 알게 되었다.

 

Geofence의 개념과 라이브러리에 대해 관심이 생겼고 간단한 프로젝트를 구현해보려고 한다.

 

 

Geofence

Geofence란 지리(Geography)와 울타리(Fence)를 결합한 것이다.

 

쉽게 말하자면 특정 위치로부터 반경 x미터의 가상 구역을 만드는 것을 의미한다.

 

Geofence를 사용하는 것을 Geofencing이라고 부른다.

 

 

Geofence는 위도와 경도를 입력받아 하버사인 공식(Haversine Formula)을 이용해서 가상 구역을 만든다.

하버사인 공식이란?

 구면 삼각법에서 지구와 같은 구면의 두 점 사이의 대원 거리를 계산하는 공식

 

 

하버사인 공식

하버사인 공식을 구현한 코드는 다음과 같다.

const EARTH_RADIUS = 6371; // 지구 반지름 (km)
const degToRad = (deg: number) => deg * (Math.PI / 180); // 각도를 라디안으로 변환하는 함수

const latitudeDiff = degToRad(latitude - nowLatitude);
const longitudeDiff = degToRad(longitude - nowLongitude);
const sinLatDiff = Math.sin(latitudeDiff / 2) ** 2;
const sinLonDiff = Math.sin(longitudeDiff / 2) ** 2;
const cosNowLat = Math.cos(degToRad(nowLatitude));
const cosLat = Math.cos(degToRad(latitude));
const centralAngle = 2 * Math.atan2(Math.sqrt(sinLatDiff + cosNowLat * cosLat * sinLonDiff), Math.sqrt(1 - sinLatDiff - cosNowLat * cosLat * sinLonDiff));

const distance = Math.round(EARTH_RADIUS * centralAngle * 1000); // 단위를 km에서 m로 변환

 

위 공식은 이번 프로젝트에서는 사용하진 않을 것이기 때문에 바로 코드를 작성한다.

728x90

 

 

코드

위치 권한을 얻는 Authority 와 위치를 보여주는 Main 두 개의 스크린으로 작성했다.

 

사용 라이브러리

> @react-navigation/native
> @react-navigation/native-stack
> react-native-geolocation-service
> react-native-permissions
> react-native-safe-area-context
> react-native-screens
> styled-components
> zustand

 

Authority.tsx

import React, { useCallback, useEffect } from "react";
import { PermissionsAndroid } from "react-native";

import { AuthorityProps } from "../navigation/navigation";
import { REQUIRE_LOCATION_PERMISSIONS, checkPermissions } from '../utils/permission';
import { Layout, NextButton } from "../utils/styles";

const Authority = ({route, navigation}: AuthorityProps) => {
  // 위치 권한 상태 체크
  const onLocationPermissionsCheck = useCallback(async () => {
    const { result, permission } = await checkPermissions(REQUIRE_LOCATION_PERMISSIONS);

    if ( !result && permission ) {
      PermissionsAndroid.request(
        PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION
      )
    }
  }, []);

  useEffect(() => {
    onLocationPermissionsCheck();
  }, []);

  return (
    <Layout>
      <NextButton 
        title="Next" 
        onPress={() => navigation.navigate("Main")} 
      />
    </Layout>
  );
};

export default Authority;

 

Main.tsx

import React, { useEffect } from "react";
import { SafeAreaView } from "react-native";
import { MainProps } from "../navigation/navigation";
import useGeolocation from "../hooks/useGeolocation";
import useDataStore from "../store/useDataStore";
import { Layout, TextFont } from "../utils/styles";

const Main = ({route, navigation}: MainProps) => {
  const { position } = useDataStore();
  const { onWatchPosition, onClearWatch } = useGeolocation();

  useEffect(() => {
    onWatchPosition();

    return () => onClearWatch();
  }, [position]);

  return (
    <SafeAreaView>
      <Layout>
        <TextFont>latitude : {position.latitude}</TextFont>
        <TextFont>longitude : {position.longitude}</TextFont>
      </Layout>
    </SafeAreaView>
  );
};

export default Main;

 

useGeolocation.ts

import { useCallback } from "react";
import GeoLocation from "react-native-geolocation-service";
import useDataStore from "../store/useDataStore";

let watchId = -1;

const getWatchId = () => watchId;

const setWatchId = (id: number) => {
  watchId = id;
};

const useGeolocation = () => {
  const { position, setPosition } = useDataStore();

  const onClearWatch = useCallback(() => {
    if( getWatchId() !== -1 ) {
      GeoLocation.clearWatch(watchId);
      GeoLocation.stopObserving();
    }
  }, []);

  const onWatchPosition = useCallback(() => {
    const id = GeoLocation.watchPosition(
      (position) => {
      const { latitude, longitude } = position.coords;
      
      setPosition({latitude, longitude});
    },
    (error) => {
      console.log(error);
    },
    {
      accuracy: {
        android: "high",
      },
      enableHighAccuracy: true, // 정확한 위치 추적
    });

    setWatchId(id);
  }, []);

  return {
    onWatchPosition,
    onClearWatch,
  }
};

export default useGeolocation;

 

코드 동작 순서

  1. Authority에서 위치 권한을 받아온다.
  2. Next 버튼을 터치하면 Main으로 이동해서 useGeolocation의 onWatchPosition() 메소드를 실행한다.
  3. onWatchPosition() 메소드에서 받은 현재 위치를 상태 관리 라이브러리에 저장한다.
  4. 저장된 position 값을 Main 화면에 출력한다.

 

전체 코드

https://github.com/eoqna/geofence_rn

 

GitHub - eoqna/geofence_rn

Contribute to eoqna/geofence_rn development by creating an account on GitHub.

github.com

 

 

테스트 및 동작 확인

그림 2-1. 구현 화면

 

 

참고 사이트

https://www.npmjs.com/package/react-native-geolocation-service#API

 

react-native-geolocation-service

React native geolocation service for iOS and android. Latest version: 5.3.1, last published: a year ago. Start using react-native-geolocation-service in your project by running `npm i react-native-geolocation-service`. There are 57 other projects in the np

www.npmjs.com

 

https://velog.io/@eunddodi/ReactNative%EB%A1%9C-%EB%9F%AC%EB%8B%9D-%EC%96%B4%ED%94%8C-%EB%A7%8C%EB%93%A4%EA%B8%B0-Geolocation%EC%9C%BC%EB%A1%9C-%EC%82%AC%EC%9A%A9%EC%9E%90-%EB%9F%AC%EB%8B%9D-%ED%8A%B8%EB%9E%98%ED%82%B9%ED%95%98%EA%B8%B0

 

ReactNative로 러닝 어플 만들기 - Geolocation으로 사용자 러닝 트래킹하기

react-native-geolocation-service 활용하기

velog.io

 

 

 

 

 

 

 

 

 

 

728x90

댓글