Intro
로컬에 있는 GeoJSON 파일 2개(서울 지도 레이어, 서울 시군구 중심점)를 각각 불러오는데 추후 GeoServer에서 데이터를 요청할 것을 가정해 react-query를 사용했다. 레이어명을 파라미터로 전달해 원하는 파일을 선택 조회하는 것을 의도했는데 처음 전달한 이름 1개만으로 조회가 되는 상황이 발생했다.
기존 코드
import axios from "axios";
import { useQuery } from "react-query";
/**
* 지도 레이어를 가져오는 쿼리
* GeoJson, EPSG:5179
* @param layerName
* @returns {useQuery}
*/
export const useGetSeoulMap = (layerName: string) => {
// 어떤 지도 레이어를 불러올지 선택
let layerUrl = "";
switch (layerName) {
case "seoulSig":
layerUrl = "/src/assets/maps/seoul_sig_5179.geojson";
break;
case "seoulSigCentroid":
layerUrl = "/src/assets/maps/seoul_sig_centroid.geojson";
break;
default:
break;
}
const mapData = async () => {
try {
const { data } = await axios.get(layerUrl);
return data;
} catch (error) {
return console.error("Error loading GeoJson file:", error);
}
};
return useQuery("mapData", () => mapData());
};
// Import
const { data: seoulSigMap } = useGetSeoulMap("seoulSig");
const { data: seoulSigCentroid } = useGetSeoulMap("seoulSigCentroid");
단순히 파일을 로드하는 쿼리이기 때문에 layerName 파라미터에 따라 각각 GeoJSON 파일을 불러오려고 시도했다. 그러나 첫번째로 load를 시도한 seoulSigMap만 2번 호출되었다.
Query Key 로 캐싱하는 React Query
react-query에 대해 좀 더 공부한 후 useQuery 부분이 문제였다는 것을 발견했다. React Query는 Query Key를 기준으로 불러온 데이터를 캐싱하고있는데, 파라미터는 변경되었지만 Query Key는 계속 동일하게 mapData라는 이름으로 사용하고 있기 때문에 데이터 Fetch가 일어나지 않았다. useQuery부분을 수정한 후 의도한대로 데이터가 fetching된 것을 확인할 수 있었다.
// 기존 코드
return useQuery("mapData", () => mapData());
// 수정 코드
return useQuery(["mapData", [layerUrl]], () => mapData());
수정된 전체 코드
import axios from "axios";
import { useQuery } from "react-query";
/**
* 지도 레이어를 가져오는 쿼리
* GeoJson, EPSG:5179
* @param layerName
* @returns {useQuery}
*/
export const useGetSeoulMap = (layerName: string) => {
// 어떤 지도 레이어를 불러올지 선택
let layerUrl = "";
switch (layerName) {
case "seoulSig":
layerUrl = "/src/assets/maps/seoul_sig_5179.geojson";
break;
case "seoulSigCentroid":
layerUrl = "/src/assets/maps/seoul_sig_centroid.geojson";
break;
default:
break;
}
const mapData = async () => {
try {
const { data } = await axios.get(layerUrl);
return data;
} catch (error) {
return console.error("Error loading GeoJson file:", error);
}
};
return useQuery(["mapData", [layerUrl]], () => mapData());
};
react-query 의 캐싱
위 코드는 레이어명을 인자로 전달해 함수 재사용성 향상을 의도했다. 그런데 react-query의 캐싱관점에서 보면 비효율적이라는 생각이 들었다. react-query가 작동하는 방식은 다음과 같다.
처음 데이터 로드 → 사용자가 다른 라우팅으로 이동 → 다시 react-query의 useQuery가 있는 라우팅으로 이동 → 기존 캐싱에 있던 내용을 돌려줌
때문에 기존 코드는 다른 페이지 이동 후 다시 돌아오면 캐싱된 데이터를 제대로 활용하지 못하고 레이어명 변경 때문에 또다시 쿼리를 호출하는 모양새가 된다. 따라서 지도 레이어와 중심점을 불러오는 쿼리를 각각 작성하여 사용하는 것이 캐싱부분에서는 더 효율적인 것 같다.
쿼리를 분리
- 각각 다른 데이터는 쿼리를 분리했다.
- useQuery의 첫번째 인자는 쿼리키, 두번째 인자는 Fetcher, 세번째 인자는 옵션이다.
- 쿼리키는 하나일 경우 배열에 넣지 않아도 내부적에서 자동으로 배열로 변경한다. (둘 이상일 경우 배열을 선언하여 넣어야 한다)
- 쿼리키가 같으면 캐싱 적용 가능, 다르면 서버에 다시 fetch 요청
- 쿼리 내에서 조회조건이 변경되어 다시 fetch가 필요한 경우 쿼리키를 배열로 설정하여 useEffect 처럼 조회 조건 변경을 감지하는 방법도 있다.
import axios from "axios";
import { useQuery } from "react-query";
/**
* 서울 지도 레이어를 가져오는 쿼리
* GeoJson, EPSG:5179
* @returns {useQuery}
*/
export const useGetSeoulMap = () => {
const mapData = async () => {
try {
const { data } = await axios.get(
"/src/assets/maps/seoul_sig_5179.geojson"
);
return data;
} catch (error) {
return console.error("Error loading GeoJson file:", error);
}
};
return useQuery("mapData", () => mapData());
};
/**
* 서울 중심점을 가져오는 쿼리
* GeoJson, EPSG:5179
* @returns {useQuery}
*/
export const useGetSeoulCentroid = () => {
const mapData = async () => {
try {
const { data } = await axios.get(
"/src/assets/maps/seoul_sig_centroid.geojson"
);
return data;
} catch (error) {
return console.error("Error loading GeoJson file:", error);
}
};
return useQuery("mapData", () => mapData());
};
참고 자료
- React Query 강좌 2편. 캐시로 움직이는 useQuery 작동 원리
- 리액트 쿼리 useQuery 사용법
'React' 카테고리의 다른 글
[React] SVG 파일 index.ts에서 export 하기 - SVGR (0) | 2024.05.22 |
---|---|
[React] npm start시 port 번호 변경하는 방법 (0) | 2024.03.06 |
[React-Query] Axios로 로컬 GeoJSON 파일 불러오기 (1) | 2024.02.06 |
[React] Vite 4.0 이상에서 SVG 사용하는 방법 (2) | 2024.02.02 |
[React] 환경변수를 .env → config.js 형태로 변경하기 (0) | 2024.01.30 |