React의 컴포넌트·상태·이벤트·Hook·라우팅 흐름을 복습하고, OpenWeatherMap과 카카오맵 OpenAPI를 호출해 위치에 따른 날씨를 화면에 출력했다.
React 화면은 다음과 같은 흐름으로 동작한다.
사용자 이벤트 → state 변경 → 컴포넌트 재렌더링 → 화면 갱신
const [city, setCity] = useState("");
const cityHandler = (cityName) => {
setCity(cityName);
};
state는 직접 수정하지 않고 반드시 setter 함수를 사용해야 한다.
useState
화면에서 변경되는 데이터를 관리한다.
const [weather, setWeather] = useState({});
useEffect
컴포넌트가 렌더링된 이후 API 호출이나 외부 라이브러리 연결 같은 작업을 처리한다.
useEffect(() => {
getCurrentLocation();
}, []);
의존성 배열에 따라 실행 시점이 달라진다.
[]: 처음 마운트될 때 실행[city]: city가 변경될 때 실행useRef
렌더링과 관계없이 값이나 DOM·외부 객체를 유지할 때 사용한다.
const mapRef = useRef(null);
const markerRef = useRef(null);
카카오 지도와 마커 객체를 저장해 다른 이벤트에서도 다시 사용할 수 있었다.
OpenAPI는 외부 서버가 제공하는 데이터를 정해진 주소와 요청 형식에 맞춰 가져오는 방식이다.
API Key 준비
→ Endpoint와 Query String 작성
→ fetch 요청
→ JSON 변환
→ state 저장
→ 화면 렌더링
API Key는 환경변수에서 불러왔다.
const key = process.env.REACT_APP_WEATHER_API_KEY;
Create React App에서는 환경변수 이름을 REACT_APP_으로 시작하고, 환경변수를 변경한 뒤에는 개발 서버를 다시 실행해야 한다.
단, 프론트엔드 환경변수는 빌드 결과에 포함되므로 완전한 비밀 저장소는 아니다. 중요한 비밀 키는 백엔드 서버를 통해 호출하는 방식이 안전하다.
fetch()로 날씨 데이터 호출하기도시 이름으로 날씨를 조회할 수 있다.
const getCityWeather = async () => {
const endpoint =
`https://api.openweathermap.org/data/2.5/weather` +
`?q=${city}&appid=${key}`;
const response = await fetch(endpoint);
if (!response.ok) {
throw new Error("날씨 정보를 불러오지 못했습니다.");
}
const data = await response.json();
setWeather(data);
};
fetch()의 응답은 바로 사용할 수 없기 때문에 response.json()으로 JSON 데이터를 꺼내야 한다.
위도와 경도를 이용할 때는 Query String만 변경한다.
const endpoint =
`https://api.openweathermap.org/data/2.5/weather` +
`?lat=${lat}&lon=${lng}&appid=${key}`;
브라우저의 Geolocation API로 현재 위치를 얻을 수 있다.
navigator.geolocation.getCurrentPosition((position) => {
const lat = position.coords.latitude;
const lng = position.coords.longitude;
getWeatherByCoords(lat, lng);
});
사용자가 위치 권한을 허용해야 하며, 실제 코드에서는 권한 거부에 대한 오류 처리도 필요하다.
카카오맵을 클릭하면 클릭한 지점의 위도와 경도를 가져온다.
kakao.maps.event.addListener(map, "click", (mouseEvent) => {
const lat = mouseEvent.latLng.getLat();
const lng = mouseEvent.latLng.getLng();
marker.setPosition(mouseEvent.latLng);
getWeatherByCoords(lat, lng);
});
이 좌표를 OpenWeatherMap API에 전달하면 지도에서 선택한 위치의 날씨를 조회할 수 있다.
지도 클릭
→ 위도·경도 추출
→ 날씨 API 호출
→ weather state 변경
→ WeatherBox 재렌더링
API 응답은 비동기로 도착하기 때문에 최초 렌더링 시 데이터가 없을 수 있다. 옵셔널 체이닝을 사용하면 오류 없이 접근할 수 있다.
<div>{weather?.name}</div>
<div>
{weather?.main?.temp
? (weather.main.temp - 273.15).toFixed(1)
: "로딩 중"}
</div>
<div>{weather?.weather?.[0]?.description}</div>
기온은 Kelvin으로 전달되기 때문에 273.15를 빼서 섭씨로 변환했다.
map()으로 배열을 컴포넌트 목록으로 변환한다.key가 필요하다.&&로 조건부 렌더링을 처리한다.value와 onChange를 연결해 제어 컴포넌트를 만든다.useNavigate()와 useParams()로 페이지 이동과 URL 데이터를 처리한다.get, post, patch, delete로 CRUD 요청을 처리한다.useRef를 사용하면 카카오 지도와 마커 객체를 리렌더링 이후에도 유지할 수 있다.fetch()는 Axios와 달리 JSON 변환과 HTTP 상태 확인을 직접 처리해야 한다.도시 버튼을 클릭했을 때 도시 이름으로 날씨를 조회하는 함수와 좌표로 조회하는 함수가 모두 실행되면 API가 중복 호출될 수 있다.
따라서 도시 이름 방식과 좌표 방식 중 하나를 기준으로 흐름을 정리하는 것이 좋다.
const cityHandler = (cityName) => {
setCity(cityName);
getCoordsByCity(cityName);
};
도시를 좌표로 변환한 다음 좌표 기반 날씨 요청만 실행하면 지도 이동과 날씨 조회를 하나의 흐름으로 처리할 수 있다.
useRef를 이용해 지도 컴포넌트와 날씨 컴포넌트 연결하기도시 버튼이나 지도에서 위치를 선택하면 좌표를 구하고, 해당 좌표의 날씨 정보를 조회해 지역명·기온·날씨 설명을 출력하는 흐름을 이해했다.
작업 중 코드 구조가 꼬이면서 기존 코드와 강사님 코드를 구분하기 어려워졌다.
기존 코드를 src_tmp, src_tmp2에 보관하고 강사님 코드를 현재 src로 가져와 정상적인 구조부터 다시 확인했다.
앞으로는 작업 전 커밋을 남기고 별도 브랜치에서 수정하면 파일을 직접 복사하지 않아도 이전 상태와 비교하거나 복구할 수 있다.
src_tmp, src_tmp2와 현재 코드를 비교해 필요한 기능만 다시 적용하기