https://conquer-it.tistory.com/195
API로 날씨 앱 만들기 2(OpenWeather API 사용)
6. 도시별 날씨 가져오기
우선 여러 도시의 날씨를 불러오기 위해
App.js에서 city라는 state를 만든다.
const [city, setCity] = useState("");
그 후에 setCity를 <WeatherButton />의 props로 보낸다.
return (
<>
<div className="container">
<WeatherBox weather={weather} />
<WeatherButton cities={cities} setCity={setCity} /> //props로 보낸다.
</div>
</>
);
그 후 <WeatherButton />의 도시 버튼에 onClick 이벤트를 주어 setCity를 통해 아이템을 읽을 수 있도록 한다.
import React from "react";
import { Button } from "react-bootstrap";
const WeatherButton = ({ cities, setCity }) => {
console.log(cities);
return (
<div className="weather-btn">
<Button variant="warning" className="btn">
Current Location
</Button>
{cities.map((item) => (
<Button variant="warning" className="btn" onClick={() => setCity(item)}>
{item}
</Button>
))}
</div>
);
};
export default WeatherButton;
과연 잘 읽어오는지 useEffect로 확인을 해보자
App.js로 와서 useEffect를 만들자.
useEffect(() => {
console.log("city", city);
}, [city]);
각 도시별 버튼을 눌렀을 때 도시명이 잘 뜨는 것을 확인할 수 있다.
이제 이것을 통해 도시별 기온을 화면에 뿌려주기만 하면 된다.
화면에 정보를 뿌려주기 위해 위의 useEffect의 콘솔 부분을 지워주고
getWeatherByCity()라는 함수를 실행시켜주자.
이 함수에서는 지역별 날씨를 api를 통해 부르는 역할을 한다.
const getWeatherByCity = async () => {
let url = `https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=07c8f80150954d942a79882827366bc7&units=metric`;
let response = await fetch(url);
let data = await response.json();
setWeather(data);
};
useEffect(() => {
if (city == "") {
getCurrentLocation();
} else {
getWeatherByCity();
}
}, [city]);
여기까지 작성하고 실행을 해보면
클릭한 도시의 날씨 정보가 잘 나오는 것을 확인할 수 있다.
7. 로딩 스피너 만들기
우리가 다른 지역의 버튼을 클릭할 때마다 api를 불러야 하기때문에 약간의 시간이 걸리는데
이때 로딩 스피너가 있다면 유저 입장에서는 인내심을 가지고 기다려 줄 수 있을 것이다.
(로딩 스피너가 나오는 타이밍은 데이터가 도착하기 전이 될 것이다.)
리액트 로딩 스피너 사이트
https://www.npmjs.com/package/react-loader-spinner
우선 터미널을 켜서 npm을 설치해준다.
그 후에 자신이 원하는 로딩 스피너를 골라서 코드에 복붙하면 된다.
만약 이것이 마음에 든다고 한다면
App.js로 가서 import를 추가 작성해준다.
import { ThreeDots } from "react-loader-spinner";
그 후에 위의 코드를 App()의 return 부분에 냅다 붙여넣기를 하면
아래와 같이 로딩 스피너가 계속 보여질 것이다.
return (
<>
<div className="container">
<ThreeDots
height="80"
width="80"
radius="9"
color="#4fa94d"
ariaLabel="three-dots-loading"
visible={true}
/>
<WeatherBox weather={weather} />
<WeatherButton cities={cities} setCity={setCity} />
</div>
</>
);
이 문제를 어떻게 해결할 수 있을까?
로딩 스피너가 화면에 나올 수 있는 유일한 시간은 api 데이터를 가져오는 시간 뿐이다.
그렇기 때문에 데이터를 가져오는 동안에는 visible이 true이다가 데이터가 도착을 했으면 false로 바뀌어야 한다.
visible이라는 state를 만들어서 초기 값으로 false를 준다.
그리고 getWeatehrByCurrentLocation()와 getWeatherByCity()에서 데이터를 부를때
visible의 값을 true로 바꾸고 데이터가 도착을 하면 다시 false로 바꿔준다.
const [visible, setVisible] = useState(false);
//생략
const getWeatherByCurrentLocation = async (lat, lon) => {
let url = `https://api.openweathermap.org/data/2.5/weather?lat=${lat}&lon=${lon}&appid=07c8f80150954d942a79882827366bc7&units=metric`;
setVisible(true);
let response = await fetch(url);
let data = await response.json();
setWeather(data);
setVisible(false);
};
const getWeatherByCity = async () => {
let url = `https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=07c8f80150954d942a79882827366bc7&units=metric`;
setVisible(true);
let response = await fetch(url);
let data = await response.json();
setWeather(data);
setVisible(false);
};
//생략
그 후에 return부분을 아래와 같이 바꿔주면 로딩 스피너까지 구현에 성공했다.
return (
<>
{visible ? (
<div className="container">
<ThreeDots
height="80"
width="80"
radius="9"
color="#4fa94d"
ariaLabel="three-dots-loading"
visible={visible}
/>
</div>
) : (
<div className="container">
<WeatherBox weather={weather} />
<WeatherButton cities={cities} setCity={setCity} />
</div>
)}
</>
);
8. Current Location 버튼을 누르면 현재 위치의 날씨 보여주기
현재 다른 지역의 버튼을 누르고 다시 Current Location 버튼을 누르면 아무 일도 일어나지 않는다.
다시 현재 위치의 날씨를 보여주기 위해서는
Current Location 버튼을 눌렀을 때 getCurrentLocation() 함수가 실행되어야 한다.
그렇기 때문에 getCurrentLocation()을 <WeatherButton/>의 props로 보낸다.
App.js
<WeatherButton cities={cities} setCity={setCity} getCurrentLocation={getCurrentLocation}/>
그러면 <WeatherButton />에서 이 함수를 받아 onClick 이벤트로 실행시켜주면
현재 위치의 날씨가 잘 출력된다.
WeatherButton.js
import React from "react";
import { Button } from "react-bootstrap";
const WeatherButton = ({ cities, setCity, getCurrentLocation }) => {
return (
<div className="weather-btn">
<Button variant="warning" className="btn" onClick={getCurrentLocation}>
Current Location
</Button>
// 생략
</div>
);
};
export default WeatherButton;
전체코드
App.js
import { useEffect,useState } from 'react';
import './App.css';
import "bootstrap/dist/css/bootstrap.min.css";
import WeatherBox from "./component/WeatherBox";
import WeatherButton from "./component/WeatherButton";
import { ThreeDots } from "react-loader-spinner";
function App() {
const [weather, setWeather] = useState(null);
const cities = ["Paris", "New York", "London", "Busan"];
const [city, setCity] = useState("");
const [visible, setVisible] = useState(false);
//위치 가져오기
const getCurrentLocation = () => {
navigator.geolocation.getCurrentPosition((position) => {
let lat = position.coords.latitude;
let lon = position.coords.longitude;
getWeatherByCurrentLocation(lat, lon);
});
};
const getWeatherByCurrentLocation = async (lat, lon) => {
let url = `https://api.openweathermap.org/data/2.5/weather?lat=${lat}&lon=${lon}&appid=07c8f80150954d942a79882827366bc7&units=metric`;
setVisible(true);
let response = await fetch(url);
let data = await response.json();
setWeather(data);
setVisible(false);
};
const getWeatherByCity = async () => {
let url = `https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=07c8f80150954d942a79882827366bc7&units=metric`;
setVisible(true);
let response = await fetch(url);
let data = await response.json();
setWeather(data);
setVisible(false);
};
useEffect(() => {
if (city == "") {
getCurrentLocation();
} else {
getWeatherByCity();
}
}, [city]);
return (
<>
{visible ? (
<div className="container">
<ThreeDots
height="80"
width="80"
radius="9"
color="lightblue"
ariaLabel="three-dots-loading"
visible={visible}
/>
</div>
) : (
<div className="container">
<WeatherBox weather={weather} />
<WeatherButton
cities={cities}
setCity={setCity}
getCurrentLocation={getCurrentLocation}
selectedCity={city}
/>
</div>
)}
</>
);
}
export default App;
WeatherBox.js
import React from "react";
const WeatherBox = ({ weather }) => {
console.log(weather);
return (
<div className="weather-box">
<div>{weather?.name}</div>
<h2>
{weather?.main.temp}℃ / {(weather?.main.temp * 1.8 + 32).toFixed(2)}℉
</h2>
<h3>{weather?.weather[0].description}</h3>
</div>
);
};
export default WeatherBox;
WeatherButton.js
import React from "react";
import { Button } from "react-bootstrap";
const WeatherButton = ({
cities,
setCity,
getCurrentLocation,
selectedCity,
}) => {
return (
<div className="weather-btn">
<Button
variant={`${selectedCity == null ? "outline-warning" : "warning"}`}
className="btn"
onClick={getCurrentLocation}
>
Current Location
</Button>
{cities.map((city) => (
<Button
variant={`${selectedCity == city ? "outline-warning" : "warning"}`}
className="btn"
onClick={() => setCity(city)}
>
{city}
</Button>
))}
</div>
);
};
export default WeatherButton;
App.css
body{
background-image: url(https://free4kwallpapers.com/uploads/originals/2015/09/16/weather-live-wallpaper-icon.jpg);
height: 100vh;
background-position: center;
}
.container{
display: flex;
justify-content: center;
flex-direction: column;
align-items: center;
height: 100vh;
}
.weather-box{
border: 3px solid #ffc107;
padding: 30px 50px;
border-radius: 10px;
margin-bottom: 20px;
width: 650px;
background-color: #48aac09d;
}
.weather-btn{
border: 3px solid black;
padding: 20px 50px;
border-radius: 10px;
background-color: black;
width: 650px;
display: flex;
justify-content: center;
}
.btn:last-child{
margin: 0;
}
.btn{
margin-right: 15px;
}
.btn:hover{
background-color: red;
color:red;
}
'React' 카테고리의 다른 글
[React] 간단한 코드로 애니메이션 만드는 법(Fade, Zoom, Flip 등) (0) | 2023.07.13 |
---|---|
[React] React로 간단한 H&M 사이트 만들기1 (0) | 2023.03.20 |
[React] reactrouter로 여러개의 웹페이지 만들기 (0) | 2023.02.20 |
[React] API로 날씨 앱 만들기 2(OpenWeather API 사용) (0) | 2023.02.15 |
[React] API로 날씨 앱 만들기 1(OpenWeather API 사용) (0) | 2023.02.13 |