본 게시글에서는 Open API 를 호출해 날씨 데이터를 읽어온다.
1. API request 를 위해 axios module 추가
* 참고 : https://www.npmjs.com/package/axios
yarn add axios
2. weather API 호출 규격 및 개인 key 확인
* weather API : https://openweathermap.org/current
* [Home > API > Current Weather Data] 확인 시, 좌표값으로 날씨 정보를 읽어 올 수 있는 규격 있음
* 회원가입 후 [Home > My API keys] 에서 개인 key 확인 가능
3. weather API 테스트
* isLoading || error = true 일 때, 별도로 <Text> 를 구성하여 'Wating...' 표시
* API_KEY 는 개인이 발급 받은 key 값 입력
* latitude, longitude 임의값을 입력하여 API response sample을 화면에 display
* response 값이 JSON 형식이므로 JSON.stringify(resWeather) 사용하여 text 로 변환
* useEffect 를 사용하여 WeatherData tab 으로 전환할 때마다 API 호출
# screens > WeatherData > index.js
import React, { useEffect, useState } from "react";
import { Text, View, Alert } from "react-native";
//components
import Header from 'components/Header'
//API request
import axios from "axios";
const index = () => {
const [isLoading, setIsLoading] = useState(true);
const [currentWeather, setCurrentWeather] = useState('');
const [error, setError] = useState(false);
const API_KEY = "{개인별 발급한 key}";
const latitude = 38;
const longitude = 128;
const getWeather = async (latitude, longitude) => {
try {
const resWeather = await axios.get(
`http://api.openweathermap.org/data/2.5/weather?lat=${latitude}&lon=${longitude}&appid=${API_KEY}&units=metric`
);
let res = JSON.stringify(resWeather);
console.log('[LOG] resWeather : ' + res);
setCurrentWeather(res);
} catch (error) {
Alert.alert("날씨 정보를 읽어올 수 없습니다.")
setError(true);
} finally {
setIsLoading(false);
}
};
useEffect(() => {
getWeather(latitude, longitude);
}, []);
return (
<View>
<Header title='날씨' />
{isLoading || error
? <Text> Waiting.. </Text>
: <Text> 날씨 : {currentWeather} </Text>
}
</View>
);
};
export default index;
* case 별로 테스트 시 모두 잘 동작함
* [LOG] 확인해보면 API response 값이 아래와 같이 나오고, 여기서 활용할 "main", "temp" 만 가져올 예정
4. 좌표값에 따른 '날씨', '온도' 값 출력
* Math.round() 로 정수만 출력
* return () 값을 하나의 요소로 감싸 주어야 하기 때문에 <> 로 감싸 주도록 함
# screens > WeatherData > index.js
import React, { useEffect, useState } from "react";
import { Text, View, Alert } from "react-native";
//components
import Header from 'components/Header'
//API request
import axios from "axios";
const index = () => {
const [isLoading, setIsLoading] = useState(true);
const [currentWeather, setCurrentWeather] = useState('');
const [temp, setTemp] = useState('');
const [error, setError] = useState(false);
const API_KEY = "{개인별 발급한 key}";
const latitude = 38;
const longitude = 128;
const getWeather = async (latitude, longitude) => {
try {
const resWeather = await axios.get(
`http://api.openweathermap.org/data/2.5/weather?lat=${latitude}&lon=${longitude}&appid=${API_KEY}&units=metric`
);
let _main = resWeather.data.weather[0].main;
let _temp = resWeather.data.main.temp;
setCurrentWeather(_main);
setTemp(_temp);
} catch (error) {
Alert.alert("날씨 정보를 읽어올 수 없습니다.")
setError(true);
} finally {
setIsLoading(false);
}
};
useEffect(() => {
getWeather(latitude, longitude);
}, []);
return (
<View>
<Header title='날씨' />
{isLoading || error
? (<Text> Waiting.. </Text>)
: (
<>
<Text> WEATHER is {currentWeather}</Text>
<Text> TEMP is {Math.round(temp)} </Text>
</>
)
}
</View>
);
};
export default index;
* 메모리 누수 관련 Warning 발생 (Can't perform a React state update on an unmounted component.)
* 발생 : 천천히 Bottom tab 을 [날씨 > 설정] 으로 이동할 때는 발생하지 않았는데, 빠르게 이동하면 발생
* 이유 : 서버에서 API response 값을 받아오기 전에 Bottom tab 을 이동하게 되면, [날씨] 화면이 unmount 된 상태에서 API response를 받게 되고, 이후 setCurrentWeather(), setTemp(), setIsLoading() 이 동작하게 되면서 불필요한 메모리를 사용함
5. 메모리 누수 해결
* mounted = false 설정을 통해, API response가 늦을 시 setCurrentWeather(), setTemp() 동작 방지
* setIsLoading 위치 변경
* useEffect() 내부에 getWeather() 가 위치하도록 변경
# screens > WeatherData > index.js
import React, { useEffect, useState } from "react";
import { Text, View, Alert } from "react-native";
//components
import Header from 'components/Header'
//API request
import axios from "axios";
const index = () => {
const [isLoading, setIsLoading] = useState(true);
const [currentWeather, setCurrentWeather] = useState('');
const [temp, setTemp] = useState('');
const [error, setError] = useState(false);
const API_KEY = "{개인별 발급한 key}";
const latitude = 38;
const longitude = 128;
useEffect(() => {
let mounted = true;
const getWeather = async (latitude, longitude) => {
try {
const resWeather = await axios.get(
`http://api.openweathermap.org/data/2.5/weather?lat=${latitude}&lon=${longitude}&appid=${API_KEY}&units=metric`
);
if (mounted) {
let _main = resWeather.data.weather[0].main;
let _temp = resWeather.data.main.temp;
setCurrentWeather(_main);
setTemp(_temp);
setIsLoading(false);
}
} catch (error) {
Alert.alert("날씨 정보를 읽어올 수 없습니다.")
setError(true);
setIsLoading(false);
}
};
getWeather(latitude, longitude);
return () => {
mounted = false
}
}, []);
return (
<View>
<Header title='날씨' />
{isLoading || error
? <Text> Waiting.. </Text>
: <Text> 날씨 : {currentWeather} , 온도 : {temp} </Text>
}
</View>
);
};
export default index;
* [지도] 화면에서도 동일한 메모리 누수 현상 발생하여 수정
# screens > Map > index.js
* mounted = false 설정을 통해, API response가 늦을 시 setLocation() 동작 방지
import React, { useState, useEffect } from "react";
import { StyleSheet } from "react-native";
//components
import Header from 'components/Header'
//google map
import MapView, { PROVIDER_GOOGLE } from 'react-native-maps';
//location
import * as Location from 'expo-location';
const index = ({ navigation }) => {
const [initialRegion, setInitialRegion] = useState({
latitude: 35.91395373474155,
longitude: 127.73829440215488,
latitudeDelta: 5,
longitudeDelta: 5,
})
const [mapWidth, setMapWidth] = useState('99%');
const [location, setLocation] = useState(null);
const [errorMsg, setErrorMsg] = useState(null);
// Update map style to force a re-render to make sure the geolocation button appears
const updateMapStyle = () => {
setMapWidth('100%')
}
// Get current location information
useEffect(() => {
let mounted = true;
(async () => {
let { status } = await Location.requestForegroundPermissionsAsync();
if (status !== 'granted') {
setErrorMsg('Permission to access location was denied');
return;
}
let location = await Location.getCurrentPositionAsync({});
if (mounted) {
setLocation(location);
}
})();
return () => {
mounted = false
}
}, []);
let text = 'Waiting..';
if (errorMsg) {
text = errorMsg;
} else if (location) {
text = JSON.stringify(location);
console.log('[LOG] current location : ' + text);
}
return (
<Wrapper>
<Header title='지도' />
<MapView
initialRegion={initialRegion}
style={[styles.map, { width: mapWidth }]}
provider={PROVIDER_GOOGLE}
showsUserLocation={true}
showsMyLocationButton={true}
onMapReady={() => {
updateMapStyle()
}}
/>
</Wrapper>
);
};
export default index;
const Wrapper = styled.View`
flex: 1;
flex-direction: column;
`;
const styles = StyleSheet.create({
map: {
flex: 1,
width: '100%',
height: '100%',
},
});
끝.
'React Native > React Native_study' 카테고리의 다른 글
[리액트 네이티브를 다루는 기술 #1] 2장 컴포넌트 (p. 70 ~ 98) (0) | 2021.12.20 |
---|---|
[React Native] #9 FlatList 로 데이터 나타내기 (0) | 2021.06.17 |
[React Native] #7 Google Maps 를 활용한 화면 구성 (0) | 2021.06.15 |
[React Native] #6 이미지를 읽어와 홈 화면 구성 (0) | 2021.06.11 |
[React Native] #5 Bottom Tabs 에 Icon 추가 (0) | 2021.06.09 |