개발/React

리액트 날씨 사이트(1 - 날씨 API 가져오기)

TTOLBE 2022. 4. 14. 13:47

리액트로 날씨 사이트 만들기01 - 날씨 API 가져오기

 

날씨 API키 신청하기

 

실시간 날씨 사이트를 만들기 위해선 우선 날씨 API키를 얻어 와야 한다. 아래 사이트로 가서 무료 API키를 신청 해보자.

https://openweathermap.org/price

신청 후에 기다리면 날씨 API 키를 이메일로 보내준다.

나는 이 프로젝트를 최대한 혼자 힘으로 하고 싶었기 때문에 우선 API DOC을 읽었다.

Current weather data

무려 20만개가 넘는 도시의 날씨 정보를 끌어올 수 있으며, 데이터는 JSON, Html, Xml 포맷을 지원한다고 한다.

아래는 API DOC 에 쓰여져 있는 API 호출 방식이다.

https://api.openweathermap.org/data/2.5/weather?lat={lat}&lon={lon}&appid={API key}

lat, lon은 위치 정보 값을 의미한다.(Latitude, Longtitude) 그런데 위치정보 값에 기반한 도시 이름등을 사용하기 위해서는 Geocoding API가 필요하다고 한다. 우선 Geocoding API에 대해 알아보자.

Geocoding API

Geocoding API 호출 방식은 이러하다.

http://api.openweathermap.org/geo/1.0/direct?q={city name},{state code},{country code}&limit={limit}&appid={API key}

여기서 country code 항목은 ISO 3166 코드를 사용하면 되고 state code 항목은 미국의 날씨를 보는 경우에만 쓰면 된다. limit값은 필수적이지는 않고 최대한 5개를 불러 온다고 한다.

Geocoding API 호출 예시 코드는 이러하다.

http://api.openweathermap.org/geo/1.0/direct?q=London&limit=5&appid={API key}

위와 같이 호출시 불러오는 정보는 아래와 같다.

[
  {
    "name": "London",
    "local_names": {
      "af": "Londen",
      "ar": "لندن",
      "ascii": "London",
      "az": "London",
      "bg": "Лондон",
      "ca": "Londres",
      "da": "London",
      "de": "London",
      "el": "Λονδίνο",
      "en": "London",
      "eu": "Londres",
      "fa": "لندن",
      "feature_name": "London",
      "fi": "Lontoo",
      "fr": "Londres",
      "gl": "Londres",
      "he": "לונדון",
      "hi": "लंदन",
      "hr": "London",
      "hu": "London",
      "id": "London",
      "it": "Londra",
      "ja": "ロンドン",
      "la": "Londinium",
      "lt": "Londonas",
      "mk": "Лондон",
      "nl": "Londen",
      "no": "London",
      "pl": "Londyn",
      "pt": "Londres",
      "ro": "Londra",
      "ru": "Лондон",
      "sk": "Londýn",
      "sl": "London",
      "sr": "Лондон",
      "th": "ลอนดอน",
      "tr": "Londra",
      "vi": "Luân Đôn",
      "zu": "ILondon"
    },
    "lat": 51.5085,
    "lon": -0.1257,
    "country": "GB"
  },
  {
    "name": "London",
    "local_names": {
      "ar": "لندن",
      "ascii": "London",
      "bg": "Лондон",
      "de": "London",
      "en": "London",
      "fa": "لندن، انتاریو",
      "feature_name": "London",
      "fi": "London",
      "fr": "London",
      "he": "לונדון",
      "ja": "ロンドン",
      "lt": "Londonas",
      "nl": "London",
      "pl": "London",
      "pt": "London",
      "ru": "Лондон",
      "sr": "Лондон"
    },
    "lat": 42.9834,
    "lon": -81.233,
    "country": "CA"
  },
  {
    "name": "London",
    "local_names": {
      "ar": "لندن",
      "ascii": "London",
      "en": "London",
      "fa": "لندن، اوهایو",
      "feature_name": "London",
      "sr": "Ландон"
    },
    "lat": 39.8865,
    "lon": -83.4483,
    "country": "US",
    "state": "OH"
  },
  {
    "name": "London",
    "local_names": {
      "ar": "لندن",
      "ascii": "London",
      "en": "London",
      "fa": "لندن، کنتاکی",
      "feature_name": "London",
      "sr": "Ландон"
    },
    "lat": 37.129,
    "lon": -84.0833,
    "country": "US",
    "state": "KY"
  },
  {
    "name": "London",
    "local_names": {
      "ascii": "London",
      "ca": "Londres",
      "en": "London",
      "feature_name": "London"
    },
    "lat": 36.4761,
    "lon": -119.4432,
    "country": "US",
    "state": "CA"
  }
]

총 5개의 London이라는 이름을 가진 도시들의 정보가 출력되었다. 직접 해보자.

우선 GeocoodeAPI.js 파일을 생성하고 한 번 API만 불러와 보자.

const { useState, useEffect } = require('react');

const GeocodeAPI = () => {
  const [loading, setLoading] = useState(true);
  const [geo, setGeo] = useState([]);
  const getGeo = async () => {
    const response = await fetch(
      `http://api.openweathermap.org/geo/1.0/direct?q=seoul&limit=5&appid=e6b599415a269a00410ee2f4055aed56`
    );
    const json = await response.json();
    console.log(json);
  };
  useEffect(() => {
    getGeo();
  }, []);
  return <div></div>;
};

export default GeocodeAPI;

GeocodeAPI를 App.js 에 임포트 하고 나서 콘솔을 살펴보면 서울에 대한 정보가 json 형식으로 들어온 것을 볼 수 있다.

