React-Native

[React-Native] 현재 위치 불러오기 / Expo Location 사용하기

solfa 2024. 7. 9. 02:23

구현하고 싶은 것과 현재 상황

사진처럼 장소 추가하기에 현재 위치를 자동으로 불러오게 하고싶다!!!

 

 

Expo Location으로 위치를 불러왔던 경험이 있어서 이번에도 Expo Location을 사용해보기로 했다! 

RN은 공식문서가 굉장히 친절하고 좋으니까 공식 문서 잘 읽어보기~~

 

https://docs.expo.dev/versions/latest/sdk/location/#installation

 

Location

A library that provides access to reading geolocation information, polling current location or subscribing location update events from the device.

docs.expo.dev

 

구현 할 것

1. 위치 권한 물어보는 것

2. 서울특별시 00구까지 받아와서 바로 장소에 띄워주기

3. 권한 거부시 유저가 직접 입력하게 하기

4. 에러메시지 추가

5. 데이터 서버로 보내기


 

구현 방법

1. 라이브러리 설치하기

npx expo install expo-location

 

2. 코드 상단에 import 하기

import * as Location from 'expo-location';

 

3. 쓸 함수 결정하기

공식 문서에 들어가보면 함수들이 정말~~ 진짜 많다!!

기능이 비슷한 함수도 있고 이것 저것 많은데 그 것들 중 가장 많이 쓰이면서 최소한의 기능을 하는 함수들을 살펴보자!

 

3-1. 위치 권한 요청 함수

useBackgroundPermissions()
useForegroundPermissions()

 

useForegroundPermissions()

앱이 foreground에 있을 때만 위치 권한을 요청한다.

앱이 백그라운드에서 실행 중이거나 종료된 상태에서는 위치 권한을 요청할 수 없다.

보통 사용자가 앱을 사용하는 동안 필요한 위치 정보를 요청할 때 사용된다.

 

useBackgroundPermissions()

앱이 background에 있을 때도 위치 권한을 요청할 수 있다.

예를 들어, 위치 추적 앱이나 배달 서비스와 같이 앱이 백그라운드에서도 위치 정보를 사용해야 하는 경우에 사용된다.

백그라운드에서 위치 권한을 요청하는 것은 보안 및 개인 정보 보호 측면에서 추가적인 사용자 동의를 필요로 한다.

 

-> 보통 useForegroundPermissions 이걸 많이 씀! 나도 이거 쓸 거임

const [status, requestPermission] = Location.useForegroundPermissions();

 

이 함수들은 이런 식으로 사용할 수 있다!

useForegroundPermissions()은 두 개의 요소를 반환한다.

현재 위치 권한의 상태를 나타내는 status 와 사용자가 위치 권한을 허용하도록 요청하는 함수인 requestPermission 을 반환한다.

 

  • status
    • status는 현재 위치 권한의 상태를 나타낸다.
    • 이는 undetermined, granted, denied 중 하나!
    • undetermined: 사용자가 아직 권한 요청에 응답하지 않았음을 의미
    • granted: 사용자가 위치 권한을 허용했음을 의미
    • denied: 사용자가 위치 권한을 거부했음을 의미
    • 이 상태를 통해 사용자가 위치 서비스 활성화 여부도 판단하여 에러메시지나 직접 장소를 입력하게 할 예정!
  • requestPermission
    • requestPermission은 사용자가 위치 권한을 허용하도록 요청하는 함수이다.
    • 이 함수는 호출되었을 때 권한 대화 상자(그냥 동의 하시겠습니까? 이런 거)를 표시하여 사용자가 권한을 허용할지, 거부할지를 선택할 수 있게 한다.

 

 

3-2. 위치를 우편주소로 바꿔주는 함수

Location.reverseGeocodeAsync(location, options)

 

현재 위치의 좌표를 읽기 쉬운 주소 형식으로 변환해주는 함수이다.

이 함수는 특정 위치의 위도와 경도를 입력받아 해당 위치의 지리적 정보를 반환해주는 함수이다.

이렇게 위치를 꼭 우편주소로 바꿔줘야 내가 원하는 00시 00구 00동 이런 형식으로 얻을 수 있다!!!

옵션에는 많은 것들이 있는데 도시, 구, 동, 우편주소까지 전부 나온다!

 

  • location: 위도와 경도를 포함하는 객체 { latitude, longitude }.
  • options: 추가적인 옵션 (필수는 아님).

 

 

친절한 공식문서...

 

const location = await Location.getCurrentPositionAsync();

 

이렇게 변환 함수를 안써줬을 땐 

{
  "coords": {
    "accuracy": 5,
    "altitude": 0,
    "altitudeAccuracy": 0.5,
    "heading": 0,
    "latitude": 37.4945267,
    "longitude": 126.9598517,
    "speed": 0
  },
  "mocked": false,
  "timestamp": 1720458556325
}

 

이런식으로 어디에도 갖다 쓸 수 없는 json이 반환된다 흑흑

 

