Next.js, Typescript 환경에서 Kakao Maps API 사용하기
카카오 MAP API를 사용하기 위해선 다음과 같이 코드를 작성해야 합니다.
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>Kakao 지도 시작하기</title>
</head>
<body>
<div id="map" style="width:500px;height:400px;"></div>
<script
type="text/javascript"
src="//dapi.kakao.com/v2/maps/sdk.js?appkey=발급받은 APP KEY를 넣으시면 됩니다."
></script>
<script>
var container = document.getElementById('map');
var options = {
center: new kakao.maps.LatLng(33.450701, 126.570667),
level: 3,
};
var map = new kakao.maps.Map(container, options);
</script>
</body>
</html>
위 코드를 jsx 문법에 맞게 작성해야 하는데요. 다양한 방법들이 존재하지만 <div>
에 id 를 지정하는 대신에 useRef 를 이용했고, next의 <Script>
를 사용하여 api를 호출했습니다.
'use client';
import { useEffect, useRef } from 'react';
import styles from './map.module.scss';
import Script from 'next/script';
export default function Map() {
const mapRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const options = {
//지도를 생성할 때 필요한 기본 옵션
center: new kakao.maps.LatLng(33.450701, 126.570667), //지도의 중심좌표.
level: 3, //지도의 레벨(확대, 축소 정도)
};
const map = new kakao.maps.Map(mapRef, options); //지도 생성 및 객체 리턴
}, []);
return (
<>
<Script
src={`//dapi.kakao.com/v2/maps/sdk.js?appkey=${process.env.NEXT_PUBLIC_MAP_KEY}`}
/>
<div ref={mapRef} className={styles.map}></div>;
</>
);
}
이렇게 작성하면 전역 객체에서 kakao를 찾을 수 없어 참조 에러가 발생합니다. 전역 객체에 kakao 타입을 추가한 후에 다음과 같이 코드를 수정했습니다.
declare global {
interface Window {
kakao: any;
}
}
기존 kakao.maps
에서 window.kakao.maps
로 변경
useEffect(() => {
const options = {
//지도를 생성할 때 필요한 기본 옵션
center: new window.kakao.maps.LatLng(33.450701, 126.570667), //지도의 중심좌표.
level: 3, //지도의 레벨(확대, 축소 정도)
};
const map = new window.kakao.maps.Map(mapRef, options); //지도 생성 및 객체 리턴
}, []);
지도가 생성될 것 같지만 오류가 발생합니다.
이 오류는 스크립트보다 useEffect 코드가 먼저 실행되어 maps 프로퍼티를 찾을 수 없기 때문에 발생하는 오류입니다. 카카오 API는 스크립트를 동적으로 불러오기 위한 정적 메서드를 제공하고 있기에 코드를 수정하고 api key 뒤에 autoload=false
를 추가했습니다.
<script
type="text/javascript"
src="http://dapi.kakao.com/v2/maps/sdk.js?autoload=false"
></script>
<script type="text/javascript">
kakao.maps.load(function () {
// v3가 모두 로드된 후, 이 콜백 함수가 실행됩니다.
var map = new kakao.maps.Map(node, options);
});
</script>
script 컴포넌트에 strategy
옵션에 beforeInteractive
를 주어 hydration 이전에 실행시키게 만듭니다. beforeInteractive
를 주게 되면 해당 스크립트를 루트 레이아웃으로 빼야 합니다. beforeInteractive
를 설정한 스크립트는 루트 레이아웃 내에서만 작동한다고 공식 문서에서 설명하고 있습니다.
// layout.tsx
// ...
import Script from 'next/script';
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="ko">
<body className={inter.className}>
<Script
strategy="beforeInteractive"
src={`//dapi.kakao.com/v2/maps/sdk.js?appkey=${process.env.NEXT_PUBLIC_MAP_KEY}&autoload=false`}
/>
{children}
</body>
</html>
);
}
//Map.tsx
'use client';
import { useEffect, useRef } from 'react';
import styles from './map.module.scss';
export default function Map() {
const mapRef = useRef<HTMLDivElement>(null);
useEffect(() => {
window.kakao.maps.load(() => {
const options = {
//지도를 생성할 때 필요한 기본 옵션
center: new window.kakao.maps.LatLng(33.450701, 126.570667), //지도의 중심좌표.
level: 3, //지도의 레벨(확대, 축소 정도)
};
const map = new window.kakao.maps.Map(mapRef.current, options); //지도 생성 및 객체 리턴
});
}, []);
return <div ref={mapRef} className={styles.map}></div>;
}
이제 카카오 맵이 정상적으로 생성됩니다. 이 방법 밖에도 document.createElement
를 이용해 script를 생성하는 방법과 <Script>
컴포넌트에 onLoad
prop을 이용해 하는 방법, 라이브러리를 이용한 방법 등 다양한 방법들이 있습니다.