FRONTEND/ReactNative

[날씨앱] 현재 위치 확인 및 오픈웨더 API 사용하여 날씨 가져오기

죠으닝 2024. 2. 1. 10:18

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

 

Location

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

docs.expo.dev

 

vscode에서 현재 실행중인 터미널은 놔두고 

 

git bash로 하나 더 열어서 설치한다.

 

그러면 실행중에도 설치 가능함. 

 

 

 

그리고 app.json 에 아래의 플러그인을 추가한다. 

 

 

 

{
  "expo": {
    "plugins": [
      [
        "expo-location",
        {
          "locationAlwaysAndWhenInUsePermission": "Allow $(PRODUCT_NAME) to use your location."
        }
      ]
    ],
    "name": "my-app",
    "slug": "my-app",
    "version": "1.0.0",
    "orientation": "portrait",
    "icon": "./assets/icon.png",
    "userInterfaceStyle": "light",
    "splash": {
      "image": "./assets/splash.png",
      "resizeMode": "contain",
      "backgroundColor": "#ffffff"
    },

 

 

 

 

 

App.js로 돌아가서 

 

 

expo-location을 import 하고 

 

처음 시작 시 위치정보 가져오게끔 요청을 보낸다.

 

만약 허용을 하지 않으면 false 로 바꿈 

 

import { StatusBar } from "expo-status-bar";
import { Dimensions, ScrollView, StyleSheet, Text, View } from "react-native";
import * as Location from "expo-location";
import { useEffect, useState } from "react";

//각 스마트폰 화면의 가로 사이즈
const { width: SCREEN_WIDTH } = Dimensions.get("window");

export default function App() {
  const [location, setLocation] = useState();
  const [ok, setOk] = useState(true);
  const ask = async () => {
    const { granted } = await Location.requestForegroundPermissionsAsync();
    if (!granted) {
      setOk(false);
    }
  };
  useEffect(() => {
    ask();
  }, []);

 

 

아래와 같이 뜬다

 

앱 사용 중에만 허용 클릭 

 

 

 

이제 실제로 로케이션 객체에서 위도, 경도를 가져와 위치 정보를 알아내기.

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

 


export default function App() {
  const [location, setLocation] = useState();
  const [ok, setOk] = useState(true);
  const ask = async () => {
    const { granted } = await Location.requestForegroundPermissionsAsync();
    if (!granted) {
      setOk(false);
    }
    //accuracy 5 -> 정확도 5단계 제일 높음
    const {
      coords: { latitude, longitude },
    } = await Location.getCurrentPositionAsync({ accuracy: 5 });
  };

 

 

 

import { StatusBar } from "expo-status-bar";
import { Dimensions, ScrollView, StyleSheet, Text, View } from "react-native";
import * as Location from "expo-location";
import { useEffect, useState } from "react";

//각 스마트폰 화면의 가로 사이즈
const { width: SCREEN_WIDTH } = Dimensions.get("window");

export default function App() {
  const [location, setLocation] = useState();
  const [ok, setOk] = useState(true);
  const ask = async () => {
    const { granted } = await Location.requestForegroundPermissionsAsync();
    if (!granted) {
      setOk(false);
    }
    //경도와 위도 값을 가지고 옴
    //accuracy 5 -> 정확도 5단계 제일 높음
    const {
      coords: { latitude, longitude },
    } = await Location.getCurrentPositionAsync({ accuracy: 5 });
    // 가지고 온 경도, 위도 값에 맍는 위치를 파악할 수 있는 API사용해서 요청
    const location = await Location.reverseGeocodeAsync(
      { latitude, longitude },
      { useGoogleMaps: false }
    );
     console.log(location);
  };

 

 

콜솔로 위치를 잘 가져왔는데 확인한다. 

콘솔은 터미널에서 확인가능하다

 

 

위와 같이 현재 위치가 잘 출력된다. 

 

이것을 state로 저장해서 화면에 보여주면 됨 

 

 

import { StatusBar } from "expo-status-bar";
import { Dimensions, ScrollView, StyleSheet, Text, View } from "react-native";
import * as Location from "expo-location";
import { useEffect, useState } from "react";

//각 스마트폰 화면의 가로 사이즈
const { width: SCREEN_WIDTH } = Dimensions.get("window");

export default function App() {
  const [city, setCity] = useState("Loading...");
  const [location, setLocation] = useState();
  const [ok, setOk] = useState(true);
  const ask = async () => {
    const { granted } = await Location.requestForegroundPermissionsAsync(); //위치 정보 사용자 동의 얻기
    if (!granted) {
      setOk(false);
    }
    //경도와 위도 값을 가지고 옴
    //accuracy 5 -> 정확도 5단계 제일 높음
    const {
      coords: { latitude, longitude },
    } = await Location.getCurrentPositionAsync({ accuracy: 5 });
    // 가지고 온 경도, 위도 값에 맍는 위치를 파악할 수 있는 API사용해서 요청
    const location = await Location.reverseGeocodeAsync(
      { latitude, longitude },
      { useGoogleMaps: false }
    );

    console.log(location);
    setCity(location[0].city || location[0].district);
  };

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

  return (
    <View style={styles.container}>
      <View style={styles.city}>
        <Text style={styles.cityName}>{`${city}`}</Text>
      </View>
      {/* //스크롤뷰를 가로로 해서 수평으로 스크롤하고 스크롤바를 삭제함. */}
      <ScrollView
        horizontal
        showsHorizontalScrollIndicator={false}
        style={styles.weather}
      >
        <View style={styles.day}>
          <Text style={styles.temp}>27°</Text>
          <Text style={styles.description}>Sunny</Text>
        </View>
        <View style={styles.day}>
          <Text style={styles.temp}>28°</Text>
          <Text style={styles.description}>Sunny</Text>
        </View>
        <View style={styles.day}>
          <Text style={styles.temp}>29°</Text>
          <Text style={styles.description}>Sunny</Text>
        </View>
        <View style={styles.day}>
          <Text style={styles.temp}>30°</Text>
          <Text style={styles.description}>Sunny</Text>
        </View>
        <View style={styles.day}>
          <Text style={styles.temp}>31°</Text>
          <Text style={styles.description}>Sunny</Text>
        </View>
      </ScrollView>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: "#FFF3CF",
  },
  city: {
    flex: 1.2,
    justifyContent: "center",
    alignItems: "center",
  },
  cityName: {
    fontSize: 68,
    fontWeight: "500",
  },
  weather: {},
  day: {
    flex: 1,
    width: SCREEN_WIDTH,
    alignItems: "center",
  },
  temp: {
    marginTop: 50,
    fontWeight: "600",
    fontSize: 170,
  },
  description: {
    marginTop: -30,
    fontSize: 60,
  },
});

 

 

화면에 서울 -> 부산진구로 출력된다. 

 

 

지역에 맞는 날씨를 가져오기 위해 오픈웨더 API 서비스를 이용한다. 

 

https://openweathermap.org/forecast16

 

Daily Forecast 16 Days - OpenWeatherMap

 

openweathermap.org

 

 

 

lat={lat}&lon={lon}

 

우리가 가져온 위도 경도를 넣어주면 됨. 

 

import { StatusBar } from "expo-status-bar";
import {
  ActivityIndicator,
  Dimensions,
  ScrollView,
  StyleSheet,
  Text,
  View,
} from "react-native";
import * as Location from "expo-location";
import { useEffect, useState } from "react";

//각 스마트폰 화면의 가로 사이즈
const { width: SCREEN_WIDTH } = Dimensions.get("window");
const API_KEY = "784ab24ff2ed5d94d4288abed9e25d13";

export default function App() {
  const [city, setCity] = useState("Loading...");
  const [days, setDays] = useState([]);
  const [location, setLocation] = useState();
  const [ok, setOk] = useState(true);
  const weather = async () => {
    const { granted } = await Location.requestForegroundPermissionsAsync(); //위치 정보 사용자 동의 얻기
    if (!granted) {
      setOk(false);
    }
    //경도와 위도 값을 가지고 옴
    //accuracy 5 -> 정확도 5단계 제일 높음
    const {
      coords: { latitude, longitude },
    } = await Location.getCurrentPositionAsync({ accuracy: 5 });
    // 가지고 온 경도, 위도 값에 맍는 위치를 파악할 수 있는 API사용해서 요청
    const location = await Location.reverseGeocodeAsync(
      { latitude, longitude },
      { useGoogleMaps: false }
    );

    // console.log(location);
    setCity(location[0].city || location[0].district); // city값이 null일 경우에은 district가 들어감
    const response = await fetch(
      `https://api.openweathermap.org/data/2.5/onecall?lat=${latitude}&lon=${longitude}&exclude=alerts&appid=${API_KEY}&units=metric`
    );
    const json = await response.json();
    console.log(json);
    setDays(json.daily);
  };

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

  return (
    <View style={styles.container}>
      <View style={styles.city}>
        <Text style={styles.cityName}>{`${city}`}</Text>
      </View>
      {/* //스크롤뷰를 가로로 해서 수평으로 스크롤하고 스크롤바를 삭제함. */}
      <ScrollView
        pagingEnabled
        horizontal
        showsHorizontalScrollIndicator={false}
        style={styles.weather}
      >
        {days.length === 0 ? (
          <View style={styles.day}>
            {/* 없을 경우에는 로딩 표시  */}
            <ActivityIndicator
              color="white"
              style={{ marginTop: 10 }}
              size="large"
            />
          </View>
        ) : (
          days.map((day, index) => (
            <View key={index} style={styles.day}>
              <Text style={styles.temp}>
                {parseFloat(day.temp.day).toFixed(1)}
              </Text>
              <Text style={styles.description}>{day.weather[0].main}</Text>
              <Text style={styles.tinyText}>{day.weather[0].description}</Text>
            </View>
          ))
        )}
      </ScrollView>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: "#FFF3CF",
  },
  city: {
    flex: 1.2,
    justifyContent: "center",
    alignItems: "center",
  },
  cityName: {
    fontSize: 68,
    fontWeight: "500",
  },
  weather: {},
  day: {
    flex: 1,
    width: SCREEN_WIDTH,
    alignItems: "center",
  },
  temp: {
    marginTop: 50,
    fontWeight: "600",
    fontSize: 170,
  },
  description: {
    marginTop: -30,
    fontSize: 60,
  },
});

 

 

 

https://jsonformatter.curiousconcept.com/#