[Next.js] 카카오맵 API 이용해 지도 띄우기 (Typescript)

냔지·2021년 12월 10일
8
post-thumbnail

https://velog.io/@nyanji00/react-카카오맵-API-이용해-지도-띄우기-typescript-Next.js
위 글에 포스팅했던 방법은 React에서 카카오맵을 이용하는 방법으로 가장 잘 알려져 있지만,
Next.js에서 위 방법을 이용할 경우 external synchronous scripts are forbidden 에러가 발생해 사용할 수 없다.

Next.js에서 카카오맵을 사용하기 위해 돌고돌아 삽질했던 기록-

1. 지도를 담을 div 생성

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;

idmap 으로 지정한 div 를 생성한다.
이때 고정 px이 아닌 일정한 비율을 원한다면 padding-top 또는 aspect-ratio 를 사용한다.

2. .env.local에 App Key 등록

앱키가 노출되면 좋지 않으므로 루트에 .env.local 을 생성해 App Key를 등록한다.

NEXT_PUBLIC_KAKAOMAP_APPKEY=발급받은 Javascript AppKey

브라우저에서 환경변수를 사용하기 위해서는 앞에 NEXT_PUBLIC_ 을 꼭 붙여주어야 한다.
하지만 카카오맵 AppKey를 발급하는 과정에서 사용할 플랫폼을 등록할 수 있기 때문에 너무 보안에 신경쓰지는 않아도 된다.
출시 이후에 플랫폼에서 localhost:3000을 제거하기만 하면 된다.

이렇게 등록한 환경변수는 코드에서 process.env.{환경변수명} 으로 사용가능하다.

3. global interface 선언

declare global {
  interface Window {
    kakao: any;
  }
}

window.kakao 를 사용하기 위해 global interface를 선언한다.
해당 코드의 위치는 상관없다. Map 컴포넌트 파일 안에 작성해도 되고, 글로벌이므로 _app.tsx 에 작성하는 것도 좋다.

4. useEffect로 스크립트 불러오기

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을 호출하는 문제가 발생했었는데, 글을 작성하면서 확인해보니 별다른 에러가 발생하지 않는다...

3개의 댓글

comment-user-thumbnail
2022년 5월 25일

MapProps 에 longitude 오타 있습니당 :)

1개의 답글
comment-user-thumbnail
2022년 5월 25일

덕분에 빠르게 해결할 수 있었습니다 ㅎㅎ
상세하게 써주셔서 감사합니다 !

답글 달기