📚 What is TIL?

포스팅 에서 API를 모두 불러와서 화면에 그리는 것 까지 모두 완료했다

💡 이제 컴포넌트를 분리해서 추가기능을 넣고 예외처리까지 해보자!


컴포넌트 분리

App.jsx

import React, { useState } from 'react';
import './index.css'
import DisplayWeather from './Components/DisplayWeather/DisplayWeather';

function App() {
  const [inptLocation, setInptLocation] = useState('');
  const [searchPage, setSearchPage] = useState(false);

  function submitForm(event) {
    event.preventDefault();
    setSearchPage(true);
  }
  return (
    <div className="container">
      <div className="wrapper">
        {searchPage ? (
          <DisplayWeather />
        ) : (
          <form onSubmit={submitForm}>
              <input type="text" placeholder="Seoul" value={inptLocation} onChange={event => setInptLocation(event.target.value)} />
              <button type="submit">🔍</button>
            </form>
        )}
      </div>
</div>
    
  );
}

export default App;

components/DisplayWeather.jsx

import React, { useState, useEffect } from 'react';

export default function DisplayWeather() {
  const cityValue = 'seoul';
  const key = '34bc9f0e13f7fdd3adac28b4f182d8ad';
  const [apiValue, setApiValue] = useState('');
  const [secondApiValue, setSecondApiValue] = useState('');
  const [loading, setLoading] = useState(true);

  const getWeather = async () => {
    const response = await fetch(
      `https://api.openweathermap.org/data/2.5/weather?q=${cityValue}&appid=${key}&units=metric&lang=KR`,
    );
    const result = await response.json();
    setApiValue(result);
    console.log('첫번재 API', result);
  };

  const getFutureWeather = async () => {
    const response = await fetch(
      `https://api.openweathermap.org/data/2.5/forecast?q=${cityValue}&appid=${key}&units=metric&lang=KR`,
    );
    const result = await response.json();

    const oneDay = 1000 * 60 * 60 * 24;
    const offset = 1000 * 60 * 60 * 9;
    const today = new Date().getTime() + offset;
    const DesiredTime = ' 18:00:00';
    const oneDaysLater = new Date(today + oneDay).toISOString().slice(0, 10) + DesiredTime;
    const twoDaysLater = new Date(today + oneDay * 2).toISOString().slice(0, 10) + DesiredTime;
    const threeDaysLater = new Date(today + oneDay * 3).toISOString().slice(0, 10) + DesiredTime;

    const data = result.list.filter(item => {
      return item.dt_txt === oneDaysLater || item.dt_txt === twoDaysLater || item.dt_txt === threeDaysLater;
    });

    setSecondApiValue(data);
    setLoading(false);
    console.log('두번째 API', result);
    console.log('추출한 데이터', data);
  };

  useEffect(() => {
    getWeather();
    getFutureWeather();
  }, []);

  // * 첫번째 API
  // 날씨 이름 : name
  // 날씨 그림 : apiValue.weather[0].icon
  // 날씨 설명 : weather[0].description
  // 날씨 온도(최고/최저) : main.temp / main.temp_max / main.temp_min

  // * 두번째 API
  // 날씨 그림 : weather[0].icon
  // 예보 일자 : dt_txt
  // 날씨 설명 : weather[0].description
  // 날씨 온도 : main.temp

  return (
	<>
      {loading ? (
        <div>Loading...</div>
      ) : (
        <>
          <div className="today-weather">
            <h1>Now</h1>
            <h1>{apiValue.name}</h1>
            <h2>
              <img src={`https://openweathermap.org/img/wn/${apiValue.weather[0].icon}.png`} alt="" />
            </h2>
            <h2>{apiValue.weather[0].description}</h2>
            <h2>{apiValue.main.temp}</h2>
            <h3>
              최고: {apiValue.main.temp_max} | 최저: {apiValue.main.temp_min}
            </h3>
          </div>

          <div className="feature-weathers">
            {secondApiValue.map((item, index) => {
              return (
                <div className="feature-weather" key={index}>
                  <h4>
                    <img src={`https://openweathermap.org/img/wn/${item.weather[0].icon}.png`} alt="" />
                  </h4>
                  <h4>{item.dt_txt.slice(5, 10)}</h4>
                  <h5>{item.weather[0].description}</h5>
                  <h5>{item.main.temp}</h5>
                </div>
              );
            })}
          </div>
        </>
      )}
    </>
  );
}

