https://velog.io/@nyanji00/react-카카오맵-API-이용해-지도-띄우기-typescript-Next.js
위 글에 포스팅했던 방법은 React에서 카카오맵을 이용하는 방법으로 가장 잘 알려져 있지만,
Next.js에서 위 방법을 이용할 경우 external synchronous scripts are forbidden
에러가 발생해 사용할 수 없다.
Next.js에서 카카오맵을 사용하기 위해 돌고돌아 삽질했던 기록-
import styled from "@emotion/styled";
import { useEffect } from "react";
interface MapProps {
latitude: number;
longitude: number;
}
function Map({ latitude, longitude }: MapProps) {
return (
<MapContainer id="map" />
);
}
const MapContainer = styled.div`
aspect-ratio: 320 / 220;
`;
export default Map;
id
를 map
으로 지정한 div
를 생성한다.
이때 고정 px이 아닌 일정한 비율을 원한다면 padding-top
또는 aspect-ratio
를 사용한다.
앱키가 노출되면 좋지 않으므로 루트에 .env.local 을 생성해 App Key를 등록한다.
NEXT_PUBLIC_KAKAOMAP_APPKEY=발급받은 Javascript AppKey
브라우저에서 환경변수를 사용하기 위해서는 앞에 NEXT_PUBLIC_
을 꼭 붙여주어야 한다.
하지만 카카오맵 AppKey를 발급하는 과정에서 사용할 플랫폼을 등록할 수 있기 때문에 너무 보안에 신경쓰지는 않아도 된다.
출시 이후에 플랫폼에서 localhost:3000을 제거하기만 하면 된다.
이렇게 등록한 환경변수는 코드에서 process.env.{환경변수명}
으로 사용가능하다.
declare global {
interface Window {
kakao: any;
}
}
window.kakao
를 사용하기 위해 global interface를 선언한다.
해당 코드의 위치는 상관없다. Map 컴포넌트 파일 안에 작성해도 되고, 글로벌이므로 _app.tsx 에 작성하는 것도 좋다.
import styled from "@emotion/styled";
import { useEffect } from "react";
interface MapProps {
latitude: number;
longitude: number;
}
function Map({ latitude, longitude }: MapProps) {
useEffect(() => {
const mapScript = document.createElement("script");
mapScript.async = true;
mapScript.src = `//dapi.kakao.com/v2/maps/sdk.js?appkey=${process.env.NEXT_PUBLIC_KAKAOMAP_APPKEY}&autoload=false`;
document.head.appendChild(mapScript);
const onLoadKakaoMap = () => {
window.kakao.maps.load(() => {
const container = document.getElementById("map");
const options = {
center: new window.kakao.maps.LatLng(latitude, longitude),
};
const map = new window.kakao.maps.Map(container, options);
const markerPosition = new window.kakao.maps.LatLng(latitude, longitude);
const marker = new window.kakao.maps.Marker({
position: markerPosition,
});
marker.setMap(map);
});
};
mapScript.addEventListener("load", onLoadKakaoMap);
return () => mapScript.removeEventListener("load", onLoadKakaoMap);
}, [latitude, longitude]);
return (
<MapContainer id="map" />
);
}
const MapContainer = styled.div`
aspect-ratio: 320 / 220;
`;
export default Map;
Map 컴포넌트를 생성할 때 document.head.appendChild
로 script를 등록하고,
eventListener 를 등록해 script가 load된 이후 onLoadKakaoMap
을 호출하는 방식이다.
위 코드는 마커를 등록하는 코드까지 포함되어 있어, 마커를 원하지 않으면 marker가 포함된 세 줄을 삭제하면 된다.
https://velog.io/@familyman80/NEXTJS-에서-KAKAO-맵
위 글을 많이 참고하였는데, 추가로 useEffect의 클린업 함수로 removeEventListener를 등록해주었다.
+) 코드를 작성할 때는 removeEventListener가 없으면 Map 컴포넌트가 있는 페이지를 여러번 방문할 때 스크립트를 불러오기 전 onLoadKakaoMap을 호출하는 문제가 발생했었는데, 글을 작성하면서 확인해보니 별다른 에러가 발생하지 않는다...
MapProps 에 longitude 오타 있습니당 :)