next.js 프로젝트에서 Naver map을 사용할 일이 생겼다.
아직까지도 공식 가이드에는 바닐라 자바스크립트 내용만 있다.
따라서 리액트스럽게(?) 사용하기 위해서는 별도의 라이브러리를 이용하는 것이 좋다고 생각하는데, 아래 라이브러리가 문서 정리도 잘 되어있어 사용하기 편하다.
https://zeakd.github.io/react-naver-maps/
일단은 라이브러리 적용 전 API키를 발급받아야 한다.
계정이 없다면 새롭게 가입해주고, 아래 홈페이지에서 이용 신청 버튼을 클릭하면 어플리케이션을 등록할 수 있다.
https://www.ncloud.com/product/applicationService/maps

어플리케이션 이름은 원하는 걸로 입력하고, Maps > Web Dynamic Map을 선택해준다.
만약에 Android나 iOS 어플에서 사용할 거라면 Mobile Dynamic Map을 선택하면 되고, 더 자세한 내용을 알고 싶다면 각각의 서비스 설명/요금안내 버튼을 클릭해 확인해보자.
나는 아직 로컬 개발 단계에서만 사용할 것이기 때문에 http://localhost:3000 만 입력해주었고, 별도의 도메인이 있다면 추가해주자.

등록이 완료되었다면 콘솔에 들어와 어플리케이션의 인증 정보 버튼을 클릭하여 인증 정보를 확인하면 된다.

여기서 우리가 필요로 하는 키는 Client ID이다.
나중에 다시 들어와서 복사할 수도 있으니 창을 닫았다고 걱정하지는 말자.

이제 어플리케이션으로 돌아와 react-naver-maps 라이브러리를 추가해주자.
yarn add react-naver-maps
next.js의 루트 컴포넌트인 _app.tsx에 아래와 같이 Provider를 설정해주었다.
clientId는 노출되면 엄청난 요금 부과가 될 수 있으니 .env를 통해 알아서 잘 숨겨주자.
import Layout from "@/components/layout";
import "@/styles/globals.css";
import type {AppProps} from "next/app";
import {NavermapsProvider} from 'react-naver-maps';
export default function App({Component, pageProps}: AppProps) {
return (
<NavermapsProvider ncpClientId={MY_CLIENT_ID}>
<Layout>
<Component {...pageProps} />
</Layout>
</NavermapsProvider>
);
}
그리고 지도 컴포넌트를 생성해서 그려주면..!
import {NaverMap, useNavermaps, Container, Marker} from "react-naver-maps";
const Map = () => {
const navermaps = useNavermaps();
return <Container className="w-[300px] h-[450px] sm:w-[500px] md:w-[700px] lg:w-[900px]">
<NaverMap
defaultCenter={new navermaps.LatLng(37.3595704, 127.105399)}
defaultZoom={15}
>
<Marker defaultPosition={new navermaps.LatLng(37.5563, 127.9723)}
/>
</NaverMap>
</Container>
}
export default Map;
이런 에러를 만나게 된다.

ChatGPT에 따르면.. next.js와 같은 SSR 환경에서는 window 객체가 존재하지 않고, 라이브러리는 window 객체를 필요로 하기 때문에 발생하는 에러라고 한다.
따라서 지도 컴포넌트를 사용하는 곳에서 client 체크를 해줘야 한다.
const Page = () => {
const [isClient, setIsClient] = useState(false);
useEffect(() => {
setIsClient(true);
}, []);
return <div>
{isClient && <Map />}
</div>
}
https://zeakd.github.io/react-naver-maps/examples/map-tutorial-5-moves/
다음으로 나는 버튼을 누를때마다 지도의 center가 바뀌도록 만들어야 했기 때문에 아래 코드를 추가해줬다.
// page.tsx
const Page = () => {
const [isClient, setIsClient] = useState(false);
const [position, setPosition] = useState<Position | null>(null);
const getRandomPosition = () => {
...
return randomPosition;
}
useEffect(() => {
setIsClient(true);
}, []);
return <div>
<button onClick={getRandomPosition}>랜덤!</>
{isClient && <Map position={position}/>}
</div>
}
// map.tsx
const Map = ({position}: { position: Position | null }) => {
const navermaps = useNavermaps();
const [map, setMap] = useState<naver.maps.Map | null>(null);
useEffect(() => {
if (position && map) {
map.setCenter(new navermaps.LatLng(position.lat, position.lng))
}
}, [map, position]);
if (!position) return null;
return <Container className="w-[300px] h-[450px] sm:w-[500px] md:w-[700px] lg:w-[900px]">
<NaverMap
defaultCenter={new navermaps.LatLng(station.lat, station.lng)}
defaultZoom={15}
ref={setMap}
>
<Marker defaultPosition={new navermaps.LatLng(37.5563, 127.9723)}
/>
</NaverMap>
</Container>
}
위 코드에서 useState의 타입으로 naver.maps.Map을 설정해줬는데, react-naver-maps 라이브러리에서는 찾지 못하였기에 구글링을 통해 공식 가이드에서 타입을 찾아냈다.
yarn add -D @types/navermaps
완성!