💡 입력값을 받는 초기화면 따로 데이터를 보여주는 화면따로 컴포넌트를 분리했다
💡 입력값을 받으면 원하는 지역의 날씨를 보여주고 입력값을 받지않으면 현재 위치의 날씨를 보여주도록 구현해 볼 것이다
💡 우선 입력 값을 받으면 요청받는 URL의 매개변수가 변경되도록 해보자



입력 값에 따라 검색하기

App.jsx

import React, { useState } from 'react';
import './index.css';
import DisplayWeather from './Components/DisplayWeather/DisplayWeather';

function App() {
  const [inptLocation, setInptLocation] = useState('');
  const [searchPage, setSearchPage] = useState(false);
  const [preInput, setPreInput] = useState('');

  function submitForm(event) {
    event.preventDefault();
    setSearchPage(true);
    setInptLocation(preInput);
    setPreInput('');
  }

  function turnBack() {
    setSearchPage(false);
  }

  return (
    <div className="container">
      <div className="wrapper">
        {searchPage ? (
          <DisplayWeather inptLocation={inptLocation} turnBack={turnBack} />
        ) : (
          <form onSubmit={submitForm}>
            <input
              type="text"
              placeholder="Seoul"
              value={preInput}
              onChange={event => setPreInput(event.target.value)}
            />
            <button type="submit">🔍</button>
          </form>
        )}
      </div>
    </div>
  );
}

export default App;

components/DisplayWeather.jsx

import React, { useState, useEffect } from 'react';

export default function DisplayWeather(props) {
  const cityValue = props.inptLocation;
  const key = '34bc9f0e13f7fdd3adac28b4f182d8ad';
  const [apiValue, setApiValue] = useState('');
  const [secondApiValue, setSecondApiValue] = useState('');
  const [loading, setLoading] = useState(true);

  const getWeather = async () => {
    const response = await fetch(
      `https://api.openweathermap.org/data/2.5/weather?q=${cityValue}&appid=${key}&units=metric&lang=KR`,
    );
    const result = await response.json();
    setApiValue(result);
    console.log('첫번재 API', result);
  };

  const getFutureWeather = async () => {
    const response = await fetch(
      `https://api.openweathermap.org/data/2.5/forecast?q=${cityValue}&appid=${key}&units=metric&lang=KR`,
    );
    const result = await response.json();

    const oneDay = 1000 * 60 * 60 * 24;
    const offset = 1000 * 60 * 60 * 9;
    const today = new Date().getTime() + offset;
    const DesiredTime = ' 18:00:00';
    const oneDaysLater = new Date(today + oneDay).toISOString().slice(0, 10) + DesiredTime;
    const twoDaysLater = new Date(today + oneDay * 2).toISOString().slice(0, 10) + DesiredTime;
    const threeDaysLater = new Date(today + oneDay * 3).toISOString().slice(0, 10) + DesiredTime;

    const data = result.list.filter(item => {
      return item.dt_txt === oneDaysLater || item.dt_txt === twoDaysLater || item.dt_txt === threeDaysLater;
    });

    setSecondApiValue(data);
    setLoading(false);
    console.log('두번째 API', result);
    console.log('추출한 데이터', data);
  };

  useEffect(() => {
    getWeather();
    getFutureWeather();
  }, []);

  // * 첫번째 API
  // 날씨 이름 : name
  // 날씨 그림 : apiValue.weather[0].icon
  // 날씨 설명 : weather[0].description
  // 날씨 온도(최고/최저) : main.temp / main.temp_max / main.temp_min

  // * 두번째 API
  // 날씨 그림 : weather[0].icon
  // 예보 일자 : dt_txt
  // 날씨 설명 : weather[0].description
  // 날씨 온도 : main.temp

  return (
		<>
      {loading ? (
        <div>Loading...</div>
      ) : (
        <div onClick={props.turnBack}>
          <div className="today-weather">
            <h1>Now</h1>
            <h1>{apiValue.name}</h1>
            <h2>
              <img src={`https://openweathermap.org/img/wn/${apiValue.weather[0].icon}.png`} alt="" />
            </h2>
            <h2>{apiValue.weather[0].description}</h2>
            <h2>{apiValue.main.temp}</h2>
            <h3>
              최고: {apiValue.main.temp_max} | 최저: {apiValue.main.temp_min}
            </h3>
          </div>

          <div className="feature-weathers">
            {secondApiValue.map((item, index) => {
              return (
                <div className="feature-weather" key={index}>
                  <h4>
                    <img src={`https://openweathermap.org/img/wn/${item.weather[0].icon}.png`} alt="" />
                  </h4>
                  <h4>{item.dt_txt.slice(5, 10)}</h4>
                  <h5>{item.weather[0].description}</h5>
                  <h5>{item.main.temp}</h5>
                </div>
              );
            })}
          </div>
        </>
      )}
    </>
  );
}

