๐Ÿ“” | Weather API๋กœ widget ๋งŒ๋“ค๊ธฐ (feat. Vite, React)

NewHaยท2024๋…„ 6์›” 12์ผ
0
post-thumbnail

OpenWeatehrMap API

API-KEY ํ™•์ธ

ํ™ˆํŽ˜์ด์ง€์— ์ ‘์†ํ•ด ํšŒ์›๊ฐ€์ž…/๋กœ๊ทธ์ธ์„ ํ•˜๋ฉด API KEY๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

API_KEY๋ฅผ ๋ณต์‚ฌํ•œ ๋‹ค์Œ ํ”„๋กœ์ ํŠธ์˜ .env ํŒŒ์ผ์— ๋“ฑ๋กํ•ฉ๋‹ˆ๋‹ค.

์ €๋Š” Vite๋ฅผ ์‚ฌ์šฉํ•ด ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰์ค‘์ด๋ฏ€๋กœ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ž‘์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค.

//Vite๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ, ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ์ด๋ฆ„์€ `VITE_` ๋กœ ์‹œ์ž‘ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
VITE_WEATHER_API_KEY="my_api_key"

//weather widget component์—์„œ importํ•ด์„œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
const API_KEY = import.meta.env.VITE_WEATHER_API_KEY;

โ˜๐Ÿป Vite๊ฐ€ ์•„๋‹Œ ๊ฒฝ์šฐ

// .env
REACT_WEATHER_API_KEY="api_key"
// weather widget component
const API_KEY = prpcess.env.REACT_WEATHER_API_KEY

API-URL

API ํƒญ์—์„œ ์‚ฌ์šฉํ•˜๋ ค๋Š” Current Weather Data ์˜ API doc์„ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.

ํ•ด๋‹น API๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๋Š” url์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

API url์„ ๋ณด๋ฉด lat, lon, API key ์„ธ๊ฐ€์ง€์˜ ๊ฐ’์„ ๋„ฃ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

  • API key ์œ„์น˜์—๋Š” ์œ„์—์„œ ๊ฐ€์ ธ์˜จ API_KEY๋ฅผ ๋„ฃ์–ด์ค๋‹ˆ๋‹ค.

์œ„์น˜ ์ •๋ณด ๊ฐ€์ ธ์˜ค๊ธฐ

url์— ๋„ฃ์„ ์œ„๋„์™€ ๊ฒฝ๋„๋ฅผ ์ ‘์† ์‹œ์˜ ์œ„์น˜๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•˜๊ธฐ ์œ„ํ•ด ์œ„์น˜ ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.

HTML Geolocation API๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์œ ์ €์˜ ์œ„์น˜๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ™œ์šฉํ•ด ์•ฑ ์‹คํ–‰์‹œ ์œ„์น˜๋ฅผ ๊ฐ€์ ธ์˜ค๋„๋ก useEffect๋ฅผ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.

useEffect(() => {
		// ์‚ฌ์šฉ์ž ์œ„์น˜ ์ •๋ณด๋ฅผ ๋น„๋™๊ธฐ์ ์œผ๋กœ ๊ฐ€์ ธ์˜ค๋Š” ํ•จ์ˆ˜
    navigator.geolocation.getCurrentPosition((position) => {
      // position ๋งค๊ฐœ ๋ณ€์ˆ˜๋Š” ํ˜„์žฌ ์œ„์น˜์ •๋ณด๋ฅผ ํฌํ•จํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.
      // position์—์„œ ์œ„๋„๊ฐ’๊ณผ ๊ฒฝ๋„๊ฐ’์„ ๊ตฌํ•ฉ๋‹ˆ๋‹ค.
      const lat = position.coords.latitude;
      const lon = position.coords.longitude;
    });
  }, []); // ์•ฑ์„ ์ฒ˜์Œ ์‹คํ–‰ํ•œ ๊ฒฝ์šฐ์—๋งŒ ๋™์ž‘ํ•˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

