본문 바로가기

Language & Framework/React.js

React에서 날씨 표시하기 (feat. geolocation API를 hook으로 만들기)

https://github.com/NorbertB29/geolocation-api-hook/tree/215f81d1438d617ecdf4a2f1fb7a067228ceb5f8

https://jw910911.tistory.com/108#comment13143318

 

포스팅을 작성하기에 앞서 위 깃허브와 블로그에서 도움을 받았음을 알립니다.

매번 모든 분들께 감사 인사를 드리지는 못하고 있지만 먼저 길을 개척해주시고 정보를 공유해주시는 많은 분들 덕분에 늘 감사하는 마음으로 공부하고 있습니다. 🙇‍♂️

 

 

선 결과물 후 과정

JS 배우면서 다들 한 번쯤 해봤을 날씨 불러오기.

navigator.geolocation + getCurrentPosition || navigator.geolocation + getWatchPosition으로 사용자의 위치를 탐색한 뒤 위도와 경도 값을 받아와 weather API에 넣어주면 알아서 결과 값을 뱉어준다.

(getCurrentPosition은 한 번 받아오는 것이고 getWatchPosition은 꾸준히 받아오는 것 (ex> 지도앱) )

 

아주 간단한 것인데 굳이 geolocation을 hook으로 만들어서 사용하고 싶다는 생각을 했다가 많이 고생했다..

사실 고생했다고 하면 내가 혼자 결국 만들었다는 말 같은데 결국 구글신님의 가호로 해결했습니다.

 

일단 이것을 알아야 뒷부분을 이해 가능하다.

 

1. 우선 geolocation을 hook으로 만드는 작업을 할 것이다.

 

const useCurrentLocation = (options = {}) => {
// 파라미터에 옵션을 따로 넣을 수 있도록 구성
  const [location, setLocation] = useState();
  const [error, setError] = useState();

  const handleSuccess = (location) => {
    const { latitude, longitude } = location.coords;
    setLocation({ latitude, longitude });
  };
// 위치를 가져오는 것에 성공하면 좌표 저장

  const handleError = () => {
    setError("Local navigation failed.");
  };
// 실패시 에러 메세지 지정

  useEffect(() => {
    if (!navigator.geolocation) {
      setError("Geolocation is not supported.");
      return;
    }
 // geolocation을 실행하는 것 자체를 실패할 경우 에러 메세지 지정

    navigator.geolocation.getCurrentPosition(
      handleSuccess,
      handleError,
      options
    );
  }, [options]);
// geolocaition 을 한 번 실행, option값이 바뀔 경우 재실행.

  return { location, error };
};

export default useCurrentLocation;

 

2. getCurrentPosition의 option을 지정해준다. 이것도 따로 모듈로 만들던지, 말던지 편한대로 하면 된다. 나는 모듈로 만들었다.

 

const positionOptions = {
  maximumAge: 0,
  // 뭔지 잘 모르겠다. MDN에서 5번 읽었는데 외계어 같다. 구글링 해봐도 잘 모르겠다.
  timeout: 5000,
  // 위치를 찾기 위해 최대 몇 ms를 소모할 것인가? 만약 infinity로 설정한다면 위치 정보를 받아오기 전까
  // 지는 아무 것도 반환하지 않는다.
  enableHighAccuracy: true,
  // 더 정밀한 위치 추적을 할 것인가? 다만 사용 시 시간 지연이나 배터리 사용량이 증가될 수있음.
  // 이거 켜도 전혀 정밀하지 않은데 끄면 얼마나 이상한 위치에 보내놓을 지 모름. 무조건 켜도록 하자.
};

export default positionOptions;

 

3. 사용할 컴포넌트에 불러오고 내가 화면상에 표시할 값들의 state를 준비한다.

나는 도시 이름, 날씨, 온도 세가지를 불러올 것이다.

function Weather() {
  const { location, error } = useCurrentLocation(positionOptions);
  const [city, setCity] = useState("");
  const [weather, setWeather] = useState("");
  const [temp, setTemp] = useState("");
  }

 

4. weather API를 제공하는 사이트에 가서 회원가입을 하여 user-Key를 발급 받은 뒤 api docs에서 시키는대로 입력하면 된다.

나는 openWeather:https://openweathermap.org/api 에서 제공하는 API를 사용하였으나 AccuWeather나 그 외에도 몇 군데가 있으니 취향에 맞게 사용하면 된다.

그리고 나는 axios를 사용했으나 fetch를 사용해도 당연히 무관하다.

  useEffect(() => {
    if (error) {
      return console.log("error");
    }
    // geolocation에서 error를 받아왔을 경우 error 출력
    // 사용자 위치 값을 받아왔을 경우 아래 코드 실행
    
    if (location) {
      const axios = require("axios");
      const weather_KEY = "몰?루";
      // user key는 직접 받아와서 사용하십시오.
      axios
        .get(
          `https://api.openweathermap.org/data/2.5/weather?lat=${location.latitude}&lon=${location.longitude}&appid=${weather_KEY}&units=metric`
        )
        // 위도, 경도, userKey를 넣어서 get 요청을하면 각종 정보를 보내준다.
        .then((response) => {
          console.log(response);
          setCity(response.data.name);
          setWeather(response.data.weather[0].icon);
          // 그냥 weather를 요청하면 날씨를 글자로 보내주고 icon을 붙이면 날씨에 맞는 이미지 url을 보내준다.
          setTemp(`${response.data.main.temp}°C`);
        })
        .catch(() => {
          alert("Local navigation failed.");
          // 실패했을 경우
          // 사실 여기서 실패는 위치 찾는 것에 실패한 게 아니라 적절한 문구는 아니다. 알아서 수정하십시오.
        });
    }
  }, [location]);
  // 단 한 번만 실행되며 location이 변경될 때만 재실행

 

5. 원하는대로 HTML, CSS 짜서 넣어주자.

나는 city에 state 값이 들어왔을 경우 날씨를 보여주고 아닐 경우 loading 메세지를 띄우도록 설정했다.

나는 styled-components를 사용해서 css를 설정한다.

참고하고 싶은 경우 https://7357.tistory.com/48를 확인해보자.

  return (
    <div>
      {!city == false ? (
        <WeatherDiv>
          <img src={`http://openweathermap.org/img/wn/${weather}.png`}></img>
          <span>{temp}</span>
          <p>{city}</p>
        </WeatherDiv>
      ) : (
        <CostumSpin tip={"Finding your location.."} />
      )}
    </div>
  );
const CostumSpin = styled(Spin)`
  color: white;
`;

const WeatherDiv = styled.div`
  user-select: none;

  img {
    width: 40px;
  }

  span {
    font-size ${(props) => props.theme.fontSize.large}
  }

  p {
    font-size: ${(props) => props.theme.fontSize.xsmall};
    font-weight: 300;
  }
`;

 

6. 순서가 이상해졌는데 해당 로딩 스피너? 는 Ant design라이브러리를 설치해줘야 사용 가능하다.

 

터미널에서 설치하고

npm install antd
// or
yarn add antd
import { Spin } from "antd";
import "antd/dist/antd.css";

임포트 해주면 끝.