React에 카카오맵을 설정 하고 map
을 불러오는데, 여러 방법들, 문제점등의 글이 작성되어있었다.
여러 블로그와 공식문서를 읽어보고 내가 "이해"한 것을 적어본다.
카카오맵 공식문서와 블로그에서 자주 볼 수 있는 것이 있다.
<script type="text/javascript" src="//dapi.kakao.com/v2/maps/sdk.js?appkey=발급받은 APP KEY를 넣으시면 됩니다."></script>
먼저 script를 index.html에 첨부하여 window
에 kakao
객체를 넣어준다.
-> 실행코드보다 먼저 선언되어야 window객체 안에 kakao를 실행할 수 있으니까
특정 컴포넌트 파일을 생성하고 document.getElementById('아이디이름')
으로 그 요소안에 kakaoMap
을 주입해준다.
const { kakao } = window;
const KakaoMap = () => {
useEffect(() => {
const container = document.getElementById('map');
const options = {
center: new kakao.maps.LatLng(33.450701, 126.570667),
level: 3
};
const map = new kakao.maps.Map(container, options);
}, []);
return (
<div id='map' style={{
width: '500px',
height: '500px'
}}></div>
);
}
map이라는 아이디를 먼저 선언 후, 그 이후에 사이드 이펙트인 useEffect
로 카카오 맵을 첨부한다.
조작하고, 기능 넣고 휘뚜루 마뚜루 하고 싶으면
const KakaoMap = () => {
//...
const mapRef = useRef<kakao | null>(null)
const [state,setState] = useState(state) //예시일뿐입니다.
//...
useEffect(() => {
//...생략
const map = new kakao.maps.Map(container, options);
if(!mapRef.current) mapRef.current = map
return (()=>//... map객체 삭제
)
}, []);
useEffect(() => {
// 원하는 작업을 하면 됩니다.
}, [state]);
//... 생략
useRef
를 통해 직접적으로 조건문을 걸어 라이프 사이클을 활용하여 useEffect
안에서 map을 없애고, 다시 map을 생성하며 지도api를 사용할 수 있다. 그리고 clean up function을 통해 unmount
할 때 map을 삭제해주면 깔끔해집니다.
위에 것만 보아도 어떻게 해야할지 감이 잡히는 것 같습니다. 저번 부트캠프 때 kakaoMap관련 자료조사 및 현재까지 조사하고 이해한 걸로 치면 4주짜리 이해한 건데, 혹시 틀린 내용이 있으면, 비판해주세요 비난말고요~~
Dom 조작으로 카카오맵을 설치?, 조작하는 방법을 간략히 알아봤지만, 무언가 찜찜한 느낌이 듭니다..
어쩔수 없이 위와 같이 DOM조작을 해야하나?? 무언가 리액트 스럽지 못하다는 느낌이 강력히 들고, 마음적으로 불편함이 커집니다.
이렇게 불편함이 있지만, react이므로 react kakaomap이라고 구글링해보면 react용 kakaoMap이 있다.
react-kakao-maps-sdk를 사용하여 선언적 프로그래밍으로만으로도 카카오맵을 사용 할 수 있다.
현재 내 프로젝트는 Next.js page router
를 사용하고 있다.
먼저 react-kakao-maps-sdk를 설치하자
pnpm i react-kakao-maps-sdk
그런다음에 KakaoMap.tsx 파일을 생성 후 다음과 같이 작성해보자
import Script from 'next/script';
import { Map } from 'react-kakao-maps-sdk';
const KAKAO_SDK_URL = `//dapi.kakao.com/v2/maps/sdk.js?appkey=${NEXT_PUBLIC_KAKAO_APP_JS_KEY}&autoload=false`;
const KakaoMap = () => {
return (
<>
<Script src={KAKAO_SDK_URL} strategy="beforeInteractive" />
<Map center={{ lat: 33.450701, lng: 126.570667 }} style={{ width: '100%', height: '100%' }}>
</Map>
</>
);
};
export default KakaoMap;
여기서 중요 포인트는 다음 2가지
autoload=false
를 추가해야 합니다. 만약 이 파라미터를 생략하거나 true 값을 주게 되면, kakao.maps 제대로 로딩하지 못하게 되어TypeError: kakao.maps.LatLng is not a constructor
오류가 발생합니다.beforeInteractive
로 지정해주어야 합니다.afterInteractive
로 되어있는데, nextjs모듈들이 형성되기전부터 kakaoMap을 로드하기 위해서 beforeInteractive
로 지정했습니다. 이렇게 하면 react-sdk에서 제공하는 Map
Component와 Marker, Circle 등 다양한 리액트 Component를 활용해서 프로젝트에서 사용 하면 된다.
위의
Script
를 사용하여 카카오맵을 조작했지만, 조금더 좋은것이 없을까 생각하게 되었다. ( 이놈의 욕심과, 집작은.... 진짜 )
react-kakao-maps-sdk에서 내장되어있는 class인 useKakaoLoader
를 사용해서 카카오 맵을 주입할 수 있다.
해당 hook은 cleanup 시점에도 Kakao Map Api를 제거 하지 않고, 동일한 hook를 여러 위치에서 호출 하더라도 최초 한번만 Loading 됩니다.
내부에서 반환하는 loading state는 hook를 통해 제어할 때 사용하도록 제공하는 state 입니다.
loading를 통한 Map 컴포넌트를 conditional rendering를 하지 않아도 됩니다.
ReactKakaoMapSDK useKakaoLoader
kakao Map API를 최초 한번만 불르고 unmount 되었다가 다시 mount될 때도 살아있다라고 이해 했다. Next.js page router에서는 page 단으로 최적화를 진행하므로 무리가 없다 판단하였고, Map Page에서는 depths가 0이므로, 사용하기에 적합하다고 판단 했다.
useKakaoLoader
는 말그대로 훅이기에 Hook으로 뺀 후에 loading과 error를 뱉어 내어 UI 최적화에도 사용하기로 생각하였다.
// src/hooks/client/map/kakao-map
import { useKakaoLoader as useKakaoLoaderOrigin } from 'react-kakao-maps-sdk';
const useKakaoLoader = () => {
const [loading, error] = useKakaoLoaderOrigin({
appkey: process.env.NEXT_PUBLIC_KAKAO_MAP_API_KEY as string,
libraries: ['clusterer', 'drawing', 'services'], // 사용하고싶은 다른 라이브러리 추가
});
return [loading, error];
};
export default useKakaoLoader;
쌈뽕하다.
loading, error 관련 UI 처리도 할 수 있고..... React에서 "cluster" , "drawing" 등 다른 카카오맵 라이브러리를 사용하고 싶으면 script에 넣어줘야하는데, 객체안에 라이브러리들을 명시 해주니 한눈에 보기에도 좋다.
이제 훅을 호출하자
import useKakaoLoader from '@/hooks/client/map/kakao-map/useKakaoLoader';
import { Map } from 'react-kakao-maps-sdk';
const KakaoMap = () => {
const [loading, error] = useKakaoLoader();
if (loading || error) return <Skeleton type="map" />;
return (
<Map center={...} style={...}>
</Map>
);
};
export default KakaoMap;
코드가 이쁘고, 쌈뽕하지 않은가.
결국은 위의 코드를 위해 이틀을 고생한 것이 후회가 안남았다.
위의 코드를 작성하기 위해 여러 부분을 알아보면서
Nextjs에서 Script를 알게 되었고, Script의 옵션관련하여서 알게 되었다는 점도 좋았고,
제일 중요한 부분은
외부 API를 사용하는데 자신감이 생겨져서 좋았다.
참고
Nextjs kakao 블로그
React스럽게 kakaoMap
ReactKakaoDocs
블로그1/React
블로그2/React