์œ„์—์„œ ์–ป์„ ์ˆ˜ ์žˆ๋Š” lat, lon ๊ฐ’์„ ๊ธฐ์ค€์œผ๋กœ ๋‚ ์”จ ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค. ์ด๋•Œ ์œ„์น˜์ •๋ณด๋ฅผ ํ—ˆ์šฉํ•˜์ง€ ์•Š์„ ๊ฒฝ์šฐ๋ฅผ ๋Œ€๋น„ํ•ด ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ์„œ์šธํŠน๋ณ„์‹œ ์ค‘๊ตฌ์˜ ์œ„๋„, ๊ฒฝ๋„๋ฅผ ๋„ฃ์–ด์ค๋‹ˆ๋‹ค.

const getWeather = async (lat = 37.5641, lon = 126.9970) => {
	try {
		const res = await fetch(`https://api.openweathermap.org/data/2.5/weather?lat=${lat}&lon=${lon}&appid=${API_KEY}&units=metric`);
		const data = res.json()
		console.log(data)
	} catch (error) {
		console.error(error)
	}
};

๋‚ ์”จ ์ •๋ณด ๊ฐ€์ ธ์˜ค๊ธฐ

๋‚ ์”จ ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ํ•จ์ˆ˜(getWeather())๋ฅผ ์œ„์น˜ ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ํ•จ์ˆ˜ ์•ˆ์—์„œ lat, lon ๊ฐ’์„ ์ „๋‹ฌํ•˜์—ฌ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค. console๋กœ ๋‚ ์”จ ๋ฐ์ดํ„ฐ๋ฅผ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.

useEffect(() => {
    navigator.geolocation.getCurrentPosition((position) => {
      const lat = position.coords.latitude;
      const lon = position.coords.longitude;
      getWeather(lat, lon);
    });
  }, []);

์ž์„ธํ•˜๊ฒŒ ํ™•์ธํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

{
  "base": "stations",  // ๋‚ด๋ถ€ ๋งค๊ฐœ ๋ณ€์ˆ˜
  "clouds": {
    "all": 0  // ํ๋ฆผ(%)
  },
  "cod": 200,  // ๋‚ด๋ถ€ ๋งค๊ฐœ๋ณ€์ˆ˜
  "coord": {
    "lon": 126.7298,  // ์œ„์น˜์˜ ๊ฒฝ๋„
    "lat": 37.5126    // ์œ„์น˜์˜ ์œ„๋„
  },
  "dt": 1718188665,  // ๋ฐ์ดํ„ฐ ๊ณ„์‚ฐ ์‹œ๊ฐ„(unix, UTC)
  "id": 1838716,  // ๋„์‹œid
  "main": {
    "feels_like": 22.42,  // ์˜จ๋„ (๊ธฐ๋ณธ๊ฐ’ ๋‹จ์œ„ : ์ผˆ๋นˆ)
    "pressure": 1005,  // ํ•ด์ˆ˜๋ฉด์˜ ๋Œ€๊ธฐ์••(hPa)
    "humidity": 60,  // ์Šต๋„(%)
    "temp": 22.54,  // ์˜จ๋„ (๊ธฐ๋ณธ๊ฐ’ ๋‹จ์œ„: ์ผˆ๋นˆ)
    "temp_min": 20.94,  // ์ตœ์ €๊ธฐ์˜จ
    "temp_max": 27.91,  // ์ตœ๊ณ ๊ธฐ์˜จ
  },
  "name": "Bucheon-si",  // ๋„์‹œ์ด๋ฆ„
  "sys": {
    "country": "KR",  // ๊ตญ๊ฐ€ ์ฝ”๋“œ
    "id": 8105,  // ๋‚ด๋ถ€ ๋งค๊ฐœ๋ณ€์ˆ˜
    "sunrise": 1718136690,  // ์ผ์ถœ ์‹œ๊ฐ„(UTC)
    "sunset": 1718189672,  // ์ผ๋ชฐ ์‹œ๊ฐ„(UTC)
    "type": 1,  // ๋‚ด๋ถ€ ๋งค๊ฐœ๋ณ€์ˆ˜
  },
  "timezone": 32400,  // UTC ์ดˆ๋‹จ์œ„ ์ด๋™
  "visibility": 10000,  //๊ฐ€์‹œ์„ฑ (๋‹จ์œ„ m, ์ตœ๋Œ€ 10km)
  "weather": [
    {
      "description": "clear sky",  // ๊ทธ๋ฃน ๋‚ด ๋‚ ์”จ ์ƒํƒœ
      "icon": "01d",   // ๋‚ ์”จ ์•„์ด์ฝ˜ id
      "id": 800,   // ๊ธฐ์ƒ์กฐ๊ฑด id
      "main": "Clear",  // ๋‚ ์”จ ๋งค๊ฐœ๋ณ€์ˆ˜ ๊ทธ๋ฃน(๋น„, ๋ˆˆ, ๊ตฌ๋ฆ„ ๋“ฑ)
    }
  ],
  "wind": {
    "deg": 300, // ํ’ํ–ฅ
    "speed": 3.6,  // ํ’์†(๊ธฐ๋ณธ๊ฐ’ ๋‹จ์œ„ : m/s)
  },
}

