[Day63] ๋ ์จ API ์ฐ๋
๐ ์ฌ์ฉ API: OpenWeatherMap
OpenWeatherMap์ ์ ์ธ๊ณ ๋ ์จ ์ ๋ณด๋ฅผ ์ ๊ณตํ๋ ๋ฌด๋ฃ API ์ด๋ค !
โ API Key ๋ณด์ ๊ด๋ฆฌ
.env
ํ์ผ ์์ฑ
API Key๋ ์ธ๋ถ์ ๋
ธ์ถ๋๋ฉด ์ ๋๋ ๋ฏผ๊ฐ ์ ๋ณด โ .env
ํ์ผ์ ์ ์ฅํด์ ์ฝ๋์์ ๋ถ๋ฆฌ ๊ด๋ฆฌ
1
VITE_WEATHER_API_KEY = 8b7f7d685453d033636d45
.env
- ๊ธฐ๋ณธ ํ์ผ, ๋ชจ๋ ํ๊ฒฝ์์ ๋ก๋.env.local
- ๋ก์ปฌ ํ๊ฒฝ ๋ณ์, git์์ ๋ฌด์ํด์ผ ํจ.env.development
- ๊ฐ๋ฐ ํ๊ฒฝ.env.production
- ํ๋ก๋์ ํ๊ฒฝ.env.test
- ํ ์คํธ ํ๊ฒฝ
๐ Vite ํ๊ฒฝ์์์ ์ฃผ์์ฌํญ
VITE_
์ ๋์ฌ๋ Vite ํ๊ฒฝ์์๋ง ํ์VITE_API_KEY=abcdef123456
(O)API_KEY=abcdef123456
(X - ํด๋ผ์ด์ธํธ ์ฝ๋์์ ์ ๊ทผ ๋ถ๊ฐ)
.env.local
์ ๋ฐ๋์.gitignore
์ ์ถ๊ฐํด์ Git์ ์ฌ๋ผ๊ฐ์ง ์๋๋ก ๊ด๋ฆฌ
๐ ํ๊ฒฝ ๋ณ์ ์ฌ์ฉ ์์
1
2
3
4
5
6
7
8
9
10
11
12
13
const API_KEY = import.meta.env.VITE_WEATHER_API_KEY;
const BASE_URL = "https://api.openweathermap.org/data/2.5/weather";
export const getWeatherByCurrentLocation = async (lat, lon) => {
try {
const res = await axios.get(
`${BASE_URL}?lat=${lat}&lon=${lon}&appid=${API_KEY}&lang=kr&units=metric`
);
return res.data;
} catch (error) {
console.log("๋ ์จ ๊ฐ์ ธ์ค๊ธฐ ์คํจ", error);
}
};
import.meta.env.VITE_๋ณ์๋ช
์ผ๋ก ์ฌ์ฉ ๊ฐ๋ฅ
โ ๊ณต๊ณต API ์ฌ์ฉ ๊ฐ์
API ์๋ํฌ์ธํธ
- ํ์ฌ ์์น ๊ธฐ๋ฐ:
https://api.openweathermap.org/data/2.5/weather?lat={์๋}&lon={๊ฒฝ๋}&appid={API_KEY}
- ๋์๋ช
๊ธฐ๋ฐ:
https://api.openweathermap.org/data/2.5/weather?q={๋์๋ช }&appid={API_KEY}
API ํธ์ถ ์ฝ๋ ๊ตฌ์กฐ
๐ useWeatherApi.js
๊ณตํต API ํธ์ถ ํจ์๋ค์ ๋ชจ์๋์ ์ ํธ ํ์ผ
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import axios from "axios";
const API_KEY = import.meta.env.VITE_WEATHER_API_KEY;
const BASE_URL = "https://api.openweathermap.org/data/2.5/weather";
// 1. ์ขํ ๊ธฐ๋ฐ ๋ ์จ ์ ๋ณด
export const getWeatherByCurrentLocation = async (lat, lon) => {
const res = await axios.get(
`${BASE_URL}?lat=${lat}&lon=${lon}&appid=${API_KEY}&lang=kr&units=metric`
);
return res.data;
};
// 2. ํ์ฌ ์์น ๊ธฐ๋ฐ ๋ ์จ ์ ๋ณด
export const getCurrentData = async () => {
return new Promise((resolve, reject) => {
navigator.geolocation.getCurrentPosition(
async (pos) => {
const { latitude, longitude } = pos.coords;
const res = await getWeatherByCurrentLocation(latitude, longitude);
resolve(res);
},
(err) => reject(err)
);
});
};
// 3. ๋์๋ช
๊ธฐ๋ฐ ๋ ์จ ์ ๋ณด
export const getCountryData = async (city) => {
const res = await axios.get(
`${BASE_URL}?q=${city}&appid=${API_KEY}&lang=kr&units=metric`
);
return res.data;
};
๐ ์ getCurrentData์์ Promise๋ฅผ ์๋ก ๋ง๋ค์ด์ผ ํ ๊น?
getCurrentData
ํจ์์์ Promise
๋ฅผ ์ฌ์ฉํ๋ ์ด์ ๋ navigator.geolocation.getCurrentPosition
์ด Promise
๋ฅผ ๋ฐํํ์ง ์๋ ์ฝ๋ฐฑ ๊ธฐ๋ฐ API์ด๊ธฐ ๋๋ฌธ !
โ ๋ฌธ์ ์ํฉ
1
navigator.geolocation.getCurrentPosition(successCallback, errorCallback);
- ์ด API๋ ์ฑ๊ณต ์์๋
successCallback
, ์คํจ ์์๋errorCallback
์ ์คํ - ํ์ง๋ง ๋น๋๊ธฐ ์์
์์๋ ๋ถ๊ตฌํ๊ณ
Promise
๋ฅผ ๋ฐํํ์ง ์๊ธฐ ๋๋ฌธ์await
์ผ๋ก ์ฌ์ฉํ ์ ์๋ค
โ ํด๊ฒฐ ๋ฐฉ๋ฒ: Promise๋ก ๊ฐ์ธ๊ธฐ
์ฐ๋ฆฌ๊ฐ ์ํ๋ ๊ฑด ์๋ ์ฝ๋์ฒ๋ผ getCurrentData
๋ฅผ ๋ถ๋ฌ์ค๋ ๊ฒ
1
const data = await getCurrentData();
์ด๋ ๊ฒ ์ฐ๋ ค๋ฉด getCurrentData
๋ Promise
๋ฅผ ๋ฐํํด์ผ ํ๋ฏ๋ก ๊ฐ์ผ๋ค !
1
2
3
4
5
6
7
8
9
10
11
12
export const getCurrentData = async () => {
return new Promise((resolve, reject) => {
navigator.geolocation.getCurrentPosition(
async (pos) => {
const { latitude, longitude } = pos.coords;
const res = await getWeatherByCurrentLocation(latitude, longitude);
resolve(res); // ์ฑ๊ณตํ๋ฉด ๋ ์จ ๋ฐ์ดํฐ ๋ฐํ
},
(err) => reject(err) // ์คํจํ๋ฉด ์๋ฌ ๋ฐํ
);
});
};
๐ ํ๋ฆ ์์ฝ
- ๋ธ๋ผ์ฐ์ ๊ฐ
ํ์ฌ ์์น ์ขํ
๋ฅผ ๊ฐ์ ธ์ค๊ธฐ ์ํดgetCurrentPosition
ํธ์ถ - ์ฑ๊ณต ์ โ ์ขํ ๋ฐ์์
getWeatherByCurrentLocation
์ผ๋ก ๋ ์จ ์ ๋ณด ์์ฒญ - ์ด ๋ชจ๋ ํ๋ฆ์ ํ๋์
Promise
๋ก ๊ฐ์ธ์await
์ผ๋ก ๋ค๋ฃฐ ์ ์๊ฒ ๋ณํ
๐ก
์์ฝํ์๋ฉด
getCurrentPosition
์ ์ค๋๋ API๋ผ์ ์ฝ๋ฐฑ ํจํด์ ์ฌ์ฉ
๊ทธ๋์async/await
ํจํด์ ์ฐ๋ ค๋ฉด ์ด๊ฑธPromise
๋ก ๊ฐ์ธ๋ฉด ๋น๋๊ธฐ ํ๋ฆ์ ํตํฉํ๊ฒ ํด์ค
getCurrentData
๋ ์ด๊ฑธ ๊ฐ์ธ์, ์ฐ๋ฆฌ๊ฐ ํธํ๊ฒ awaitํ ์ ์๋๋ก ๋์์ฃผ๋ ํจ์
๐ WeatherPage ์ปดํฌ๋ํธ
React์์ useSearchParams
๋ฅผ ํ์ฉํด ๋์๋ช
์ URL๋ก ๊ด๋ฆฌํ๊ณ ,
์ ํ๋ ๋์ ๋๋ ํ์ฌ ์์น์ ๋ฐ๋ผ ๋ ์จ๋ฅผ ๋ณด์ฌ์ฃผ๋ ๊ตฌ์กฐ
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
import React, { useEffect, useState } from "react";
import css from "./WeatherPage.module.css";
import { getCurrentData } from "./useWeatherApi";
import { useSearchParams } from "react-router-dom";
import Button from "../weather/Button";
import { getCountryData } from "./useWeatherApi";
const WeatherPage = () => {
const [searchParams, setSearchParams] = useSearchParams();
const city = searchParams.get("city");
const [weatherData, setWeatherData] = useState(null);
const cityButtons = [
{ id: "current", label: "ํ์ฌ์์น" },
{ id: "seoul", label: "์์ธ" },
{ id: "tokyo", label: "๋์ฟ" },
{ id: "new york", label: "๋ด์" },
{ id: "paris", label: "ํ๋ฆฌ" },
];
useEffect(() => {
const fetchWeatherData = async () => {
try {
let data;
if (city) {
data = await getCountryData(city);
} else {
data = await getCurrentData();
}
setWeatherData(data);
} catch (err) {
console.err(err);
}
};
fetchWeatherData();
}, [city]);
const handleChangeCity = (city) => {
if (city === "current") {
setSearchParams({});
} else {
setSearchParams({ city });
}
};
return (
<main className={css.main}>
<h2>WeatherPage</h2>
<div className={css.weatherInfo}>
<p className={css.location}>
{weatherData?.sys.country} / {weatherData?.name}
</p>
<div className={css.temperature}>
<p>{weatherData?.main.temp} ℃</p>
<p>
<img
src={`http://openweathermap.org/img/wn/${weatherData?.weather[0].icon}.png`}
alt="weather"
/>
</p>
</div>
</div>
<div className={css.btnList}>
{cityButtons.map((button) => (
<Button
key={button.id}
city={button.id}
label={button.label}
onClick={handleChangeCity}
/>
))}
</div>
</main>
);
};
export default WeatherPage;
์คํ ๊ฒฐ๊ณผ ์์
- ๋์ ๋ฒํผ ํด๋ฆญ โ URL์
?city=tokyo
๋ฑ ์๋ ๋ฐ์ ํ์ฌ ์์น
ํด๋ฆญ ์ geolocation API๋ก ํ์ฌ ์ขํ ์์ฒญ