[NextJS 지도 개발 #1] next/script를 이용하여 네이버 지도 API를 이용하자

김유진·2023년 4월 23일
4

Nextjs

목록 보기
4/9
post-thumbnail

네이버 지도 api를 가져와서 NextJS를 이용하여 지도를 만들어 보자!
이번 포스팅에는 네이버 오픈소스인 네이버 지도 관련 API를 이용하여 화면에 정상적으로 지도를 띄우는 것 까지 진행해볼 예정이다.

1. Ncloud 접속, 인증키 발급

먼저 Ncloud에 가입을 해야 한다.
https://www.ncloud.com/

이후 애플리케이션 생성 페이지로 넘어가서 Naver Map에 대한 인증 키를 발급받고, 배포할 페이지의 링크를 설정해둔다.

2. 네이버 맵 관련 타입 설정

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

3. next/script를 이용하여 api 정보 불러오기

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을 정상적으로 로드해주는 것이다.

Next/script 코드 뜯어보기

현재 다른 페이지에 방문하게 되면 네이버 맵이 언마운트되므로 해당 객체가 없어져야 한다.

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}
 />

어떻게 해당 코드가 네이버 지도에 관련된 정보를 가져올 수 있었을까?

Next/script props

src

필수 props이며, 외부 스크립트 URL가져올 수 있는 경로이다. (inline script로 사용되면 src프로퍼티는 필요하지 않다.)

strategy

스크립트를 로드하는 방법이며, 총 네가지가 존재한다.

  • beforeInteractive : 해당 코드를 매우 빠르게 가져오는 것이며, 페이지 hydration이 일어나기 전에 해당 스크립트를 가져온다. 가장 빨리 코드를 가져오나 pages/__document와 같이 전역으로 사용할 코드가 아니라면 자주 사용하지 말자 페이지가 인터렉티브하기도 전에 HTML을 먼저 주입하여 사용하는 코드라고 볼 수 있겠다.
  • afterInteractive : 기본값이며, 페이지가 로드되자마자 코드를 가져올 수 있도록 한다.
  • lazyOnload : 조금 느리게 페이지를 로드해도 괜찮다면 사용하자. 모든 페이지가 패치되고야 스크립트 관련 코드가 불려와질 수 있다.

onLoad

onLoadbeforeInteractive와 함께 쓰일 수 없다. 처음에 함수가 불려지거나 콘텐츠가 로드되는것이 끝날 때 딱 한번 스크립트 파일을 로드한다.

onReady

해당 컴포넌트가 마운트될 때마다 스크립트가 동작한다. 네이버 API같은 경우에는 다른 페이지에 이동할때 언마운트되고, 다시 돌아올 때 해당 컴포넌트가 마운트되니 onReady props를 사용하는 것이 더욱 적합하겠다.

onError

스크립트가 에러를 마주쳤을 때 콘솔에 찍어줄 수 있겠다.

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에 불러올 수 있게 되었다!

이제 이 맵 위에서 다양한 코드를 작성해보자!

0개의 댓글