# TIL - 2026.08.10

ssls·2026년 8월 10일

📌 오늘 학습한 주제

  • 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.MapMarker 객체를 활용해 지도를 생성하고, 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);
                });
            });
        });
    }, []);

    // 외부 도시 선택으로 moveTo 변경 시 지도 및 마커 이동
    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));
    };

    // Kakao Geocoder를 이용한 주소 -> 위/경도 변환
    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)함으로써 효율적인 화면 갱신을 구현함.

🎯 다음에 할 일

  • Kakao 지도에 다중 마커 표기 기능 및 커스텀 인포윈도우(Custom Overlay) 추가해보기
  • OpenWeather 5일/3시간 단위 일기예보 API를 연동하여 날씨 그래프 UI 추가하기
profile
성장중

0개의 댓글