์—ฌ๊ธฐ์„œ ์‚ฌ์šฉํ•˜๊ณ ์ž ํ•˜๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์ถ”์ถœํ•ด์„œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

๋‚ ์”จ state ๋งŒ๋“ค๊ธฐ

์ €๋Š” name, weather, temp ์ •๋ณด๋งŒ ์‚ฌ์šฉํ•ด โ€˜ํ˜„์žฌ ์œ„์น˜โ€™, โ€˜๋‚ ์”จ ์˜จ๋„โ€™, โ€˜๋‚ ์”จ ์•„์ด์ฝ˜โ€™, โ€˜๋‚ ์”จ์— ๋Œ€ํ•œ ์„ค๋ช…โ€™์„ ํฌํ•จํ•˜๋Š” ๊ฐ„๋‹จํ•œ ์œ„์ ฏ์„ ๋งŒ๋“ค ๊ฒƒ ์ž…๋‹ˆ๋‹ค. ํ•„์š”ํ•œ ์ •๋ณด๋ฅผ state๋กœ ๊ด€๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด useState๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

const [weather, setWeather] = useState({
	location: '',
	temperature: 0,
	weatherIconUrl: undefined,
	description: '',
});

๋‚ ์”จ ๋ฐ์ดํ„ฐ ๊ฐ€๊ณตํ•˜๊ธฐ

๋‚ ์”จ ์•„์ด์ฝ˜

๊ทธ ์ค‘ โ€˜๋‚ ์”จ ์•„์ด์ฝ˜โ€™์„ ๊ฐ€์ ธ์˜ค๊ธฐ ์œ„ํ•ด API doc์˜ Weather Conditions๋ฅผ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. (์‚ฌ์ดํŠธ์—์„œ ์•„์ด์ฝ˜ ํ˜•ํƒœ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.)

์œ„ ์„ค๋ช…์— ๋”ฐ๋ฅด๋ฉด weahter.icon ๊ฐ’์„ ๋„ฃ์–ด img url์„ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. url์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

const iconUrl = `https://openweathermap.org/img/wn/${weather.icon}@2x.png`;

๋‚ ์”จ์— ๋Œ€ํ•œ ์„ค๋ช…