근데 저 반환 함수를 써준다면?!

 const { latitude, longitude } = location.coords;
      const reverseGeocode = await Location.reverseGeocodeAsync({
        latitude,
        longitude,
      });

+ 위에서 위도(latitude) 경도(longitude)만 빼와서 reverseGeocodeAsync 안에 넣어줘야 함!

[
  {
    "city": null,
    "country": "South Korea",
    "district": "Dongjak District",
    "formattedAddress": "50 Sadang-ro, Sangdo-dong, Dongjak District, Seoul, South Korea",
    "isoCountryCode": "KR",
    "name": "50",
    "postalCode": "07027",
    "region": "Seoul",
    "street": "Sangdo-dong",
    "streetNumber": "50",
    "subregion": null,
    "timezone": null
  }
]

 

위치 정보가 제대로 나오게 된다! 에뮬레이터나 시뮬레이터 설정을 한국어로 하면 한국어로 제대로 잘 뜬다.

저기에서 원하는 부분만 갖다 쓰면 된다.

const city = `${reverseGeocode[0].region}`;
const country = `${reverseGeocode[0].country}`;

 

이런 식으로! 한 번 해보면 엄청 쉽당

 

이렇게 함수 세 개 정도만 써주면 한 페이지 구현이 끝난 셈이당

 

이제

 

4. 코드 작성하기

import React, { useState, useEffect } from "react";
import { View, Text, TextInput, Button } from "react-native";
import * as Location from "expo-location";

const LocationComponent = () => {
  const [city, setCity] = useState("위치 가져오는중 ...");
  const [errorMsg, setErrorMsg] = useState(null);
  const [userCity, setUserCity] = useState("");
  const [locationEnabled, setLocationEnabled] = useState(true);

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

  const getLocation = async () => {
    try {
      const { status } = await Location.requestForegroundPermissionsAsync();
      if (status !== "granted") {
        setErrorMsg("위치 권한을 허용해야 합니다.");
        setLocationEnabled(false);
        return;
      }

      const location = await Location.getCurrentPositionAsync({ accuracy: 5 });
      const { latitude, longitude } = location.coords;
      const reverseGeocode = await Location.reverseGeocodeAsync({ latitude, longitude });

      const currentCity = `${reverseGeocode[0].region} ${
        reverseGeocode[0].street || reverseGeocode[0].district
      }`;

      setCity(currentCity);
      setUserCity(currentCity);
    } catch (error) {
      console.error("위치 정보를 가져오는 중 오류 발생:", error);
      setErrorMsg("위치 정보를 가져오는 중 오류가 발생했습니다.");
    }
  };

  return (
    <View>
      {locationEnabled ? (
        <TextInput
          placeholder="📌 장소 추가하기"
          value={userCity ? `📌 ${userCity}` : `📌 ${city}`}
          onChangeText={(text) => setUserCity(text)}
        />
      ) : (
        <View>
          <TextInput
            placeholder="장소를 직접 입력해주세요! ex)서울특별시 상도동"
            value={userCity}
            onChangeText={(text) => setUserCity(text)}
          />
          <Text style={{ textAlign: "center", color: "red" }}>{errorMsg}</Text>
        </View>
      )}
    </View>
  );
};

export default LocationComponent;

 

여기서 신경 쓴 점은 사용자가 직접 장소를 입력하게 하기 (권한 거부 시) 이 부분이다.

사용자가 위치 권한을 거부하면 직접 입력할 수 있게 했다. 원래 사용자가 위치 서비스를 활성화했는지 확인하는 함수(가 있더라 별 게 다있어 증말)를 통해 구분하려고 했는데 그냥 status( == 권한 허용 여부) 상태를 조건부 렌더링 하여 true면 가져온 주소를 보여주고 false면 입력 요청을 하게 했다.

  const [locationEnabled, setLocationEnabled] = useState(true);

  ...

  const getLocation = async () => {
    try {
      const { status } = await Location.requestForegroundPermissionsAsync();
      if (status !== "granted") {
        setErrorMsg("위치 권한을 허용해야 합니다.");
        setLocationEnabled(false);
        return;
      }

  ...

  return (
    <View>
      {locationEnabled ? (
        <TextInput
         ...
          value={`📌 ${city}`}
         ...
        />
      ) : (
        <View>
          <TextInput
            placeholder="장소를 직접 입력해주세요! ex)서울특별시 상도동"
            onChangeText={(text) => setUserCity(text)}
          />
        </View>
      )}
    </View>
  );
};

 

이런 식으로 코드를 작성했다! 조건부 렌더링을 알고나니 너무 편하당

 


 

완성된 화면

 

해당 탭에 들어가면 위치 권한 여부가 디바이스에 따라 자동으로 뜨고 주소까지 잘 받아와지는 모습을 볼 수 있당

rn 개발에서 너무나도 필수인 위치정보 가져오기를 사용해봤당!!!

rn 시리즈로 글 계속 써야겠다 빌드편까지 써볼게...

728x90