본문 바로가기

개발로그/리액트네이티브

리액트 네이티브 현재 위치 정보 가져오기, 현재 위치를 구글맵에 표현하는 방법, Getting current location with a react native geolocation service

 

이전 포스팅: 리액트 네이티브에서 구글맵 적용하기

이전 포스팅에서는 리액트 네이티브 프로젝트에 구글맵을 적용하는 방법을 알아봤습니다. 현재 제가 개발하고 있는 프로젝트에서는 현재 위치를 기반으로 특정 범위 안에 있는 다른 유저들을 보여줘야 할 필요가 있는데요. 이를 위해서는 디바이스의 현재 위치를 지도 위에 표시해줘야 하고, 구글 맵이 처음 렌더링 되는 지점도 사용자의 현재 위치가 기준이 되어야 합니다.

현재 위치를 가져오기 위해서 react-native-gelocation-service라는 라이브러리를 사용했는데요. 만약 현재 위치를 가져와서 위치 정보를 그냥 보여준다거나, 다른 맵 라이브러리를 사용하는 경우에도 react-native-gelocation-service 라이브러리를 사용 하실 수 있습니다. 이번 포스팅에서는 react-native-gelocation-service 라이브러리를 통해 가져온 디바이스의 현재 위치를 기반으로 구글맵을 렌더링하는 것을 설명해보겠습니다.

 

버전 정보

- node: 12.16.3
- react: 16.13.1
- react-native: 0.63.2
- react-native-maps: 0.27.1
- react-native-geolocation-service: 5.0.0

 

핵심 요약

이 글은 아래 4단계로 요약할 수 있습니다. 디바이스의 현재 위치 정보를 가져와서 현재 위치를 구글 맵으로 띄우는 것 까지가 이 글의 목표입니다.

1. 위치 정보를 구하기 위한 라이브러리 설치
2. 라이브러리 사용 위한 iOS 프로젝트 설정(권한 설정, swift bridge 설정)
3. 라이브러리 사용 위한 android 프로젝트 설정(권한 설정)
4. 에뮬레이터(iOS/Android)의 위치 정보를 바꿔서 테스트해보기

 

1. 위치 정보를 구하기 위한 라이브러리 설치

아까 위치 정보를 가져오기 위해 react-native-gelocation-service 라이브러리를 사용한다고 했습니다. 우선 먼저 설치를 해야겠죠.

$ yarn add react-native-gelocation-service
$ cd ios
$ pod install
$ cd ..

이 포스팅은 react-native 0.60 버전 이상을 기준으로 작성되었기 때문에 autolinking이 적용됩니다. ios의 경우 pod install로 의존성 설치만 해주시면 됩니다. 라이브러리 설치는 끝났고 이제 해당 라이브러리를 iOS, Android 프로젝트에서 사용하기 위한 설정이 필요합니다.

 

2. 라이브러리 사용 위한 iOS 프로젝트 설정(권한 설정, swift bridge 설정)

 

2-1. 위치 정보에 관한 property list key 설정

 

코드 에디터에서 수정하는 경우
XCode에서 수정하는 경우

 

Info.plist에 다음의 property list key를 설정해줍니다. Build Target에 따라서 설정해야 하는 항목에 조금 차이가 있을 수 있습니다.

- NSLocationAlwaysAndWhenInUseUsageDescription: 애플리케이션을 사용할 때 디바이스의 위치 정보에 접근하는 경우
- NSLocationWhenInUseUsageDescription: 항상 위치 정보에 접근하는 경우
- NSLocationAlwaysUsageDescription: 항상 위치 정보에 접근하는 경우 (iOS 8.0 - 10.0)

해당 property list key와 함께 사용자에게 왜 이 정보가 필요한지 간단히 설명할 문장을 기입해줍니다. 아래 처럼 Info.plist에 추가하시면 됩니다. 이 작업은 에디터에서 하셔도 되고, XCode Info.plist에서 수정해주셔도 됩니다.

<key>NSLocationWhenInUseUsageDescription</key>
<string>서비스 동작을 위해 위치 정보가 필요합니다</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>서비스 동작을 위해 위치 정보가 필요합니다</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>서비스 동작을 위해 위치 정보가 필요합니다</string>

 

 

2-2. Swift Support를 위한 Swift file bridge 설정

해당 라이브러리 공식 문서를 확인해보면, iOS 부분은 Swift로 작성되었기 때문에 리액트 네이티브 프로젝트에 Swift Support를 추가해줄 필요가 있다고 합니다. 이 과정은 반드시 XCode를 열어서 Xcode를 통해 해주셔야 합니다.

1. XCode를 열어서 프로젝트 루트에서 New File 을 선택해줍니다.

 

2. Swift File을 선택하시고, Next를 눌러준 뒤, Create를 눌러서 생성해줍니다.

 

