072DATA

친환경 스토어 지도 개발 로그 2편 ( 카카오맵 불러오기, 마커와 인포 윈도우 생성 ) 본문

FrontEnd/Next.js

친환경 스토어 지도 개발 로그 2편 ( 카카오맵 불러오기, 마커와 인포 윈도우 생성 )

0720 2024. 11. 12. 10:58

지난 시간에는 서울 맵의 친환경 스토어 데이터를 분석하고

supabase에 저장하는 과정을 기록했는데욥 

이번 글에서는 실제로 카카오맵을 불러오는 과정을 기록하겠습니다.

 

먼저 카카오맵 API 문서에 나와있는 시작하기를 따라하면서 

개발자 등록 및 API 키를 발급받아야 합니다.

 

그리고 타입 지원이나 지도를 더 원할하게 사용하기 위해서 카카오맵 SDK 라이브러리를 사용합니다

npm install react-kakao-maps-sdk
혹은
yarn add react-kakao-maps-sdk

 

 

 

카카오맵 API 가이드

https://apis.map.kakao.com/web/guide/

 

카카오맵 SDK 가이드

https://react-kakao-maps-sdk.jaeseokim.dev/docs/intro

 

Tutorial | react-kakao-maps-sdk docs

Usage

react-kakao-maps-sdk.jaeseokim.dev

 

 

카카오 지도 불러오기

 

const KAKAO_SDK_URL = `//dapi.kakao.com/v2/maps/sdk.js?appkey=${process.env.NEXT_PUBLIC_KAKAO_APP_JS_KEY}&autoload=false`;

const KakaoMap = () => {
  return (
    <>
      <Script src={KAKAO_SDK_URL} strategy="beforeInteractive" />
      <div className="w-full h-screen"> {/* 지도 컨테이너 크기 설정 */}
        <Map
          center={{
            lat: 37.566826,  // 서울시청 위도
            lng: 126.978656, // 서울시청 경도
          }}
          style={{
            width: "100%",
            height: "100%",
          }}
          level={8} // 지도 확대 레벨
          draggable={true} // 드래그 가능
          zoomable={true}  // 줌 가능
        />
      </div>
    </>
  );
};


카카오 맵 API 키를 .env.local에 환경변수로 저장하고

해당 URL을 Scirpt의 src에 삽입합니다

 

또 지도의 기본 위치를 서울 시청을 중심으로 잡았고

드래그와 줌 기능 지도의 확대 레벨을 8로 설정하였습니다

 

지도 로딩 개선

 

 

지도 페이지에서 지도가 원할하게 불러와지지 않고

새로고침을 통해서만 로드되는 버그가 생겼는데

 

이를 해결하기 위해서 Script 태그를 사용하지 않고

useEffect와 useState를 사용하여 카카오 맵 API의

스크립트를 비동기적으로 불러올 수 있도록 개선했습니다.

 

 const [isMapLoaded, setIsMapLoaded] = useState<boolean>(false);
  
 useEffect(() => {
    const script = document.createElement("script");
    script.src = `//dapi.kakao.com/v2/maps/sdk.js?appkey=${process.env.NEXT_PUBLIC_KAKAO_APP_JS_KEY}&autoload=false`;
    script.async = true;

    script.onload = () => {
      window.kakao.maps.load(() => {
        setIsMapLoaded(true);
      });
    };

    document.head.appendChild(script);

    return () => {
      document.head.removeChild(script);
    };
  }, []);

 

  • useState<boolean>(false): isMapLoaded라는 상태를 생성해서 맵이 로드되었는지 여부를 나타냅니다
  • useEffect:useEffect 내부에서 스크립트를 동적으로 생성하여 문서의 <head>에 추가하는 방식으로 카카오 맵 API를 불러옵니다
  • 스크립트 로드:
    • const script = document.createElement("script");로 <script> 태그를 동적으로 생성합니다
    • script.src 로 카카오 맵 SDK의 URL을 설정합니다 환경 변수에 저장된 API키를 사용합니다
    • script.async = true로 비동기 로드를 설정하여 페이지 로드 성능에 영향을 최소화합니다.
  • script.onload: 스크립트 로드가 완료되면 실행되는 콜백 함수입니다. window.kakao.maps.load() 함수를 호출하여 맵 API 로드가 완료되면 콜백이 실행되고, 그 안에서 setIsMapLoaded(true)를 호출하여 isMapLoaded 상태를 true로 설정합니다.
  • 정리 함수: return문 안에서 정리 함수로 document.head.removeChild(script);를 호출하여 컴포넌트가 언마운트될 때 스크립트를 <head>에서 제거합니다.

 

 

