[React Native 101] #4. Location & Weather & Icons
~2.10 까지의 강의
이제 위치정보를 가지고 올 건데, expo install expo-location 를 한다.
https://docs.expo.dev/versions/latest/sdk/location/
Location - Expo Documentation
Expo is an open-source platform for making universal native apps for Android, iOS, and the web with JavaScript and React.
docs.expo.dev
유저의 권한을 요청할 수 있고, 유저의 마지막 정보를 가지고 올 수 있고,
위치를 알 수도 있다.
App.js
import * as Location from "expo-location";
import React, { useEffect, useState } from "react";
import { View, Text, Dimensions, StyleSheet, ScrollView } from "react-native";
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);
}
const {
coords: { latitude, longitude },
} = await Location.getCurrentPositionAsync({ accuracy: 5 });
const location = await Location.reverseGeocodeAsync(
{ latitude, longitude },
{ useGoogleMaps: false }
);
//setCity(location[0].city);
setCity(location[0].region);
};
useEffect(() => {
ask();
}, []);
먼저 권한을 요청해야 한다. 앱 사용중에만 허용으로 한다.(requsetForegroundPermissionAsync)
허가를 받지 못했을 경우, 다른 UI를 준비한다.
그리고, 정확도를 높여주는 것을 한다.(getCurrentPositionAsync)
그리고 경도, 위도를 가지고 reverseGeocoding을 한다.
option은 usegooglemap은 false로 한다.
console로 찍어보면, 나의 위치 정보를 확인할 수 있다.
location은 array이고, 니꼬쌤의 코드는 city인데 보다시피 city가 null이라 출력이 되지 않는다.
그래서 그냥 region으로 하기로 결정!
일기예보를 제공하는 api를 사용할 것이기 때문에, location을 날씨로 바꿔줄 것이다.
정보를 받아오기 위해, open weater api를 사용한다.
사이트에 회원가입하고, api를 부여받는다.
원래 어플리케이션에 api키가 있으면 안되지만, one call api로 간다.
문서에서 하라는 대로 하고, console에 찍어보면,
이렇게 다양한 날씨의 정보를 확인할 수 있다.
App.js
import * as Location from "expo-location";
import React, { useEffect, useState } from "react";
import {
View,
Text,
Dimensions,
ActivityIndicator,
StyleSheet,
ScrollView,
} from "react-native";
const { width: SCREEN_WIDTH } = Dimensions.get("window");
const API_KEY = "API키";
export default function App() {
const [city, setCity] = useState("Loading...");
const [days, setDays] = useState([]);
const [ok, setOk] = useState(true);
const getWeather = async () => {
const { granted } = await Location.requestForegroundPermissionsAsync();
if (!granted) {
setOk(false);
}
const {
coords: { latitude, longitude },
} = await Location.getCurrentPositionAsync({ accuracy: 5 });
const location = await Location.reverseGeocodeAsync(
{ latitude, longitude },
{ useGoogleMaps: false }
);
//setCity(location[0].city);
setCity(location[0].region);
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();
setDays(json.daily);
};
useEffect(() => {
getWeather();
}, []);
return (
<View style={styles.container}>
<View style={styles.city}>
<Text style={styles.cityName}>{city}</Text>
</View>
<ScrollView
pagingEnabled
horizontal
showsHorizontalScrollIndicator={false}
contentContainerStyle={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>
);
}
날짜를 json.daily안에 세팅한다.
문자의 길이가 0이면 loading을 나오게 하는데,
loading의 모습을 꾸미기 위해 actividyindicator를 사용한다.
그리고 map을 사용해서 온도와 날씨 등을 나타낸다.
그리고 날씨 사이트가 우리 나라랑 날라서 &units=metric 이것을 추가해주어야 한다. (doc참조)
이제 아이콘을 넣는 방법을 알아보도록 하자.
expo init을 한다면 expo icon을 사용할 수 있을 것이다.
icons.expo.fyi 에 들어가면 아이콘을 확인할 수 있다.
그래서 import하고, temperature에서 아이콘을 넣는다.
App.js
import * as Location from "expo-location";
import { StatusBar } from "expo-status-bar";
import React, { useEffect, useState } from "react";
import {
View,
Text,
Dimensions,
ActivityIndicator,
StyleSheet,
ScrollView,
} from "react-native";
import { Fontisto } from "@expo/vector-icons";
const { width: SCREEN_WIDTH } = Dimensions.get("window");
const API_KEY = "API키";
const icons = {
Clouds: "cloudy",
Clear: "day-sunny",
Atmosphere: "cloudy-gusts",
Snow: "snow",
Rain: "rains",
Drizzle: "rain",
Thunderstorm: "lightning",
};
export default function App() {
const [city, setCity] = useState("Loading...");
const [days, setDays] = useState([]);
const [ok, setOk] = useState(true);
const getWeather = async () => {
const { granted } = await Location.requestForegroundPermissionsAsync();
if (!granted) {
setOk(false);
}
const {
coords: { latitude, longitude },
} = await Location.getCurrentPositionAsync({ accuracy: 5 });
const location = await Location.reverseGeocodeAsync(
{ latitude, longitude },
{ useGoogleMaps: false }
);
//setCity(location[0].city);
setCity(location[0].region);
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();
setDays(json.daily);
};
useEffect(() => {
getWeather();
}, []);
return (
<View style={styles.container}>
<StatusBar style="light" />
<View style={styles.city}>
<Text style={styles.cityName}>{city}</Text>
</View>
<ScrollView
pagingEnabled
horizontal
showsHorizontalScrollIndicator={false}
contentContainerStyle={styles.weather}
>
{days.length === 0 ? (
<View style={{ ...styles.day, alignItems: "center" }}>
<ActivityIndicator
color="white"
style={{ marginTop: 10 }}
size="large"
/>
</View>
) : (
days.map((day, index) => (
<View key={index} style={styles.day}>
<View
style={{
flexDirection: "row",
alignItems: "center",
width: "100%",
justifyContent: "space-between",
}}
>
<Text style={styles.temp}>
{parseFloat(day.temp.day).toFixed(1)}
</Text>
<Fontisto
name={icons[day.weather[0].main]}
size={68}
color="white"
/>
</View>
<Text style={styles.description}>{day.weather[0].main}</Text>
<Text style={styles.tinyText}>{day.weather[0].description}</Text>
</View>
))
)}
</ScrollView>
</View>
);
}
최종 코드
import * as Location from "expo-location";
import { StatusBar } from "expo-status-bar";
import React, { useEffect, useState } from "react";
import {
View,
Text,
Dimensions,
ActivityIndicator,
StyleSheet,
ScrollView,
} from "react-native";
import { Fontisto } from "@expo/vector-icons";
const { width: SCREEN_WIDTH } = Dimensions.get("window");
const API_KEY = "API키";
const icons = {
Clouds: "cloudy",
Clear: "day-sunny",
Atmosphere: "cloudy-gusts",
Snow: "snow",
Rain: "rains",
Drizzle: "rain",
Thunderstorm: "lightning",
};
export default function App() {
const [city, setCity] = useState("Loading...");
const [days, setDays] = useState([]);
const [ok, setOk] = useState(true);
const getWeather = async () => {
const { granted } = await Location.requestForegroundPermissionsAsync();
if (!granted) {
setOk(false);
}
const {
coords: { latitude, longitude },
} = await Location.getCurrentPositionAsync({ accuracy: 5 });
const location = await Location.reverseGeocodeAsync(
{ latitude, longitude },
{ useGoogleMaps: false }
);
//setCity(location[0].city);
setCity(location[0].region);
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();
setDays(json.daily);
};
useEffect(() => {
getWeather();
}, []);
return (
<View style={styles.container}>
<StatusBar style="light" />
<View style={styles.city}>
<Text style={styles.cityName}>{city}</Text>
</View>
<ScrollView
pagingEnabled
horizontal
showsHorizontalScrollIndicator={false}
contentContainerStyle={styles.weather}
>
{days.length === 0 ? (
<View style={{ ...styles.day, alignItems: "center" }}>
<ActivityIndicator
color="white"
style={{ marginTop: 10 }}
size="large"
/>
</View>
) : (
days.map((day, index) => (
<View key={index} style={styles.day}>
<View
style={{
flexDirection: "row",
alignItems: "center",
width: "100%",
justifyContent: "space-between",
}}
>
<Text style={styles.temp}>
{parseFloat(day.temp.day).toFixed(1)}
</Text>
<Fontisto
name={icons[day.weather[0].main]}
size={68}
color="white"
/>
</View>
<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: "tomato",
},
city: {
flex: 1.2,
justifyContent: "center",
alignItems: "center",
},
cityName: {
fontSize: 58,
fontWeight: "500",
color: "white",
},
weather: {
},
day: {
width: SCREEN_WIDTH,
alignItems: "flex-start",
paddingHorizontal: 20,
},
temp: {
marginTop: 50,
fontWeight: "600",
fontSize: 100,
color: "white",
},
description: {
marginTop: -10,
fontSize: 30,
color: "white",
fontWeight: "500",
},
tinyText: {
marginTop: -5,
fontSize: 25,
color: "white",
fontWeight: "500",
},
});