이번 포스팅에서는 리액트를 활용하여 날씨 api를 불러와 날씨앱을 간단히 구현해 보았다.
여기서 중요한 개념들을 정리해보겠다.
목적: API 키와 같은 민감 정보를 코드 외부에 안전하게 보관하는 방법
파일 생성: 프로젝트 루트 경로에 .env
파일 생성 필요
변수명 규칙: Vite 빌드 환경에서는 변수명 앞에 VITE_
접두사 사용 규약 준수
API 키 등록 예시:
VITE_OPENWEATHER_API_KEY=발급받은_자신의_API_키_입력
코드 접근: 애플리케이션 코드 내에서 import.meta.env.VITE_변수명
형태로 접근 가능 구조
서비스 내용: 전 세계 도시 및 위치 기반의 실시간 날씨 정보 제공 서비스
API 키 발급: openweathermap.org 웹사이트 가입 후 My API Keys 메뉴에서 개인 API 키 발급 과정 진행
역할: 브라우저 및 Node.js 환경에서 사용 가능한 Promise 기반 HTTP 클라이언트 라이브러리
설치 방법: npm 또는 yarn 패키지 매니저를 통한 설치 단계 필요
npm install axios
활용: 간결한 문법으로 HTTP 요청(GET, POST 등) 처리 용이성 제공 (fetch보다 편리함)
기능: URL의 쿼리 스트링 파라미터를 생성하고 관리하는 데 유용한 내장 객체
주요 용도: GET 요청 시 필요한 파라미터들을 동적으로 구성하는 상황에 적합
Axios와 함께 사용 예시:
const params = new URLSearchParams();
params.append('q', 'Seoul'); // 도시 이름 파라미터 추가
params.append('appid', import.meta.env.VITE_OPENWEATHER_API_KEY); // API 키 추가
params.append('units', 'metric'); // 측정 단위 추가
axios.get(`https://api.openweathermap.org/data/2.5/weather?${params.toString()}`)
.then(response => {
// 응답 데이터 처리 로직
})
.catch(error => {
// 에러 처리 로직
});
// Note: Axios의 params 옵션을 사용하면 URLSearchParams 객체 또는 일반 객체 전달 가능,
// Axios가 자동으로 쿼리 스트링 생성
axios.get(`https://api.openweathermap.org/data/2.5/weather`, {
params: {
q: 'Seoul',
appid: import.meta.env.VITE_OPENWEATHER_API_KEY,
units: 'metric'
}
})
.then(...)
.catch(...);
필요 기능: 사용자로부터 입력받은 도시 이름과 OpenWeatherMap Current Weather API 조합
구현 절차:
q
)로 전달코드 예시:
useWeatherApi.js
const fetchWeatherByCityName = (cityName) => {
if (!cityName) {
console.log('도시 이름을 입력해주세요.');
return; // 도시 이름 없으면 함수 종료
}
axios.get(`https://api.openweathermap.org/data/2.5/weather`, {
params: {
q: cityName,
appid: import.meta.env.VITE_OPENWEATHER_API_KEY,
units: 'metric'
}
})
.then(response => {
console.log(`${cityName} 날씨 데이터:`, response.data);
// 날씨 데이터 상태 업데이트 또는 화면 표시 로직
})
.catch(error => {
console.error(`${cityName} 날씨 정보 가져오기 에러:`, error);
// 에러 상태 처리
});
};
// 사용 예시 (예: 'Seoul' 검색)
// fetchWeatherByCityName('Seoul');
WeatherPage.jsx
import React, { useEffect, useState } from 'react'
import css from './WeatherPage.module.css'
import Button from './Button'
import { useSearchParams } from 'react-router-dom'
import { getCurrentDate, getCountryData } from './useWeatherApi'
const WeatherPage = () => {
const [searchParams, setSearchParams] = useSearchParams()
const [weatherData, setWeatherData] = useState(null)
const city = searchParams.get('city')
const cityButtons = [
{ id: 'current', label: '현재위치' },
{ id: 'seoul', label: '서울' },
{ id: 'hongkong', label: '홍콩' },
{ id: 'new york', label: '뉴욕' },
{ id: 'paris', label: '파리' },
]
// 데이터 바인딩
useEffect(() => {
const fetchWeatherData = async () => {
try {
let data
if (city) {
// city 파라미터가 있으면 해당 도시의 날씨 정보 가져오기
data = await getCountryData(city)
} else {
data = await getCurrentDate()
}
// console.log('날씨 정보:', data)
setWeatherData(data)
} catch (err) {
console.log('날씨 정보 로딩 에러:', err)
}
}
fetchWeatherData()
}, [city])
const handleChangeCity= city => {
if (city === 'current') {
setSearchParams({})
} else {
setSearchParams({ city })
}
}
return (
<main>
<h2>날씨 API 활용하기</h2>
<div className={css.weatherInfo}>
<p className={css.location}>
{weatherData?.sys.country} / {weatherData?.name}
</p>
<div className={css.tamperature}>
<p>{weatherData?.main.temp} ℃</p>
<p>
<img
src={`http://openweathermap.org/img/wn/${weatherData?.weather[0].icon}.png`}
alt=""
/>
</p>
</div>
</div>
<div className={css.buttonContainer}>
{cityButtons.map(button => (
<Button
key={button.id}
city={button.id}
label={button.label}
onClick={handleChangeCity}
/>
))}
</div>
</main>
)
}
export default WeatherPage
React Query 개요:
React 애플리케이션의 서버 상태 관리에 특화된 라이브러리.
기존 상태 관리 라이브러리(Redux, MobX)와 달리 서버 데이터 페칭, 캐싱, 동기화, 업데이트에 중점.
클라이언트 상태(UI, 폼 입력 등)와 서버 상태(API 데이터, DB 데이터)를 명확히 구분하여 관리.
그니까 요약하면 매번 useEffect, useState로 로딩중.. 데이터 가져오기.. 안해도됨!! 한번 가져온 데이터 알아서 저장하고 똑같은 데이터는 저장된걸로 바로 보여줌!! 데이터가 오래되면 자동 업데이트도 해줌!!
주요 특징:
설치 및 기본 설정:
설치: @tanstack/react-query
, @tanstack/react-query-devtools
(추천) 설치.
기본 설정: 애플리케이션 루트에 QueryClientProvider
를 사용하여 QueryClient
인스턴스 제공.
기본 설정 예시:
import { createRoot } from 'react-dom/client';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
const queryClient = new QueryClient({
defaultOptions: {
queries: {
retry: 1,
refetchOnWindowFocus: false,
},
},
});
createRoot(document.getElementById('root')).render(
<QueryClientProvider client={queryClient}>
{/* App components */}
<ReactQueryDevtools initialIsOpen={false} /> {/* 개발 도구 */}
</QueryClientProvider>
);
핵심 훅 (useQuery
, useMutation
):
useQuery
:
데이터 조회(GET) 작업 시 사용되는 훅.
주요 파라미터: queryKey
(쿼리 식별 유니크 키, 배열), queryFn
(데이터 가져오는 비동기 함수), staleTime
, cacheTime
, enabled
, retry
등.
주요 반환 값: data
(성공 데이터), isLoading
(초기 로딩), isError
(에러 발생), error
(에러 객체), isFetching
(백그라운드 로딩), refetch
(수동 리페치 함수) 등.
useQuery
사용 예시:
import { useQuery } from '@tanstack/react-query';
export const useWeather = city => {
return useQuery({
queryKey: ['weather', city], // 쿼리 식별 키
queryFn: async () => { // 데이터를 가져오는 함수
try {
const data = city ? await getCountryData(city) : await getCurrentDate();
return data;
} catch (error) {
throw error;
}
},
staleTime: 1000 * 60 * 5, // 5분간 데이터 신선 상태 유지
retry: 1, // 실패 시 1번 재시도
});
};~~
useMutation
:
서버 데이터 변경(POST, PUT, PATCH, DELETE 등) 작업 시 사용되는 훅.
주요 옵션/콜백: mutationFn
(데이터 변경 비동기 함수), onSuccess
, onError
, onSettled
등 뮤테이션 라이프사이클 콜백.
주요 반환 값: mutate
(뮤테이션 실행 함수), isLoading
(뮤테이션 실행 중), isError
(에러 발생), error
(에러 객체) 등.
useMutation
사용 예시:
import { useMutation } from '@tanstack/react-query';
import { queryClient } from './queryClient'; // QueryClient 인스턴스
const createTodo = async (newTodo) => {
const response = await fetch('/api/todos', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(newTodo),
});
if (!response.ok) throw new Error('Failed to create todo');
return response.json();
};
export const useCreateTodo = () => {
return useMutation({
mutationFn: createTodo,
onSuccess: () => {
// 뮤테이션 성공 후 특정 쿼리 무효화 (예: todos 목록 쿼리)
queryClient.invalidateQueries(['todos']);
},
onError: (error) => {
console.error('Todo 생성 실패:', error);
},
});
};
데이터 흐름 및 상태:
데이터 상태 주기: Fresh(신선), Stale(오래된), Inactive(비활성), Deleted(삭제됨).
기본 동작:
cacheTime
경과 시 캐시에서 데이터 제거.React Query 개발 도구로 접근