https://openweathermap.org/api
날씨 api 가져오기
회원 가입 후 사용한다
로직
//1. 앱이 실행 되자 마자 현재 위치 기반의 날씨가 보인다
//2. 지금 현재 도시와 온도 , 날씨 상태 정보가 나온다
//3. 하단에 도시 버튼 5개 (현재위치, 다른 도시 4개)
//4. 버튼을 누를 때 마다 해당 되는 도시별 날씨가 보여진다
//5. 현재위치 버튼을 누르면 다시 현재위치 기반의 날씨 정보를 보여준다
//6. 데이터를 들고오는 동안 로딩 스피너가 돌아간다
위치 정보 가져오기
https://www.w3schools.com/html/html5_geolocation.asp
import { useEffect, useState } from 'react';
import './App.css';
//1. 앱이 실행 되자 마자 현재 위치 기반의 날씨가 보인다
//2. 지금 현재 도시와 섭씨/화씨 , 날씨 상태 정보가 나온다
//3. 하단에 도시 버튼 5개 (현재위치, 다른 도시 4개)
//4. 버튼을 누를 때 마다 해당 되는 도시별 날씨가 보여진다
//5. 현재위치 버튼을 누르면 다시 현재위치 기반의 날씨 정보를 보여준다
//6. 데이터를 들고오는 동안 로딩 스피너가 돌아간다
function App() {
const getCurrentLocation = () => {
navigator.geolocation.getCurrentPosition((position) => {
let lat = position.coords.latitude;
let lon = position.coords.longitude;
console.log('현재 위도 경도는?', lat, lon);
});
};
useEffect(() => {
getCurrentLocation();
}, []);
return <div>안녕텍스트</div>;
}
export default App;
날씨 api 들고오기
api 호출 함수 만들기
const getWeatherByCurrentLocation = async (lat, lon) => {
let url = `https://api.openweathermap.org/data/2.5/weather?lat=${lat}&lon=${lon}&appid=81523c3aa0ea13bf7e0d71967cd5d5d4`;
let response = await fetch(url);
let data = await response.json();
console.log('데이터 확인!', data);
};
리액트 부트스트랩 사용하기 (버튼 만들어주기)
https://react-bootstrap.github.io/getting-started/introduction
터미널에서 설치하기
npm install react-bootstrap bootstrap
사용하고 싶은 컴포넌트 파일 상단에 import 걸어주기
import { Button } from 'react-bootstrap';
Weather.box.js
import React from 'react';
const WeatherBox = ({ weather }) => {
console.log('웨더 정보 확인', weather);
return (
<div className="weather-box">
<div>{weather.name}</div>
<h2>30도 / 130화씨</h2>
<h3>흐림</h3>
</div>
);
};
export default WeatherBox;
{다이나믹 값} 으로 웨더 데이터에서 정보를 가져 온다
섭씨온도로 확인하기 위해서 추가할 것들
let url = `https://api.openweathermap.org/data/2.5/weather?lat=${lat}&lon=${lon}&appid=81523c3aa0ea13bf7e0d71967cd5d5d4&units=metric`;
url 뒤에 &units=metric 을 추가해준다
혹은 이렇게 추가하지 않고 섭씨/화씨 온도를 각각 따로 처리 해도 된다
섭씨로 표기 된 데이터를 바탕으로 화씨 온도를 표시해 주는 방법
import React from 'react';
const WeatherBox = ({ weather }) => {
console.log('웨더 정보 확인', weather);
return (
<div className="weather-box">
<div>{weather?.name}</div> {/* weather가 참이면 실행 (삼항연산자의 또다른 표기법) */}
<h2>
{weather?.main.temp}℃ / {((weather?.main.temp * 9) / 5 + 32).toFixed(2)}℉
</h2>
<h3>흐림</h3>
</div>
);
};
/* 섭씨를 화씨로 변환하기 (0°C × 9/5) + 32 = 32°F */
export default WeatherBox;
도시 추가하기 array를 만들어 관리한다
const cities = ['paris', 'new york', 'tokyo', 'seoul'];
import React from 'react';
import { Button } from 'react-bootstrap';
const WeatherButton = ({ cities }) => {
console.log('도시정보 확인', cities);
return (
<div>
<Button variant="warning">Current Location</Button>
{cities.map((item) => (
<Button variant="warning">{item}</Button>
))}
</div>
);
};
export default WeatherButton;
배열에 있는 도시들이 버튼으로 생성 됐다
리액트는 one-way 구조이다
app(부모)에 모든 정보를 넣어 놓고 자식들에게 정보를 나눠주는 형식
WeatherButton.js 에서 버튼 클릭시 해당 국가의 데이터를 app.js 로 바로 넘겨줄 수 없다
WeatherButton.js
import React from 'react';
import { Button } from 'react-bootstrap';
const WeatherButton = ({ cities, setCity }) => {
console.log('도시정보 확인', cities);
return (
<div>
<Button variant="warning">Current Location</Button>
{cities.map((item) => (
<Button variant="warning" onClick={() => setCity(item)}>
{item}
</Button>
))}
</div>
);
};
export default WeatherButton;
로딩 스피너 만들기
https://www.npmjs.com/package/react-spinners
npm install --save react-spinners
설치 후
app.js 상단에 임포트 걸기
import ClipLoader from "react-spinners/ClipLoader";
오늘 날짜 추가
const todayData = () => {
const week = ['월','화','수','목','금','토','일'];
let now = new Date();
let todayMonth = (now.getMonth()+1) > 9 ? (now.getMonth()+1) : (now.getMonth()+1);
let todayDate = now.getDate() > 9 ? now.getDate() : '0' + now.getDate();
let dayOfWeek = week[now.getDay()];
return todayMonth + '월 ' + todayDate + '일 ' + dayOfWeek + '요일'
}
날씨 아이콘 추가하기
open API 에 있는 날씨 아이콘을 살펴본다
https://openweathermap.org/weather-conditions
id 값에 따라 날씨가 다르게 표시 되어 있다
App.js
WeatherBox.js 에 와서 props 로 id를 던져준다
const WeatherBox = ({weather, id})
icon URL 주소를 가지고 온다
<img className="img-fluid" src = {'http://openweathermap.org/img/wn/{icon}@2x.png'} />
아직 엑박으로 나오고 있지만 콘솔창에 확인해보면 id 값이 맞게 출력 되고 있다
이제 엑박을 어떻게 이미지로 나올 수 있게 수정하는 일만 남았다!
에러 해결
icon URL 주소가 잘못 되어 있어서 엑박으로 뜨게 된 것이었다
수정 전
<img className="img-fluid" src = {'http://openweathermap.org/img/wn/{icon}@2x.png'} />
수정 후 동적 값으로 주기 위해 $를 붙여 줬다
<img className="img-fluid" src={`http://openweathermap.org/img/wn/${icon}@2x.png`} />
아이디와 아이콘 값이 잘 출력 되고 그에 맞게 아이콘도 그림으로 잘 나오게 됐다
근데 get 404 에러가 떠있어서 추후에 다시 수정해야겠다
수정하면서 추가 된 내용들
https://uigradients.com/#Horizon
배경색 그라데이션
{Math.round(weather?.main.temp)}º
Math.round 함수 사용하여 소수점 아래 다 버리기
검색 기능 추가하기
const [query, setQuery] = useState('');
App.js
/* 검색 기능 */
const search = async (e) => {
if(e.key === "Enter"){
fetch(`https://api.openweathermap.org/data/2.5/weather?q=${query}&appid=${API_KEY}&units=metric`)
.then(res => res.json())
.then(result => {
setWeather(result);
setQuery('');
//console.log("result 확인", result);
})
}
}
검색창에 도시를 입력하고 엔터를 누르면
url에 동적값으로 도시 이름이 들어가게 된다 ${query}
기온에 따라 배경 색상 바뀌게 하기
className={(typeof weather.main != "undefined")
? ((weather.main.temp > 26)
? 'container_warm' : 'container') : 'container'}
기온이 26도 이상일 때 container의 클래스네임을 다르게 부여하여 색상을 바꿔준다
하단의 버튼에 없는 도시들도 검색하면 바로 정보가 나오고 26도 이상일 때 배경색이 바뀐다
며칠 지나니까 에러 뜨고 데이터가 넘겨지는게 안되어서 코드 수정 했다
현재 위치 정보가 아예 뜨지 않는 상태였음
수정 전
/* 현재 위치의 위도 경도 가져오기 */
const getCurrentLocation = () => {
navigator.geolocation.getCurrentPosition((position) => {
let lat = position.coords.latitude;
let lon = position.coords.longitude;
//console.log('현재 위도 경도는?', lat, lon);
getWeatherByCurrentLocation(lat, lon);
});
};
수정 후
/* 현재 위치의 위도 경도 가져오기 */
const getCurrentLocation = () => {
navigator.geolocation.getCurrentPosition((position) => {
const { latitude, longitude } = position.coords;
getWeatherByCurrentLocation(latitude, longitude);
});
};
- 변수 수정
수정 전
/* 현재 위치의 날씨 정보 */
const getWeatherByCurrentLocation = async (lat, lon) => {
let url = `https://api.openweathermap.org/data/2.5/weather?q=lat=${lat}&lon=${lon}&appid=${API_KEY}&units=metric`;
let response = await fetch(url);
let data = await response.json();
//console.log('데이터 확인!', data);
setWeather(data);
setId(data.weather[0].id);
setIcon(data.weather[0].icon);
setLoading(false); //스피너 종료
};
수정 후
/* 현재 위치의 날씨 정보 */
const getWeatherByCurrentLocation = async (lat, lon) => {
try {
let url = `https://api.openweathermap.org/data/2.5/weather?lat=${lat}&lon=${lon}&appid=${API_KEY}`;
let response = await fetch(url);
let data = await response.json();
setWeather(data);
setIcon(data.weather[0].icon);
setLoading(false);
} catch (err) {
setError(err.message);
setLoading(false);
}
};
- try-catch 사용해서 에러처리
수정 전
useEffect((city) => {
/* useEffect는 componentDidUpdate 역할도 한다 */
/* useEffect 는 한 곳에서 정리한다 */
if (city === '') {
setLoading(true);
getCurrentLocation();
} else {
setLoading(true);
getWeatherByCity();
}
}, [city]);
수정 후
useEffect(() => {
if (city === '') {
setLoading(true);
getCurrentLocation();
} else {
setLoading(true);
getWeatherByCity();
}
}, [city]);
city 인자 넘겨받지 않고 뺌
수정 전
return (
/* UI를 보여주는 곳 */
<div>
{/* 스피너 true일 때의 조건 */}
{loading ? (
<div className="container">
<ClipLoader color="#ff0000" loading={loading} size={100} />
</div>
) : (
<div className={(typeof weather.main != "undefined")
? ((weather&&weather.main.temp > 26)
? 'container_warm' : 'container') : 'container'}>
<div className='search_box'>
<input
type="text"
placeholder='Search...'
onChange={e=>setQuery(e.target.value)}
value={query}
onKeyPress={search}
>
</input>
</div>
<WeatherBox weather={weather} id={id} icon={icon}/> {/* props로 넘기기 */}
<WeatherButton cities={cities} selectCity={city} handleCityChange={handleCityChange} /> {/* setCity라는 함수를 props로 넘겨준다 */}
</div>
)}
</div>
);
수정 후
return (
/* UI를 보여주는 곳 */
<div>
{/* 스피너 true일 때의 조건 */}
{loading ? (
<div className="container">
<ClipLoader color="#ff0000" loading={loading} size={100} />
</div>
) : !error ? (
<div className={typeof weather.main != 'undefined' ? (weather && weather.main.temp > 26 ? 'container_warm' : 'container') : 'container'}>
<div className="search_box">
<input type="text" placeholder="Search..." onChange={(e) => setQuery(e.target.value)} value={query} onKeyPress={search}></input>
</div>
<WeatherBox weather={weather} icon={icon} /> {/* props로 넘기기 */}
<WeatherButton cities={cities} selectCity={city} handleCityChange={handleCityChange} /> {/* setCity라는 함수를 props로 넘겨준다 */}
</div>
) : (
error
)}
</div>
);
error 에러처리
'Dev. > React' 카테고리의 다른 글
React :: shopping site 만들기 01 /폰트어썸 리액트 (0) | 2022.09.15 |
---|---|
React :: Router / Restful Route (0) | 2022.09.05 |
React :: 라이프 사이클 (0) | 2022.08.24 |
React :: 클래스 컴포넌트 (0) | 2022.08.22 |
React :: 가위바위보 게임 만들기 (0) | 2022.08.17 |
댓글