API doc์— ๋”ฐ๋ฅด๋ฉด ๋‚ ์”จ ์„ค๋ช…์„ ํ•œ๊ธ€๋กœ ๊ฐ€์ ธ์˜ค๊ธฐ ์œ„ํ•ด API_URL์— lang=kr์„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ๋‚˜์™€์žˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ๋ฒˆ์—ญ์ด ๋ถ€์กฑํ•˜์—ฌ ๊ตฌ๊ธ€๋งํ•œ ๊ฒฐ๊ณผ OpenWeatherMap api ๋‹ค๊ตญ์–ด์— ํ•œ๊ตญ์–ด ์ง€์›์„ ์œ„ํ•œ ํ˜‘์—…์šฉ ํŒŒ์ผ์ด๋ผ๊ณ  ๋‚ ์”จ ์ฝ”๋“œ์— ๋”ฐ๋ฅธ ์ ์ ˆํ•œ ์„ค๋ช…์ด ๊ณต์œ ๋˜์–ด ์žˆ์—ˆ๊ณ , ์นœ์ ˆํ•˜์‹  ๋ถ„๋“ค์ด ๋งคํ•‘ ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค์–ด ๊ณต์œ ํ•ด ์ฃผ์…จ์Šต๋‹ˆ๋‹ค.(์ •๋ง ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค!)

๋ฐฐ์—ด์•ˆ์— ๊ฐ์ฒด๋กœ ๋งคํ•‘ํ•œ js์ฝ”๋“œ๋„ ์žˆ์ง€๋งŒ, ์ €๋Š” dart map์œผ๋กœ ์ž‘์„ฑํ•ด์ฃผ์‹  @b0unt9๋‹˜์˜ ์ฝ”๋“œ๋ฅผ js๋กœ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค.

// weatherDescKo.js ํŒŒ์ผ์„ ๋”ฐ๋กœ ์ •์˜
const weatherDescKo = {
    201: '๊ฐ€๋ฒผ์šด ๋น„๋ฅผ ๋™๋ฐ˜ํ•œ ์ฒœ๋‘ฅ๊ตฌ๋ฆ„',
    200: '๋น„๋ฅผ ๋™๋ฐ˜ํ•œ ์ฒœ๋‘ฅ๊ตฌ๋ฆ„',
    ...
    962: 'ํ—ˆ๋ฆฌ์ผ€์ธ',
  };

export default weatherDescKo

weatherWidgetComponent์—์„œ importํ•ด์„œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

// weatherWidgetComponent
import weatherDescKo from '../weatherDescKo';

const getWeather = async (lat, lon ) => {
        try {
           ...
           
            const location = data.name;
            // ์˜จ๋„ ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์™€ ๋ฐ˜์˜ฌ๋ฆผ ํ•ด์ค๋‹ˆ๋‹ค.
            const temperature = Math.round(data.main.temp);
            // icon๋ฒˆํ˜ธ๋ฅผ ๊ฐ€์ ธ์™€ url๋กœ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.
            const weatherIcon = data.weather[0].icon;
            const weatherIconUrl = `https://openweathermap.org/img/wn/${weatherIcon}@2x.png`;
            // weather_id๋ฅผ ํ†ตํ•ด ๊ฐ€์ ธ์˜จ weatherDescKo์—์„œ id์— ํ•ด๋‹นํ•˜๋Š” ์„ค๋ช…์„ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.
            const weatherId = data.weather[0].id;
            const description = weatherDescKo[weatherId];
            
						// ์ •์˜ํ•œ ์†์„ฑ๋“ค์„ weather state์— ๋ฐ˜์˜ํ•ฉ๋‹ˆ๋‹ค.
            setWeather({
                location,
                temperature,
                weatherIconUrl,
                description,
            });
        } catch (err) {
            console.log(err);
        }
    };

return๋ฌธ์—์„œ weather state ๊ฐ’์„ ์ ์ ˆํ•˜๊ฒŒ ๋ฐฐ์น˜ํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ‘€ Reference

profile
๋ฐฑ ๋ฒˆ์„ ๋ณด๋ฉด ํ•œ ๊ฐ€์ง€๋Š” ์•ˆ๋‹ค ๐Ÿ‘€

0๊ฐœ์˜ ๋Œ“๊ธ€