교육 72일차 강의

구성본·2022년 6월 30일

1. 학습한 내용

리액트 날씨 앱 만들기

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

  • 동기와 비동기

• 동기
-작업(task)들이 순차적으로 이루어 지는 것
-다른 작업들을 blocking

• 비동기
-작업(task)들이 순차적으로 이루어지지 않음
-다른 작업들은 non-blocking
-Javascript를 사용한 비동기 통신 방식을 Ajax(Asynchronous Javascript and XML)
-Http요청(GET, POST), 이벤트 핸들러(click, over …), setTimeout, setInterval

  • Callback
    -콜백 함수란 인자로 들어오는 함수를 칭함
    -콜백은 비동기 통신을 할 수 있는 한 패턴
    -문제점: 콜백 헬로 인한 에러처리 불가, 가독성 hell

  • Promise
    -ES6에서 나온 비동기 패턴
    -비동기 통신 결과와 상태를 저장하는 객체
    -후속처리 매서드로 then(성공), catch(에러), finally(무조건)가 있다

  • async/await
    -Promise의 복잡성으로 인해 ES8에서 나온 비동기 패턴
    -Promise를 기반으로 하며 완전히 같지는 않으나 사용하기 편하다

  • stateful, stateless
    -컴포넌트를 나누는 또 다른 방식
    -stateful 컴포넌트 : 모든 state를 들고있는 컴포넌트
    -stateless 컴포넌트: state를 안들고 있고 props 와 같이 받은 데이터만 보여주는 컴포넌트
    -리액트는 단방향 통신이다 즉 부모에서 자식으로만 데이터를 전달 할 수 있다
    이를통해 데이터 흐름을 추적하기는 더 쉽지만, 같은 형제끼리 데이터를 주고받기는 힘들다. 그래서 데이터는 주로 부모가 들고있고 자식에게 전달해주는 형식이 된다. state를 들고있는 부모 컴포넌트는 stateful 컴포넌트, 부모가주는 데이터만 받는 컴포넌트를 stateless 컴포넌트라고 한다
    -이렇게 컴포넌트를 구성하면 장점은 state가 없는 stateless컴포넌트는 재사용이 쉽고 데이터를 걱정하지 않아도 된다
    -모든 중요한 데이터들은 stateful컴포넌트에 있기 때문에 유지보수가 쉽다 (stateful컴포넌트 하나만 주시하고 관리해주면 되니까)
    -그래서 최대한 모든 컴포넌트를 stateless로 만들고 몇개의 stateful컴포넌트에서 데이터를 관리하는 구조가 이상적이라고 리액트는 말하고 있다

  • App.js

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 API_KEY = 'ecfc1cc396fcde85c6e3516f792af3e1';
const cities = ['paris', 'new york', 'tokyo', 'seoul'];

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) => {
    try {
      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=ecfc1cc396fcde85c6e3516f792af3e1';

      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('현재위치?', latitude, longitude);
    });
  };

  const getWeatherByCity = async () => {
    try {
      let url = `https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${API_KEY}`;

      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;
  • 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.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;
  • App.css
body{
  background: url(https://images.unsplash.com/photo-1522441815192-d9f04eb0615c?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2127&q=80);
  height: 100vh;
  background-repeat: no-repeat;
  background-size: cover;
}

.weather-card{
  background-color: rgba(52,52,52,.2);
  padding: 50px;
  border: 2px solid #fff;
  border-radius: 20px;
  max-width: 700px;
  width:100%;
  height: 300px;
}
.main-container{
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  height: 100%;
  
}
.menu-container{
  display: flex;
  justify-content: center;
  background-color: #000;
  border-radius: 60px;
  max-width: 700px;
  width:100%;
  padding: 30px;
  margin-top: 30px;

}
.menu-container Button{
  margin-right:30px;
}

.menu-container Button:hover{
  background-color: #ffc107;
}

2.학습한 내용 중 어려웠던 점

처음엔 같이 작성을 해보다가 문법적인 부분이 많아지고 여기 저기 코딩을 넣고 빼고 하다보니 따라가기가 정말 어려웠던 것 같다. 그러다보니 이 후엔 만들어 놓으신 코드들을 받아서 작동 원리들을 보고 자기 환경에 맞게 구동할 수 있게 바꾸는 것을 했는데 그 조차도 쉽지 않았다.

3.해결방법

최근에 가장 유용하게 사용되는 리액트에 대해서 문법적인 부분을 많이 찾아보고 할 수 있어야겠다. 문법적인 부분이 어려운 만큼 많이 사용해봐야겠다. 간단한 앱이라도 문법적인 부분에서 거의 다 막히는만큼 예제들을 찾아봐야겠다.

4.학습소감

파이썬이나 리액트, 파이스크립트 등 많은 기술들이 나오고 바뀌는만큼 기본적으로 접근할 수 있는 바탕을 최대한 깔아놓을 수 있게 할 수 있는 부분은 최대한 해봐야겠다.

profile
코딩공부중

0개의 댓글