React

[React-Query] 파리미터를 통해 레이어 선택 불러오기, 캐싱고민

캐럿노트 2024. 2. 8. 20:00

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 작동 원리

https://mycodings.fly.dev/blog/2023-09-17-react-query-cachetime-staletime-refetch-poll#react-query-%EA%B0%95%EC%A2%8C-2%ED%8E%B8-%EC%BA%90%EC%8B%9C%EB%A1%9C-%EC%9B%80%EC%A7%81%EC%9D%B4%EB%8A%94-usequery-%EC%9E%91%EB%8F%99-%EC%9B%90%EB%A6%AC---cachetime-staletime-refetchonmount-pollingrefetchinterval

- 리액트 쿼리 useQuery 사용법

https://velog.io/@dkdlel102/React-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EC%BF%BC%EB%A6%AC-useQuery-%EC%82%AC%EC%9A%A9%EB%B2%95