💡 App.jsx 에서 inptLocation변수와 turnBack함수를 props로 넘겨준 다음에,
💡 DisplayWeather.jsx 에서 URL에 들어갈 변수값을 inptLocation값으로 재할당을,
💡 결과를 보여주는 요소에서 onClick하면 turnBack함수가 실행되도록 했다
💡 이제 잘못된 값을 넣었을 때의 예외처리를 해보자



try-catch로 예외처리하기

import React, { useState, useEffect } from 'react';

export default function DisplayWeather(props) {
  const cityValue = props.inptLocation;
  const key = '34bc9f0e13f7fdd3adac28b4f182d8ad';
  const [apiValue, setApiValue] = useState('');
  const [secondApiValue, setSecondApiValue] = useState('');
  const [loading, setLoading] = useState(true);

  const getWeather = async () => {
    try {
      const response = await fetch(
        `https://api.openweathermap.org/data/2.5/weather?q=${cityValue}&appid=${key}&units=metric&lang=KR`,
      );
      const result = await response.json();
      setApiValue(result);
      console.log('첫번재 API', result);
    } catch (error) {
      console.error('Error:', error);
      props.turnBack();
    }
  };

  const getFutureWeather = async () => {
    try {
      const response = await fetch(
        `https://api.openweathermap.org/data/2.5/forecast?q=${cityValue}&appid=${key}&units=metric&lang=KR`,
      );
      const result = await response.json();

      const oneDay = 1000 * 60 * 60 * 24;
      const offset = 1000 * 60 * 60 * 9;
      const today = new Date().getTime() + offset;
      const DesiredTime = ' 18:00:00';
      const oneDaysLater = new Date(today + oneDay).toISOString().slice(0, 10) + DesiredTime;
      const twoDaysLater = new Date(today + oneDay * 2).toISOString().slice(0, 10) + DesiredTime;
      const threeDaysLater = new Date(today + oneDay * 3).toISOString().slice(0, 10) + DesiredTime;

      const data = result.list.filter(item => {
        return item.dt_txt === oneDaysLater || item.dt_txt === twoDaysLater || item.dt_txt === threeDaysLater;
      });

      setSecondApiValue(data);
      setLoading(false);
      console.log('두번째 API', result);
      console.log('추출한 데이터', data);
    } catch (error) {
      console.error('Error:', error);
      props.turnBack();
    }
  };

  useEffect(() => {
    getWeather();
    getFutureWeather();
  }, []);

  // * 첫번째 API
  // 날씨 이름 : name
  // 날씨 그림 : apiValue.weather[0].icon
  // 날씨 설명 : weather[0].description
  // 날씨 온도(최고/최저) : main.temp / main.temp_max / main.temp_min

  // * 두번째 API
  // 날씨 그림 : weather[0].icon
  // 예보 일자 : dt_txt
  // 날씨 설명 : weather[0].description
  // 날씨 온도 : main.temp

  return (
<>
      {loading ? (
        <div>Loading...</div>
      ) : (
        <>
          <div className="today-weather">
            <h1>Now</h1>
            <h1>{apiValue.name}</h1>
            <h2>
              <img src={`https://openweathermap.org/img/wn/${apiValue.weather[0].icon}.png`} alt="" />
            </h2>
            <h2>{apiValue.weather[0].description}</h2>
            <h2>{apiValue.main.temp}</h2>
            <h3>
              최고: {apiValue.main.temp_max} | 최저: {apiValue.main.temp_min}
            </h3>
          </div>

          <div className="feature-weathers">
            {secondApiValue.map((item, index) => {
              return (
                <div className="feature-weather" key={index}>
                  <h4>
                    <img src={`https://openweathermap.org/img/wn/${item.weather[0].icon}.png`} alt="" />
                  </h4>
                  <h4>{item.dt_txt.slice(5, 10)}</h4>
                  <h5>{item.weather[0].description}</h5>
                  <h5>{item.main.temp}</h5>
                </div>
              );
            })}
          </div>
        </>
      )}
    </br>
  );
}

