📌 오늘 학습한 주제
- OpenWeather API 연동을 통한 비동기 날씨 데이터 수신 및 온섭씨 단위 변환 (
WeatherPage.jsx, WeatherBox.jsx)
- Kakao 지도 지도/마커 SDK 조작 및 클릭 기반 위치 좌표 수신 (
KakaoMap.jsx)
- Kakao Geocoder를 활용한 도시 이름 ↔ 위/경도 좌표 변환 및 지도 동적 이동 (
WeatherPage.jsx)
- 도시 변경 및 좌표 클릭 기반의 조건부 날씨 데이터 바인딩
💡 오늘 배운 것 — 나만의 언어로
① 오늘의 목표
- Kakao 지도 API와 OpenWeather API를 결합하여 지도의 클릭 지점 및 도시 선택 버튼 동작 시 위/경도 좌표 기반으로 실시간 날씨 정보를 가져와 화면에 출력하는 통합 날씨 애플리케이션 구축하기
② 학습할 내용
- Kakao 지도 SDK 제어 (
KakaoMap.jsx): window.kakao.maps.Map과 Marker 객체를 활용해 지도를 생성하고, useRef로 지도/마커 인스턴스를 유지했다. 클릭 이벤트 발생 시 해당 위치의 위/경도(lat, lng) 값을 추출하여 날씨 수신 함수로 전달했다.
- 주소/도시명 ↔ 좌표 변환 (
WeatherPage.jsx): window.kakao.maps.services.Geocoder()의 addressSearch를 사용하여 사용자가 선택한 도시명(예: "서울", "부산")을 위/경도 좌표로 변환하고, 변환된 좌표로 지도의 중심점과 마커 위치를 이동시켰다.
- 날씨 정보 가공 및 단위 변환 (
WeatherBox.jsx): OpenWeather API에서 온 켈빈(Kelvin) 온도 데이터를 섭씨(℃)로 변환(temp - 273.15)하여 소수점 첫째 자리까지 표시했다.
💻 오늘 핵심 코드
1. Kakao 지도 생성 및 클릭 이벤트 바인딩 (KakaoMap.jsx)
import { useEffect, useRef } from "react";
const KakaoMap = ({ setWeatherByCoords, moveTo }) => {
const mapRef = useRef(null);
const markerRef = useRef(null);
useEffect(() => {
window.kakao.maps.load(() => {
navigator.geolocation.getCurrentPosition((position) => {
const container = document.getElementById('map');
const centerPosition = new window.kakao.maps.LatLng(37.5665, 126.9780);
const map = new window.kakao.maps.Map(container, { center: centerPosition, level: 3 });
const marker = new window.kakao.maps.Marker({ position: centerPosition });
marker.setMap(map);
mapRef.current = map;
markerRef.current = marker;
window.kakao.maps.event.addListener(map, "click", function (mouseEvent) {
const lat = mouseEvent.latLng.getLat();
const lng = mouseEvent.latLng.getLng();
marker.setPosition(new window.kakao.maps.LatLng(lat, lng));
setWeatherByCoords(lat, lng);
});
});
});
}, []);
useEffect(() => {
if (!moveTo || !mapRef.current || !markerRef.current) return;
const lat = parseFloat(moveTo.lat);
const lng = parseFloat(moveTo.lng);
const position = new window.kakao.maps.LatLng(lat, lng);
mapRef.current.setCenter(position);
markerRef.current.setPosition(position);
}, [moveTo]);
return <div id='map' style={{ width: '100%', height: '400px' }}></div>;
}
export default KakaoMap;
2. 도시명 ↔ 좌표 변환 및 날씨 수신 통신 (WeatherPage.jsx)
import { useEffect, useState } from "react";
import WeatherBox from "../ui/WeatherBox";
import WeatherButton from "../ui/WeatherButton";
import KakaoMap from "../ui/KakaoMap";
import '../css/weather.css';
const WeatherPage = () => {
const key = process.env.REACT_APP_WEATHER_API_KEY;
const cities = ["서울", "부산", "대전", "인천", "대구", "전남광주"];
const [city, setCity] = useState('');
const [weather, setWeather] = useState({});
const [moveTo, setMoveTo] = useState({});
const cityHandler = (e, cityName) => {
setCity(cityName);
getCoordsByCity(cityName);
};
const getWeatherByCoords = async (lat, lng) => {
const endPoint = `https://api.openweathermap.org/data/2.5/weather?lat=${lat}&lon=${lng}&appid=${key}`;
await fetch(endPoint)
.then(res => res.json())
.then(data => setWeather(data))
.catch(err => console.log('fetch error:', err));
};
const getCoordsByCity = (cityName) => {
const geocoder = new window.kakao.maps.services.Geocoder();
geocoder.addressSearch(cityName, (result, status) => {
if (status === window.kakao.maps.services.Status.OK) {
const lat = parseFloat(result[0].y);
const lng = parseFloat(result[0].x);
setMoveTo({ lat, lng, time: Date.now() });
getWeatherByCoords(lat, lng);
}
});
};
return (
<div className="container">
<KakaoMap setWeatherByCoords={getWeatherByCoords} moveTo={moveTo} />
<WeatherBox weather={weather} />
<WeatherButton cities={cities} city={city} handler={cityHandler} />
</div>
);
}
export default WeatherPage;
3. 날씨 데이터 바인딩 및 섭씨 변환 (WeatherBox.jsx)
import '../css/weather.css';
const WeatherBox = ({ weather }) => {
return (
<div className='weather-box'>
<div className='weather-city'>{weather.name}</div>
<div className='weather-temp'>
{weather?.main?.temp
? (weather.main.temp - 273.15).toFixed(1) + " ℃"
: "로딩중"}
</div>
<div className='weather-desc'>
{weather?.weather?.[0]?.description}
</div>
</div>
);
}
export default WeatherBox;
🛠️ 실습 / 결과물 & 참고 자료
③ 실습 / 결과물
- 강의 실습코드: Kakao 지도 컴포넌트(
KakaoMap.jsx), 날씨 정보 표시(WeatherBox.jsx), 도시 선택 버튼(WeatherButton.jsx), 날씨 페이지 메인(WeatherPage.jsx)을 연동하여, 지도 클릭 및 도시 선택에 따라 위치 마커 이동과 섭씨 온도 표시가 동적으로 이뤄지는 결과물 완성.
④ 참고 자료
🔍 문제와 해결
- 막힌 부분: 지도 클릭 시 발생하는 마커 이동 및 도시 버튼 클릭 시 주소 검색 결과 좌표 반영이 지도 객체 재생성 없이 이루어지지 않는 현상 발생.
- 해결 방법:
useRef를 이용해 초기 생성된 지도(mapRef.current)와 마커(markerRef.current) 인스턴스를 유지하고, useEffect 의존성 배열로 moveTo 상태 변경을 감지하여 지도의 중심점과 마커 좌표만 업데이트(setCenter, setPosition)함으로써 효율적인 화면 갱신을 구현함.
🎯 다음에 할 일