우리는 여기서 lat과 lon 정보가 필요하니 geo를 lon, lat으로 바꾸자.

const GeocodeAPI = () => {
  const [loading, setLoading] = useState(true);
  const [lon, setLon] = useState('');
  const [lat, setLat] = useState('');
  const getGeo = async () => {
    const response = await fetch(
      `http://api.openweathermap.org/geo/1.0/direct?q=seoul&limit=5&appid=e6b599415a269a00410ee2f4055aed56`
    );
    const json = await response.json();
    setLoading(false);
    setLon(json[0].lon);
    setLat(json[0].lat);
    console.log(json[0]);
    console.log(`lon:${lon}`)
    console.log(`lat:${lat}`)
  };
  useEffect(() => {
    getGeo();
  }, []);
  return <div></div>;
};

console log를 확인하니 새로 고침을 할 때마다 자꾸 빈 값이 나왔지만 React dev tools(크롬확장프로그램)의 Component칸을 들어가니 스테이트에 제대로 값이 담긴걸 확인할수 있었다.

lon과 lan 값을 가져왔으니 이제 날씨 API를 호출 할 차례이다. 이 쯤에서 지리정보를 끌어오는건 부가적인 기능이니 날씨 API를 끌어오는 컴포넌트와 하나로 처리해도 무방할거라고 생각했고 컴포넌트와 파일명을 WeatherAPI로 변경했다.

const WeatherAPI = () => {
  const [loading, setLoading] = useState(true);
  const [lon, setLon] = useState('');
  const [lat, setLat] = useState('');
  const [weather, setWeather] = useState([]);
  const getGeo = async () => {
    const response = await fetch(
      `http://api.openweathermap.org/geo/1.0/direct?q=seoul&limit=5&appid=e6b599415a269a00410ee2f4055aed56`
    );
    const json = await response.json();
    setLoading(false);
    setLat(json[0].lat);
    setLon(json[0].lon);
  };
  const getWeather = async () => {
    const response = await fetch(
      `https://api.openweathermap.org/data/2.5/weather?lat=${lat}&lon=${lon}&appid=e6b599415a269a00410ee2f4055aed56`
    );
    const json = await response.json();
    setWeather(json);
    console.log(json);
  };
  useEffect(() => {
    getGeo();
  }, []);
  useEffect(() => {
    getWeather();
  }, [lat, lon]);
  return (
    <div>
      <p>{loading ? 'Loading' : null}</p>
      <p>{lon}</p>
      <p>{lat}</p>
    </div>
  );
};

처음에는 getWeather 함수를 콜백함수로 넣어봤지만 자꾸 lat과 lon 정보가 담기지 않은 채로 Weather 값을 불러와서 useEffect 함수에 lat과 lon의 값이 변경된 후에 함수가 실행 되게 만들었다.

이번에는 날씨와 도시 정보가 화면에 출력되게 만들어 보자.

const WeatherAPI = () => {
  const [loading, setLoading] = useState(true);
  const [lon, setLon] = useState(0);
  const [lat, setLat] = useState(0);
  const [weather, setWeather] = useState([]);
  const getGeo = async () => {
    const response = await fetch(
      `http://api.openweathermap.org/geo/1.0/direct?q=shanghai&limit=5&appid=e6b599415a269a00410ee2f4055aed56`
    );
    const json = await response.json();
    setLat(json[0].lat);
    setLon(json[0].lon);
    console.log('set geo');
  };
  const getWeather = async () => {
    const response = await fetch(
      `https://api.openweathermap.org/data/2.5/weather?lat=${lat}&lon=${lon}&appid=e6b599415a269a00410ee2f4055aed56`
    );
    const json = await response.json();
    setWeather(json);
    console.log(json);
    setLoading(false);
  };
  useEffect(() => {
    getGeo();
  }, []);
  useEffect(() => {
    getWeather();
  }, [lon]);

  return (
    <div>
      {loading ? (
        'Loading'
      ) : (
        <div>
          <p>{weather.name}</p>
          <ul>
            <li>{weather.main.humidity}</li>
            <li>{Math.round((weather.main.temp - 273.15) * 2) / 2}</li>
            <li>{weather.weather[0].main}</li>
            <li>{weather.weather[0].description}</li>
          </ul>
        </div>
      )}
    </div>
  );
};

여기까지 코드를 짜면서 큰 문제가 2개 있었는데,

  1. useEffect를 썼음에도 불구하고 자꾸만 getWeather 함수가 lon과 lat 값이 지정되기도 전에 호출되었다.
  2. 날씨 값이 지정되기 전에 자꾸만 loading 값이 false가 되어버려 출력 할 게 없으므로 오류가 뜬다.

이 두가지 문제를 해결하기위해 콘솔로그로 각 함수의 실행 순서와 타이밍을 알아냈고, 우선 lon과 lat의 기본 값을 0으로 주어서 getWeather 함수가 먼저 호출되더라도 weatherAPI에서 불러올 값이 있게 만들었다. 그렇게 했더니 아주 잠시 0,0의 날씨 값이 뜨다가 선택한 도시의 날씨 값이 화면에 출력된다.

다음번에는 Input을 추가하여 원하는 도시의 날씨를 찾는 기능을 넣어 볼 것이다.

 

ps: GeoAPI를 불러올 때 http가 아닌 https 값으로 불러와야 github 페이지에서 작동한다.

'개발 > React' 카테고리의 다른 글

NextJS 시작하기  (0) 2023.01.02
React Hooks 01) useState, useInput, useTab  (0) 2022.04.15
리액트 날씨 사이트(0 - 준비)  (0) 2022.04.12