🔎 async/await를 사용하는 경우, 예외 처리를 위해 try/catch 를 사용할 수 있다
🔎 try 에 예외가 발생할 수 있는 부분을 넣어주고, catch에 예외가 발생했을 때 예외 처리될 부분을 넣어주면 된다

💡 catch 의 인자로 error를 받아서 error를 출력할 수 있도록 한 다음
 error가 나면 그냥 초기화면으로 넘어가도록 props.turnBack() 함수가 실행되도록 했다

💡 이제 현재위치기반으로 날씨를 불러오는 기능을 추가하기 위해 현위치 정보를 구할 수 있는 API를 이용해보자



Geolocation API 이용하기

Geolocation API 는 사용자의 동의 하에 웹 애플리케이션에서 위치 정보에 접근할 수 있는 API이다
navigator.geolocation 이라는 객체를 통해 Geolocation API를 사용하고
getCurrentPosition() 메서드를 호출해서 현재 위치를 받아올 수 있다

navigator.geolocation.getCurrentPosition(success, error, [options])

🔎 getCurrentPosition의 첫 번째 매개변수에는 위치 정보를 성공적으로 가져왔을 때 실행되는 콜백 함수 이다
🔎 두 번째 매개변수는 위치 정보를 가져오는 데 실패했을 때 실행되는 콜백 함수 이다


navigator.geolocation.getCurrentPosition((position) => {
	console.log(position);
});

콘솔로 GeolocationPosition 객체를 확인한 모습

🔎 GeolocationPosition 이라는 객체를 첫번재 매개변수로 받을 수 있는데
 위 코드에서는 positionGeolocationPosition 이라는 객체를 나타내게 된다

🔎 그 안에는 latitude 와 longitude 로 경도와 위도에 대한 정보가 들어있다

🔎 Geolocation API는 비동기 함수이기 때문에 비동기로 작업이 이루어진다

🔎 이제 fetch 함수의 인자로 들어갈 URL의 매개변수로 위도와 경도가 들어가도록 수정할텐데
 이 위도와 경도를 먼저 동기적으로 가져온 다음에 fetch 함수가 실행 되어야 한다

