React_05

연가을·2022년 6월 30일
0
post-thumbnail
post-custom-banner

수업내용

날씨 정보를 API로 가져와 react로 날씨앱 구현하기

1) API란?
Application Programming Interface(애플리케이션 프로그램 인터페이스)의 줄임말.
인터페이스는 두 애플리케이션 간의 서비스 계약이라고 할 수 있으며, 이 계약은 요청과 응답을 사용하여 두 애플리케이션이 서로 통신하는 방법을 정의합니다. API 문서에는 개발자가 이러한 요청과 응답을 구성하는 방법에 대한 정보가 들어 있다.

프로토콜 집합을 사용하여 두 소프트웨어 구성 요소가 서로 통신할 수 있게 하는 메커니즘이다.

2) 날씨앱 구현

  • 날씨앱을 구현하기 전에 날씨앱에서 사용할 정보들이 들어있는 API ID값을 받기 위해서 https://openweathermap.org/api의 사이트에 가입을 한 후 API ID 값은 복사해 둔다.

3) 구현 내용

1.앱이실행되자마자현재위치기반의날씨가보인다.
2.날씨정보에는도시, 섭씨, 화씨, 날씨상태
3.5개의버튼이있다.(1개는현재위치, 4개는다른도시)
4.도시버튼을클릭할때마다도시별날씨가나온다.
5.현재위치버튼을누르면다시현재위치기반의날씨가나온다.
6.데이터를들고오는동안로딩스피너가돈다.

4) 구현을 위해 필요한 함수

Callback

  • 콜백함수란 인자로 들오는 함수를 칭한다,
  • 콜백은 비동기 통신을 할 수 있는 하나의 패턴이다.

Promise

  • ES6에서 나온 비동기 패턴
  • 비동기 통신 결과와 상태를 저장하는 객체
    후속처리 메서드로 thencatch finally
    가 있다.

async/await

  • Promise의 복잡성으로 인해 ES8에서 나온 비동기 패턴
  • Promise를 기반으로 하며, 완저니 같지는 않으나 사용하기에 편리

4) 구현 코드

App.js

UI는 부트스크랩을 이용하여 구현을 하였다.

import React, { useState, useEffect } from 'react';
import 'bootstrap/dist/css/bootstrap.min.css';
import './App.css';
import { Container } from 'react-bootstrap';
import WeatherButton from './components/WeatherButton';
import WeatherBox from './components/WeatherBox';
import { ClipLoader } from 'react-spinners';
const cities = ['paris', 'new york', 'tokyo', 'seoul'];
const API_KEY = 'b4a0b63fb0a27cc709f6ea5ecd5f5d7d';
const App = () => {
  const [loading, setLoading] = useState(false);
  const [city, setCity] = useState(null);
  const [weather, setWeather] = useState(null);
  const [apiError, setAPIError] = useState('');
  const getWeatherByCurrentLocation = async (lat, lon) => {
    console.log('현재 위치', lat, lon);
    //비동기 처리
    try {
      let url = //units=metric 캘빈을 섭씨로
        // let url = `https://api.openweathermap.org/data/2.5/weather?lat=${lat}&lon=${lon}&appid=${API_KEY}`;
        // `https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${API_KEY}`;
        'http://api.openweathermap.org/data/2.5/weather?lat=35.87222&lon=128.60250&appid=b4a0b63fb0a27cc709f6ea5ecd5f5d7d';
      //&units=metric
      const res = await fetch(url);
      const data = await res.json();
      setWeather(data);
      setLoading(false);
    } catch (err) {
      setAPIError(err.message);
      setLoading(false);
    }
  };
  const getCurrentLocation = () => {
    navigator.geolocation.getCurrentPosition((position) => {
      const { latitude, longitude } = position.coords;
      getWeatherByCurrentLocation(latitude, longitude);
      // console.log('현재 위치', lat, lon);
    });
  };
  const getWeatherByCity = async () => {
    try {
      let url = `https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${API_KEY}`;
      //&units=metric
      const res = await fetch(url);
      const data = await res.json();
      setWeather(data);
      setLoading(false);
    } catch (err) {
      console.log(err);
      setAPIError(err.message);
      setLoading(false);
    }
  };
  useEffect(() => {
    if (city == null) {
      setLoading(true);
      getCurrentLocation();
    } else {
      setLoading(true);
      getWeatherByCity();
    }
  }, [city]);
  const handleCityChange = (city) => {
    if (city === 'current') {
      setCity(null);
    } else {
      setCity(city);
    }
  };
  return (
    <Container className="vh-100">
      {loading ? (
        <div className="w-100 vh-100 d-flex justify-content-center align-items-center">
          <ClipLoader color="#f86c6b" size={150} loading={loading} />
        </div>
      ) : !apiError ? (
        <div class="main-container">
          <WeatherBox weather={weather} />
          <WeatherButton
            cities={cities}
            handleCityChange={handleCityChange}
            selectedCity={city}
          />
        </div>
      ) : (
        apiError
      )}
    </Container>
  );
};
export default App;

component

  • WeatherBox.js
import React from 'react';
import { Card } from 'react-bootstrap';

const WeatherBox = ({ weather }) => {
  const temperatureC =
    weather && weather.main ? (weather.main.temp - 273.15).toFixed(2) : '';
  const temperatureF =
    weather && weather.main
      ? (((weather.main.temp - 273.15) * 9) / 5 + 32).toFixed(2)
      : '';
  return (
    <Card className="weather-card">
      {/* <Card.Img src="holder.js/100px270" alt="Card image" /> */}
      <Card.ImgOverlay className="d-flex flex-column justify-content-center text-center">
        <Card.Title>{weather?.name}</Card.Title>
        <Card.Text className="text-success h1">
          {`${temperatureC} °C / ${temperatureF} °F`}
        </Card.Text>
        <Card.Text className="text-info text-uppercase h2">
          {weather && weather.weather[0]?.description}
        </Card.Text>
      </Card.ImgOverlay>
    </Card>
  );
};

export default WeatherBox;
  • WeatherButton.js
import React from 'react';
import { Button } from 'react-bootstrap';

const WeatherButton = ({ cities, selectedCity, handleCityChange }) => {
  return (
    <div class="menu-container">
      <Button
        variant={`${selectedCity === null ? 'outline-warning' : 'warning'}`}
        onClick={() => handleCityChange('current')}
      >
        Current Location
      </Button>

      {cities.map((city) => (
        <Button
          variant={`${selectedCity === city ? 'outline-warning' : 'warning'}`}
          onClick={() => handleCityChange(city)}
        >
          {city}
        </Button>
      ))}
    </div>
  );
};

export default WeatherButton;
  • PublicNavbar.js
import React from 'react';
import { Navbar, Nav } from 'react-bootstrap';

const PublicNavbar = () => {
  return (
    <Navbar bg="light" expand="lg" className="position-fixed navbar-fixed">
      <Navbar.Brand></Navbar.Brand>
      <Nav className="mr-auto"></Nav>
      <Nav>
        <a
          href="https://github.com/dhminh1024/cs_weather_app"
          target="_blank"
          rel="noreferrer"
        ></a>
      </Nav>
    </Navbar>
  );
};

export default PublicNavbar;
post-custom-banner

0개의 댓글