3. 그럼 XCode에서 이런 팝업 창이 뜹니다. Objective-C bridging header를 생성하겠냐고 물어보네요. Create Bridging Header를 클릭해줍니다.

 

2-3. iOS 실행 해보기

우선 iOS에서 위치 정보를 가져오기 위한 설정은 모두 끝났습니다. 제대로 동작하는지 한 번 실행해보겠습니다. 우선 위치 정보를 가져오려면 사용자가 위치 정보 수집에 동의해야 합니다. 일단 우리의 목표는 위치 정보를 제대로 가져와서 보여주는 것이기 때문에 간단하게 코드를 작성해보겠습니다.

import React, { useState, useEffect } from "react";

import Geolocation from "react-native-geolocation-service";
import MapView, { PROVIDER_GOOGLE } from "react-native-maps";
import styled from "styled-components";
import { Platform } from "react-native";

async function requestPermission() {
  try {
    if (Platform.OS === "ios") {
      return await Geolocation.requestAuthorization("always");
    }
  } catch (e) {
    console.log(e);
  }
}

function App() {
  const [location, setLocation] = useState();
  useEffect(() => {
    requestPermission().then(result => {
      if (result === "granted") {
        Geolocation.getCurrentPosition(
          pos => {
            console.log(pos);
          },
          error => {
            console.log(error);
          },
          {
            enableHighAccuracy: true,
            timeout: 3600,
            maximumAge: 3600,
          },
        );
      }
    });
  }, []);

  if (!location) {
    return (
      <View>
        <Text>Splash Screen</Text>
      </View>
    );
  }

  return (
    <>
      <View>
        <Map
          provider={PROVIDER_GOOGLE}
          initialRegion={{
            latitude: 37.78825,
            longitude: -122.4324,
            latitudeDelta: 0.0922,
            longitudeDelta: 0.0421,
          }}
        />
      </View>
    </>
  );
}

const View = styled.View`
  flex: 1;
`;

const Text = styled.Text`
  flex: 1;
`;

const Map = styled(MapView)`
  flex: 1;
`;

export default App;

 

 

※ 코드를 작성해서 실행 해보니 사용자에게 위치 정보 수집 권한을 요청할 때 "Allow While Using App" 밖에 되질 않네요. 왜 이런지 잠깐 찾아보니 사용자가 앱을 처음 실행할 때는 Allow While Using App으로 요청해야 하는 것 같습니다. (iOS 13부터 강제)

- 참고 링크 1
- 참고 링크 2

앱을 사용하는 동안 위치 정보를 수집하고 나중에 다시 한 번 always 권한을 요청하는 것은 가능한 것 같습니다. 지금 제가 개발하고 있는 앱의 경우 "항상 허용"이 필요해서 이 부분은 방법을 찾아봐야겠네요. 아무튼 getCurrentPosition 내부에 콜백 함수를 전달하면 위치 정보를 받아 올 수 있습니다. pos를 console에 찍어보면 다음 객체를 확인 할 수 있습니다.

{
  coords: {
    accuracy: 5,
    altitude: 0,
    altitudeAccuracy: -1,
    heading: -1,
    latitude: 37.55,
    longitude: 127.0165,
    speed: -1,
  },
  timestamp: 1597336350546.098,
};

이제 이걸 이용해서 latitude, longtitude를 구글 맵에 전달해주면 현재 위치를 구글맵으로 표현 가능합니다.

function App() {
  const [location, setLocation] = useState();
  useEffect(() => {
    requestPermission().then(result => {
      if (result === "granted") {
        Geolocation.getCurrentPosition(
          pos => {
            setLocation(pos.coords);
          },
          error => {
            console.log(error);
          },
          {
            enableHighAccuracy: true,
            timeout: 3600,
            maximumAge: 3600,
          },
        );
      }
    });
  }, []);

  if (!location) {
    return (
      <View>
        <Text>Splash Screen</Text>
      </View>
    );
  }

  return (
    <>
      <View>
        <Map
          provider={PROVIDER_GOOGLE}
          initialRegion={{
            latitude: location.latitude,
            longitude: location.longitude,
            latitudeDelta: 0.005,
            longitudeDelta: 0.005,
          }}
        />
      </View>
    </>
  );
}

 

현재 위치가 구글맵에 나온 모습

 

현재 위치가 구글맵에 잘 표현이 되네요. 에뮬레이터의 경우 지금 현재 위치가 아닌 기본적으로 설정되어 있는 위치 정보를 기반으로 나올겁니다. 아마 샌프란시스코로 나올텐데 이 부분은 조금 이따가 수정해보도록 하겠습니다. 만약 실제 디바이스로 개발하고 계신 경우라면 현재 위치가 제대로 나와야 합니다.

 

3. 라이브러리 사용 위한 android 프로젝트 설정(권한 설정)

iOS는 설정이 모두 끝났으니 이제 android 프로젝트를 설정해보겠습니다. iOS 보다 간단합니다.