🔎 따라서, 동기적인 처리를 하기 위해서는 await 를 사용해야하고 await 를 사용하려면
 Promise 객체를 사용해서 비동기함수를 감싼 후 await를 사용가능하다

💡 그럼 URL을 수정하고 Promise 객체로 함수를 감싸보자


import React, { useState, useEffect } from 'react';

export default function DisplayWeather(props) {
  const cityValue = props.inptLocation;
  const key = '34bc9f0e13f7fdd3adac28b4f182d8ad';
  const [apiValue, setApiValue] = useState('');
  const [secondApiValue, setSecondApiValue] = useState('');
  const [loading, setLoading] = useState(true);

  function currentLocation() {
    return new Promise((resolve, reject) => {
      navigator.geolocation.getCurrentPosition(
        position => {
          const latitude = position.coords.latitude;
          const longitude = position.coords.longitude;
          resolve({ latitude, longitude });
        },
        error => {
          reject('error');
        },
      );
    });
  }

  const getWeather = async () => {
    try {
      const { latitude, longitude } = await currentLocation();
      const response = await fetch(
        `https://api.openweathermap.org/data/2.5/weather?lat=${latitude}&lon=${longitude}&appid=${key}&units=metric&lang=KR`,
      );
      const result = await response.json();
      setApiValue(result);
      console.log('첫번재 API', result);
    } catch (error) {
      console.error('Error:', error);
      props.turnBack();
    }
  };

  const getFutureWeather = async () => {
    try {
      const { latitude, longitude } = await currentLocation();
      const response = await fetch(
        `https://api.openweathermap.org/data/2.5/forecast?lat=${latitude}&lon=${longitude}&appid=${key}&units=metric&lang=KR`,
      );
      const result = await response.json();

      const oneDay = 1000 * 60 * 60 * 24;
      const offset = 1000 * 60 * 60 * 9;
      const today = new Date().getTime() + offset;
      const DesiredTime = ' 18:00:00';
      const oneDaysLater = new Date(today + oneDay).toISOString().slice(0, 10) + DesiredTime;
      const twoDaysLater = new Date(today + oneDay * 2).toISOString().slice(0, 10) + DesiredTime;
      const threeDaysLater = new Date(today + oneDay * 3).toISOString().slice(0, 10) + DesiredTime;

      const data = result.list.filter(item => {
        return item.dt_txt === oneDaysLater || item.dt_txt === twoDaysLater || item.dt_txt === threeDaysLater;
      });

      setSecondApiValue(data);
      setLoading(false);
      console.log('두번째 API', result);
      console.log('추출한 데이터', data);
    } catch (error) {
      console.error('Error:', error);
      props.turnBack();
    }
  };

  useEffect(() => {
    getWeather();
    getFutureWeather();
  }, []);

  // * 첫번째 API
  // 날씨 이름 : name
  // 날씨 그림 : apiValue.weather[0].icon
  // 날씨 설명 : weather[0].description
  // 날씨 온도(최고/최저) : main.temp / main.temp_max / main.temp_min

  // * 두번째 API
  // 날씨 그림 : weather[0].icon
  // 예보 일자 : dt_txt
  // 날씨 설명 : weather[0].description
  // 날씨 온도 : main.temp

  return (
		<>
      {loading ? (
        <div>Loading...</div>
      ) : (
        <>
          <div className="today-weather">
            <h1>Now</h1>
            <h1>{apiValue.name}</h1>
            <h2>
              <img src={`https://openweathermap.org/img/wn/${apiValue.weather[0].icon}.png`} alt="" />
            </h2>
            <h2>{apiValue.weather[0].description}</h2>
            <h2>{apiValue.main.temp}</h2>
            <h3>
              최고: {apiValue.main.temp_max} | 최저: {apiValue.main.temp_min}
            </h3>
          </div>

          <div className="feature-weathers">
            {secondApiValue.map((item, index) => {
              return (
                <div className="feature-weather" key={index}>
                  <h4>
                    <img src={`https://openweathermap.org/img/wn/${item.weather[0].icon}.png`} alt="" />
                  </h4>
                  <h4>{item.dt_txt.slice(5, 10)}</h4>
                  <h5>{item.weather[0].description}</h5>
                  <h5>{item.main.temp}</h5>
                </div>
              );
            })}
          </div>
        </>
      )}
    </br>
  );
}

