본문 바로가기

공부/Next

[리팩토링] Next 카카오 맵 폴리곤 렌더링 리팩토링 - 커스텀 훅, 유틸 함수 구조 개선

반응형

 

리팩토링 전  📖

리팩토링 전 코드는 컴포넌트 내부에 복잡한 로직들이 섞여있어 가독성과 유지보수성이 떨어졌습니다.

 

 

Next 카카오 맵 행정구역 나누기

시도 별로 행정구역을 나누는 작업을 진행한 내용입니다. 행정구역 데이터 파일 📖SHP 파일시도, 시군구, 읍면동, 리 별로 데이터가 있어 원하는 파일을 다운 받으면 됩니다.  대한민국 최신

mingos-habitat.tistory.com

 

특히 폴리곤을 그리는 `좌표 데이터 처리`와 `mouseover``mouseout` 이벤트 처리 로직이 분리되어 있지 않고 중복되어 있어서 재사용성이 부족했습니다.

 

✨그래서 리팩토링 하기로 결정을 했습니다!

 

리팩토링 후 📖

"use client";

const KakaoMapLoader = () => {
  useKakaoLoader();
  const [location, setLocation] = useState({
    center: { lat: 35.90701, lng: 127.570667 },
    isPanto: true,
  });
  const [selectedPath, setSelectedPath] = useState([]);
  const { geoList, setGeoList } = useGeoData(); // 커스텀 훅 사용
  
  // hover 상태 변경 함수 통합
  const handleHoverState = (key: number, isHover: boolean) => {
    setGeoList((prevGeoList) =>
      prevGeoList.map((area) =>
        area.key === key ? { ...area, isHover } : area
      )
    );
  };

  return (
    <Map
      id="map"
      center={location.center}
      isPanto={location.isPanto}
      className="w-[100vw] h-[100vh] relative"
      level={12}
    >
      {geoList.map((item, index) => {
        const { key, path, isHover } = item;
        const color = MAP_COLOR[index];
        return (
          <Polygon
            key={key}
            path={path}
            strokeWeight={2}
            strokeColor={color}
            strokeOpacity={0.8}
            fillColor={isHover ? color : "#ffffff"}
            fillOpacity={0.8}
            onMouseover={() => handleHoverState(key, true)}
            onMouseout={() => handleHoverState(key, false)}
          />
        );
      })}
    </Map>
  );
};

export default KakaoMapLoader;

 

 

먼저 `mouseover`와 `moseout`로직을 하나의 함수로 통합해 중복되는 로직을 제거했습니다.

// hover 상태 변경 함수 통합
  const handleHoverState = (key: number, isHover: boolean) => {
    setGeoList((prevGeoList) =>
      prevGeoList.map((area) =>
        area.key === key ? { ...area, isHover } : area
      )
    );
  };
  
  return (
    <Polygon
      onMouseover={() => handleHoverState(key, true)}
      onMouseout={() => handleHoverState(key, false)}
    />
  )

 

 

그리고 비즈니스 로직을 컴포넌트에서 분리해 `useGeoData` 커스텀 훅을 만들어서 데이터를 가져오는 로직을 분리했습니다.

const { geoList, setGeoList } = useGeoData(); // 커스텀 훅 사용
const useGeoData = () => {
  const [geoList, setGeoList] = useState<GeoData[]>([]);

  useEffect(() => {
    const data = pathListFormatter();

    setGeoList(data);
  }, []);

  return { geoList, setGeoList };
};

export default useGeoData;

 

 

기존에 사용하던 폴리곤 좌표 데이터 처리하던 로직을 `pathListFormatter` 유틸 함수로 분리했습니다. 그리고 `getPolygonPathList`와 `getMultiPolygonPathList`로 좌표 형식을 처리했습니다.

// 행정구역 PathList 가져오기
export const pathListFormatter = () => {
  const { features } = coordRegionCode;

  const data = features.map((item) => {
    const { geometry, properties } = item;
    const { CTP_KOR_NM } = properties;
    const { coordinates, type } = geometry;

    const pathList =
      type === "Polygon"
        ? getPolygonPathList(coordinates)
        : type === "MultiPolygon"
        ? getMultiPolygonPathList(coordinates)
        : [];

    return {
      name: CTP_KOR_NM,
      path: pathList,
      isHover: false,
      key: Math.random(),
    };
  });

  return data;
};

// Polygon pathList 포멧 (3차원 배열)
const getPolygonPathList = (coordinates: CoordinatesType) => {
  return coordinates.map((areaList) =>
    areaList.map(([lng, lat]) => ({ lng: Number(lng), lat: Number(lat) }))
  );
};

// MultiPolygon pathList 포멧 (4차원 배열)
const getMultiPolygonPathList = (coordinates: CoordinatesType) => {
  return coordinates.flatMap((polygon) =>
    polygon.map(
      (areaList) =>
        areaList
          .map((area) => {
            if (Array.isArray(area)) {
              const [lng, lat] = area;
              return { lng: Number(lng), lat: Number(lat) };
            }
            return null; // null을 반환하는 대신 에러를 방지
          })
          .filter((item): item is { lng: number; lat: number } => item !== null) // null 제거
    )
  );
};

 

이를 통해 컴포넌트에는 렌더링에만 집중하고 비지니스 로직은 별도 파일로 분리해 가독성과 재사용성을 높였습니다.

 

회고 🧐

 

이번 리팩토링을 통해 컴포넌트가 복잡한 로직을 처리하기보다는 데이터와 UI의 분리를 극대화할 수 있었습니다. 특히, 이벤트 로직을 통합하면서 코드 중복을 줄이고 간결하게 표현한 것이 큰 성과라고 생각합니다. 앞으로도 지속적으로 코드 구조 개선을 통해 유지보수성과 가독성을 높여나가야겠습니다.

 

아직 많이 부족하기 때문에 조언은 언제나 환영입니다~!!

반응형