네이버 지도 api를 가져와서 NextJS를 이용하여 지도를 만들어 보자!
이번 포스팅에는 네이버 오픈소스인 네이버 지도 관련 API를 이용하여 화면에 정상적으로 지도를 띄우는 것 까지 진행해볼 예정이다.
먼저 Ncloud에 가입을 해야 한다.
https://www.ncloud.com/
이후 애플리케이션 생성 페이지로 넘어가서 Naver Map에 대한 인증 키를 발급받고, 배포할 페이지의 링크를 설정해둔다.
yarn add -D @types/navermaps
해당 명령어를 통하여 네이버 맵에 대한 타입 관련 패키지 라이브러리를 설치한다.
그리고 네이버 맵에 대한 타입을 정의해야 하므로 아래와 같이 타입을 설정한다.
export type NaverMap = naver.maps.Map;
그리고 위도와 경도를 지정할 수 있는 타입도 하나 생성해준다.
type Lat = number;
type Lng = number;
export type Coordinates = [Lat, Lng];
초기 설정을 해주기 위하여 Map에 관련된 Custom Hook을 생성하고 초기 생성 위치 및 줌에 관련된 초기값도 지정할 수 있도록 한다.
import { useCallback } from 'react';
import useSWR, { mutate } from 'swr';
import type { Coordinates } from '../types/store';
import type { NaverMap } from '../types/map';
export const INITIAL_CENTER: Coordinates = [37.5262411, 126.99289439];
export const INITIAL_ZOOM = 10;
export const MAP_KEY = '/map';
const useMap = () => {
const { data: map } = useSWR(MAP_KEY);
const initializeMap = useCallback((map: NaverMap) => {
mutate(MAP_KEY, map);
}, []);
const resetMapOptions = useCallback(() => {
map.morph(new naver.maps.LatLng(...INITIAL_CENTER), INITIAL_ZOOM);
}, [map]);
const getMapOptions = useCallback(() => {
const mapCenter = map.getCenter();
const center: Coordinates = [mapCenter.lat(), mapCenter.lng()];
const zoom = map.getZoom();
return { center, zoom };
}, [map]);
return {
initializeMap,
resetMapOptions,
getMapOptions,
};
};
export default useMap;
더욱 자세한 내용은 아래 링크에서 확인해 볼 수 있다.
https://navermaps.github.io/maps.js.ncp/docs/naver.maps.Map.html#morph__anchor
import Script from "next/script";
import { useEffect, useRef } from "react";
import { Coordinates } from "@/types/store";
import { NaverMap } from "@/types/map";
import { INITIAL_CENTER, INITIAL_ZOOM } from "@/hooks/useMap";
type Props = {
mapId?: string;
initialCenter?: Coordinates;
initialZoom?: number;
onLoad?: (map: NaverMap) => void;
};
const Map = ({
mapId = 'map',
initialCenter = INITIAL_CENTER,
initialZoom = INITIAL_ZOOM,
onLoad,
}: Props) => {
const mapRef = useRef<NaverMap | null>(null);
const initializeMap = () => {
const mapOptions = {
center: new window.naver.maps.LatLng(...initialCenter),
zoom: initialZoom,
minZoom: 9,
scaleControl: false,
mapDataControl: false,
logoControlOptions: {
position: naver.maps.Position.BOTTOM_LEFT,
},
};
//새로운 네이버 맵 인스턴스 생성
const map = new window.naver.maps.Map(mapId, mapOptions);
mapRef.current = map;
if (onLoad) {
onLoad(map);
}
};
//맵이 unmount되었을 때 맵 인스턴스 destory하기
useEffect(() => {
return () => {
mapRef.current?.destroy();
};
}, []);
return (
<>
<Script
strategy="afterInteractive"
type = "text/javascript"
src = {`https://openapi.map.naver.com/openapi/v3/maps.js?ncpClientId=${process.env.NEXT_PUBLIC_NCP_CLIENT_ID}`}
onReady = {initializeMap}
/>
<div id = {mapId} style = {{width : '100%', height: '100%'}}/>
</>
)
}
여기서 next/script을 이용하여 네이버 지도의 정보를 불러오는 것이다.
네이버 지도에 대한 정보를 가지는 DOM은 {mapId}
를 통하여 식별하며 div의 id를 통하여 지도가 들어갈 공간을 구별하는 것이다.
다음으로는 지도를 초기화하기 위해서 Map에 대한 옵션을 지정할 수 있는데 이는 mapOptions
로 지정할 수 있다. 객체 리터럴로 생성해야 하며, 프로퍼티에 대한 더욱 자세한 정보는 해당 링크로 자세히 확인할 수 있다.
const map = new window.naver.maps.Map(mapId, mapOptions);
mapRef.current = map;
해당 코드는 map 인스턴스를 생성하는 코드인데, Map 클래스는 지도를 표현하는 클래스이다. 지도가 될 DOM 요소로 설정할 요소의 id
를 직접 전달해야 한다. 초기값을 전달해도 되지만 전달하지 않아도 된다.
if (onLoad) {
onLoad(map);
}
해당 부분은 Map의 props로 onLoad
가 주어졌을 때 Map을 정상적으로 로드해주는 것이다.
현재 다른 페이지에 방문하게 되면 네이버 맵이 언마운트되므로 해당 객체가 없어져야 한다.
useEffect(() => {
return () => {
mapRef.current?.destroy();
};
}, []);
그래서 useEffect를 사용하여서 mapRef가 언마운트될 때 완전히 destroy되도록 코드를 작성한다.
<Script
strategy="afterInteractive"
type = "text/javascript"
src = {`https://openapi.map.naver.com/openapi/v3/maps.js?ncpClientId=${process.env.NEXT_PUBLIC_NCP_CLIENT_ID}`}
onReady = {initializeMap}
/>
어떻게 해당 코드가 네이버 지도에 관련된 정보를 가져올 수 있었을까?
필수 props이며, 외부 스크립트 URL가져올 수 있는 경로이다. (inline script로 사용되면 src프로퍼티는 필요하지 않다.)
스크립트를 로드하는 방법이며, 총 네가지가 존재한다.
hydration
이 일어나기 전에 해당 스크립트를 가져온다. 가장 빨리 코드를 가져오나 pages/__document
와 같이 전역으로 사용할 코드가 아니라면 자주 사용하지 말자 페이지가 인터렉티브하기도 전에 HTML을 먼저 주입하여 사용하는 코드라고 볼 수 있겠다.onLoad
는 beforeInteractive
와 함께 쓰일 수 없다. 처음에 함수가 불려지거나 콘텐츠가 로드되는것이 끝날 때 딱 한번 스크립트 파일을 로드한다.
해당 컴포넌트가 마운트될 때마다 스크립트가 동작한다. 네이버 API같은 경우에는 다른 페이지에 이동할때 언마운트되고, 다시 돌아올 때 해당 컴포넌트가 마운트되니 onReady
props를 사용하는 것이 더욱 적합하겠다.
스크립트가 에러를 마주쳤을 때 콘솔에 찍어줄 수 있겠다.
import Script from 'next/script'
export default function Page() {
return (
<>
<Script
src="https://example.com/script.js"
onError={(e) => {
console.error('Script failed to load', e)
}}
/>
</>
)
}
짜잔. 이렇게 해서 네이버 API를 Nextjs에 불러올 수 있게 되었다!
이제 이 맵 위에서 다양한 코드를 작성해보자!