현재위치 기반으로 불러온 모습

💡 이제 입력값을 받으면 원하는 지역의 날씨를 보여주고 입력값을 받지않으면 현재 위치의 날씨를 보여주도록 구현해보자


import React, { useState, useEffect } from 'react';

export default function DisplayWeather(props) {
  const cityValue = props.inptLocation;
  const key = '34bc9f0e13f7fdd3adac28b4f182d8ad';
  const [apiValue, setApiValue] = useState('');
  const [secondApiValue, setSecondApiValue] = useState('');
  const [loading, setLoading] = useState(true);

  function currentLocation() {
    return new Promise((resolve, reject) => {
      navigator.geolocation.getCurrentPosition(
        position => {
          const latitude = position.coords.latitude;
          const longitude = position.coords.longitude;
          resolve({ latitude, longitude });
        },
        error => {
          reject('error');
        },
      );
    });
  }

  const getWeather = async () => {
    try {
      const { latitude, longitude } = await currentLocation();
      const response = await fetch(
        `https://api.openweathermap.org/data/2.5/weather?${
          cityValue ? 'q=' + cityValue : 'lat=' + latitude + '&lon=' + longitude
        }&appid=${key}&units=metric&lang=KR`
      );
      const result = await response.json();
      setApiValue(result);
      console.log('첫번재 API', result);
    } catch (error) {
      console.error('Error:', error);
      props.turnBack();
    }
  };

  const getFutureWeather = async () => {
    try {
      const { latitude, longitude } = await currentLocation();
      const response = await fetch(
        `https://api.openweathermap.org/data/2.5/forecast?${
          cityValue ? 'q=' + cityValue : 'lat=' + latitude + '&lon=' + longitude
        }&appid=${key}&units=metric&lang=KR`,
      );
      const result = await response.json();

      const oneDay = 1000 * 60 * 60 * 24;
      const offset = 1000 * 60 * 60 * 9;
      const today = new Date().getTime() + offset;
      const DesiredTime = ' 18:00:00';
      const oneDaysLater = new Date(today + oneDay).toISOString().slice(0, 10) + DesiredTime;
      const twoDaysLater = new Date(today + oneDay * 2).toISOString().slice(0, 10) + DesiredTime;
      const threeDaysLater = new Date(today + oneDay * 3).toISOString().slice(0, 10) + DesiredTime;

      const data = result.list.filter(item => {
        return item.dt_txt === oneDaysLater || item.dt_txt === twoDaysLater || item.dt_txt === threeDaysLater;
      });

      setSecondApiValue(data);
      setLoading(false);
      console.log('두번째 API', result);
      console.log('추출한 데이터', data);
    } catch (error) {
      console.error('Error:', error);
      props.turnBack();
    }
  };

  useEffect(() => {
    getWeather();
    getFutureWeather();
  }, []);

  // * 첫번째 API
  // 날씨 이름 : name
  // 날씨 그림 : apiValue.weather[0].icon
  // 날씨 설명 : weather[0].description
  // 날씨 온도(최고/최저) : main.temp / main.temp_max / main.temp_min

  // * 두번째 API
  // 날씨 그림 : weather[0].icon
  // 예보 일자 : dt_txt
  // 날씨 설명 : weather[0].description
  // 날씨 온도 : main.temp

  return (
		<>
      {loading ? (
        <div>Loading...</div>
      ) : (
        <div onClick={props.turnBack}>
          <div className="today-weather">
            <h1>Now</h1>
            <h1>{apiValue.name}</h1>
            <h2>
              <img src={`https://openweathermap.org/img/wn/${apiValue.weather[0].icon}.png`} alt="" />
            </h2>
            <h2>{apiValue.weather[0].description}</h2>
            <h2>{apiValue.main.temp}</h2>
            <h3>
              최고: {apiValue.main.temp_max} | 최저: {apiValue.main.temp_min}
            </h3>
          </div>

          <div className="feature-weathers">
            {secondApiValue.map((item, index) => {
              return (
                <div className="feature-weather" key={index}>
                  <h4>
                    <img src={`https://openweathermap.org/img/wn/${item.weather[0].icon}.png`} alt="" />
                  </h4>
                  <h4>{item.dt_txt.slice(5, 10)}</h4>
                  <h5>{item.weather[0].description}</h5>
                  <h5>{item.main.temp}</h5>
                </div>
              );
            })}
          </div>
        </div>
      )}
    </>
  );
}