AndroidManifest.xml에 다음 권한을 설정해줘야 합니다.

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

gradle 빌드를 해주시고, 이제 안드로이드 에뮬레이터에서 현재 위치를 띄워보겠습니다. 코드는 아래와 같습니다.

import React, { useState, useEffect } from "react";

import Geolocation from "react-native-geolocation-service";
import MapView, { PROVIDER_GOOGLE } from "react-native-maps";
import styled from "styled-components";
import { Platform, PermissionsAndroid } from "react-native";

async function requestPermission() {
  try {
    if (Platform.OS === "ios") {
      return await Geolocation.requestAuthorization("always");
    }
    // 안드로이드 위치 정보 수집 권한 요청
    if (Platform.OS === "android") {
      return await PermissionsAndroid.request(
        PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
      );
    }
  } catch (e) {
    console.log(e);
  }
}

function App() {
  const [location, setLocation] = useState();
  useEffect(() => {
    requestPermission().then(result => {
      console.log({ result });
      if (result === "granted") {
        Geolocation.getCurrentPosition(
          pos => {
            setLocation(pos.coords);
          },
          error => {
            console.log(error);
          },
          {
            enableHighAccuracy: true,
            timeout: 3600,
            maximumAge: 3600,
          },
        );
      }
    });
  }, []);

  if (!location) {
    return (
      <View>
        <Text>Splash Screen</Text>
      </View>
    );
  }

  return (
    <>
      <View>
        <Map
          provider={PROVIDER_GOOGLE}
          initialRegion={{
            latitude: location.latitude,
            longitude: location.longitude,
            latitudeDelta: 0.005,
            longitudeDelta: 0.005,
          }}
        />
      </View>
    </>
  );
}

const View = styled.View`
  flex: 1;
`;

const Text = styled.Text`
  flex: 1;
`;

const Map = styled(MapView)`
  flex: 1;
`;

export default App;

 

 

앱을 실행시키면 역시 현재 디바이스의 위치에 접근하는 것을 허용할지 묻는 팝업창이 제대로 뜨는 것을 확인할 수 있습니다. 백그라운드에서 위치 정보 수집을 활성화하려면 BACKGROUND LOCATION에 대한 권한이 필요합니다. 일단 AndroidManifest.xml 파일에 다음의 권한을 추가해줍니다.

<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />

그리고 구글 문서에 따르면 백그라운드 위치 정보 접근 기능은 점진적으로 요청하는 것을 권장한다고 합니다. 이 부분은 자세하게 알아볼 필요가 있기 때문에 추후에 다뤄보도록 하겠습니다. 다시 돌아와서, 사용자가 정보 수집에 동의하면, 구글맵이 현재 위치를 제대로 렌더링하는 것을 확인하실 수 있습니다.

 

 

4. 에뮬레이터(iOS/Android)의 위치 정보를 바꿔서 테스트해보기

에뮬레이터에 별다른 위치 정보 설정을 하지 않았다면 아마 현재 위치가 아닌 에뮬레이터에 설정된 위치가 구글맵에 표시될 것입니다. 테스트를 위해서 에뮬레이터의 위치 정보를 수정하는 방법을 작성해보겠습니다.

4-1. 안드로이드 설정 방법

에뮬레이터를 클릭해서 포커스 상태가 되면 에뮬레이터 옆에 더보기 버튼을 확인하실 수 있습니다. 이걸 클릭해주세요.

그럼 Location 탭을 확인하실 수 있는데요, 여기서 Single Points를 추가해주시면 됩니다. 위치 정보를 업데이트하고 다시 앱을 에뮬레이터에서 실행해보시면 설정한 정보로 렌더링되는 것을 확인하실 수 있습니다.

 

4-2. iOS 설정 방법

iOS의 경우 에뮬레이터를 활성화하고 Features - Location - Custom Location으로 들어가신 뒤에 위도와 경도를 입력해주시면 끝입니다.

 

마치며

지금까지 리액트 네이티브에서 현재 위치 정보를 가져와서 구글맵에 그려주는 것을 설명했습니다. 일단 구글 맵을 현재 위치 정보를 토대로 그려주는 것을 목표로 앱을 만들어보시고, 이게 다 되면 마커를 추가해보고, 위치 검색 기능도 넣어보고 여러 가지 기능을 추가해보실 수 있을겁니다. 저도 앞으로 앱을 계속 개발하면서 구글맵과 관련된 내용을 포스팅하도록 하겠습니다. 이 글이 조금이나마 도움이 되셨으면 좋겠네요 :)

리액트 네이티브에서 구글맵을 띄우는 방법이 궁금하시다면 이전 포스팅을 확인해주세요 :)

이전 포스팅: 리액트 네이티브에서 구글맵 적용하기


 

만약 이 포스팅이 조금이나마 도움이 되셨다면 커피 한 잔 사주시면 감사하겠습니다 ☺️

Buy me a coffee Buy me a coffee