이렇게 해서 페이지 첫 로드시에 지도가 바로 표시되고 isMapLoaded 상태를 사용해서 

지도 로딩 기능도 추가할 수 있을듯 합니다!

 

결과

 

 

마커와 인포윈도우 구현

 

마커아 인포윈도우를 구현하기 전 데이터가 존재해야 합니다

export const useStoreList = () => {
  return useQuery({
    queryKey: ["ecoStores"],
    queryFn: getStoreList,
    staleTime: 5 * 60 * 1000,
    retry: 3
  });
};

 

Tanstack Query를 사용해서 데이터를 요청하였습니다

getStoreList 함수는 단순히 데이터 요청 함수입니다

 

const { data: storeList, isLoading, error } = useStoreList();

    <KakaoMap
        storeList={storeList || []}
    />

 

그리고 요청 받은 데이터를 KakaoMap 컴포넌트에  props로 전달합니다ㅣ

이제 마커와 인포 윈도우를 구현하겠습니다

 

마커 구현

react-kakao-maps-sdk의 MapMarker 컴포넌트를 사용하여 구현했으며

지도 레벨(zoom)이 5 이하일 때만 마커가 표시되도록 설정했습니다

그리고 마커 클릭 시 해당 스토어 정보를 표시하는 이벤트 핸들러 구현했습니닷

 

{level <= 5 && storeList?.map((store) => (
  <MapMarker
    key={store.store_id}
    position={{ lat: store.lat, lng: store.lon }}
    onClick={() => onClick(store)}
  >
    {/* 인포윈도우 내용 */}
  </MapMarker>
))}

 

인포윈도우 구현

MapMarker 컴포넌트의 자식 요소로 인포윈도우 내용을 구현하고

선택된 스토어일 때만 인포윈도우가 표시되도록 조건부 렌더링을 넣었으며

스토어 이름, 주소, 운영시간, 연락처 등 상세 정보를 표시했습니다

{selectedStoreId === store.store_id && (
  <div className="p-2 w-[280px]">
    <div className="mb-2 pb-2 border-b border-gray-100">
      <h3 className="font-bold text-gray-800">{store.store_name}</h3>
    </div>
    <div className="text-sm space-y-1">
      <div className="text-gray-600">{store.road_address}</div>
      <div className="text-gray-500">{store.operating_hours}</div>
      <div className="text-gray-500">{store.contact_number}</div>
    </div>
  </div>
)}

 

  const [selectedStoreId, setSelectedStoreId] = useState<string | null>(null);

 

그리고 이 상태를 가지고 인포윈도우 내용을 구현하는 것 뿐만 아니라 

지도를 이동시키거나 검색창에서 해당 카드에 대한 위치 이동 및 인포 윈도우 출력 등 

다양한 기능을 구현이 가능합니다...!

 

결과물

 

 

 

마치며

 

여기까지 카카오맵을 실제로 불러오고 마커와 인포 윈도우를 출력해보았습니다!

 

다음으로 진행할 사항은 이전에 supabase에 삽입해둔 데이터를 기반으로

리스트 렌더링을 진행하고 해당 장소들의 위치에 따라 마커와 인포 윈도우를 등록,
리스트에 존재하는 카드나 마커 클릭시 지도가 이동하는 클릭 이벤트를 구현할 계획입니다

 

3편에서 뵙겠습니닷..

 

 

1편

https://0723-0725.tistory.com/107

 

친환경 스토어 지도 개발 로그 1편 ( 데이터 분석 및 삽입 )

시작하기 전.. 처음에는 프로젝트에서 서울맵의 지도 api를 활용하여 친환경 스토어를 보여주려고 했지만서울시 지도의 UI/UX가 너무 구리(너구리)길래 카카오맵에서 보여주면 좋겠다는 의견이

0723-0725.tistory.com