💡 이제 구현은 모두 끝이 났다
💡 하지만, 두 개의 API를 연동하다보니 불필요하게 currentLocation 함수를 호출해서 구조분해할당하는 부분이 불필요하게 느껴진다
💡 두 개의 API를 하나의 함수에서 연동할 수 있도록 합쳐주고 병렬처리할 필요성이 있다



Promise.all로 병렬처리

Promise.all() 메서드사용하면 Promise.all() 의 인자로 전달된 Promise 들이 모두 완료될 때까지 기다린다
모든 Promise가 완료되면 모든 Promise들의 결과 값이 배열 형태로 반환되는데,
이때 배열 구조 분해 할당을 사용하면 각각의 결과값을 개별 변수로 받을 수 있다
이렇게 개별 변수로 받은 Promise들을 가지고, 추가적인 작업을 수행할 수 있다

const fisrtResponse = fetch(
        'url(1)...',
      );
const secondResponse = fetch(
        'url(2)...',
      );
const thirdResponse = fetch(
        'url(2)...',
      );

Promise.all([fisrtResponse , secondResponse , thirdResponse])
  .then(([data1,data2,data3]) => {
    console.log(data1); 
		console.log(data2); 
		console.log(data3); 
  })
  .catch((error) => {
    console.error(error);
  });

✔️ Promise.all()은 fisrtResponse , secondResponse , thirdResponse 세 개의 프로미스가 모두 이행될 때까지 기다린다
✔️ 그 후, 세 개의 프로미스의 이행 값들이 배열로 반환되고, 콘솔로 각각의 값을 출력한다
✔️ 만약 세 개의 프로미스 중 하나라도 거부된다면, 반환된 프로미스도 즉시 거부된다



async function getApi() {

	const fisrtResponse = fetch(
	        'url(1)...',
	      );
	const secondResponse = fetch(
	        'url(2)...',
	      );
	const thirdResponse = fetch(
	        'url(3)...',
	      );
	
	const [data1,data2,data3] = await Promise.all([fisrtResponse , secondResponse , thirdResponse])

}

🔎 이도 마찬가지로 then을 생략하고 함수로 감싸서 async await로 처리하는 것이 가능하다
🔎 전달된 프로미스 배열에 포함된 프로미스들을 동시에 실행한다
 그 과정은 비동기적으로 이루어지며, 각 프로미스는 병렬로 처리된다

💡 이제 Promise.all 을 통해서 병렬처리를 해주자


import React, { useState, useEffect } from 'react';

export default function DisplayWeather(props) {
  const cityValue = props.inptLocation;
  const key = '34bc9f0e13f7fdd3adac28b4f182d8ad';
  const [apiValue, setApiValue] = useState('');
  const [secondApiValue, setSecondApiValue] = useState('');
  const [loading, setLoading] = useState(true);

  function currentLocation() {
    return new Promise((resolve, reject) => {
      navigator.geolocation.getCurrentPosition(
        position => {
          const latitude = position.coords.latitude;
          const longitude = position.coords.longitude;
          resolve({ latitude, longitude });
        },
        error => {
          reject('error');
        },
      );
    });
  }

  const getWeather = async () => {
    try {
      const { latitude, longitude } = await currentLocation();
      const responseFirst = fetch(
        `https://api.openweathermap.org/data/2.5/weather?${
          cityValue ? 'q=' + cityValue : 'lat=' + latitude + '&lon=' + longitude
        }&appid=${key}&units=metric&lang=KR`,
      );

      const responseSecond = fetch(
        `https://api.openweathermap.org/data/2.5/forecast?${
          cityValue ? 'q=' + cityValue : 'lat=' + latitude + '&lon=' + longitude
        }&appid=${key}&units=metric&lang=KR`,
      );
  
      const [data1, data2] = await Promise.all([responseFirst, responseSecond])
  
      const result1 = await data1.json();
      const result2 = await data2.json();
  
      console.log('a:', result1);
      console.log('b', result2);
  
      const oneDay = 1000 * 60 * 60 * 24;
      const offset = 1000 * 60 * 60 * 9;
      const today = new Date().getTime() + offset;
      const DesiredTime = ' 18:00:00';
      const oneDaysLater = new Date(today + oneDay).toISOString().slice(0, 10) + DesiredTime;
      const twoDaysLater = new Date(today + oneDay * 2).toISOString().slice(0, 10) + DesiredTime;
      const threeDaysLater = new Date(today + oneDay * 3).toISOString().slice(0, 10) + DesiredTime;
  
      const data = result2.list.filter(item => {
        return item.dt_txt === oneDaysLater || item.dt_txt === twoDaysLater || item.dt_txt === threeDaysLater;
      });
  
      console.log('첫번재 API', result1);
      setApiValue(result1);
  
      console.log('두번째 API', result2);
      console.log('추출한 데이터', data);
      setSecondApiValue(data);
  
      setLoading(false);
    } catch (error) {
      console.error('Error: ', error);
      props.turnBack();
    }
  };

  useEffect(() => {
    getWeather();
  }, []);

  // * 첫번째 API
  // 날씨 이름 : name
  // 날씨 그림 : apiValue.weather[0].icon
  // 날씨 설명 : weather[0].description
  // 날씨 온도(최고/최저) : main.temp / main.temp_max / main.temp_min

  // * 두번째 API
  // 날씨 그림 : weather[0].icon
  // 예보 일자 : dt_txt
  // 날씨 설명 : weather[0].description
  // 날씨 온도 : main.temp

  return (
    <>
      {loading ? (
        <div>Loading...</div>
      ) : (
        <div onClick={props.turnBack}>
          <div className="today-weather">
            <h1>Now</h1>
            <h1>{apiValue.name}</h1>
            <h2>
              <img src={`https://openweathermap.org/img/wn/${apiValue.weather[0].icon}.png`} alt="" />
            </h2>
            <h2>{apiValue.weather[0].description}</h2>
            <h2>{apiValue.main.temp}</h2>
            <h3>
              최고: {apiValue.main.temp_max} | 최저: {apiValue.main.temp_min}
            </h3>
          </div>

          <div className="feature-weathers">
            {secondApiValue.map((item, index) => {
              return (
                <div className="feature-weather" key={index}>
                  <h4>
                    <img src={`https://openweathermap.org/img/wn/${item.weather[0].icon}.png`} alt="" />
                  </h4>
                  <h4>{item.dt_txt.slice(5, 10)}</h4>
                  <h5>{item.weather[0].description}</h5>
                  <h5>{item.main.temp}</h5>
                </div>
              );
            })}
          </div>
        </div>
      )}
    </>
  );
}

네트워크 탭으로 병렬처리가 되는지 확인한 모습



최종결과물

URL

카테고리:

업데이트:

댓글남기기