React + Typescript로 네이버지도 API를 적용해보자

nagosu·2023년 10월 7일
2

SpotlightSeoul 프로젝트에서 공연장소를 네이버지도 API를 이용해서 보여주면 좋겠다는 생각으로 이번에 처음으로 지도 API를 적용해보았다.

시작

https://www.ncloud.com/product/applicationService/maps

위 링크는 네이버 클라우드 플랫폼 Maps 서비스 페이지이다.

우선 이용 신청을 해야하기 때문에 링크를 들어가서 로그인을 한 후 이용 신청하기를 눌러준다.

그 뒤 Application 등록을 해야하는데 이름을 설정해주고 Service 선택 및 서비스 환경을 등록하는 페이지로 이동한다.

Application 이름을 설정해주고

나는 Web Dynamic Map을 선택하였다.

Web Dynamic Map은 월 10,000,000건 까지는 무료이기 때문에 규모가 큰 프로젝트가 아니라면 대부분 무료일 것이다.

그리고 우선은 로컬 환경에서 개발할 것이기 때문에 http://localhost를 서비스 URL로 추가해준다.

신청을 했으면 클라이언트 ID를 발급받았을 것이다.

클라이언트 ID는 인증 정보를 클릭한 뒤

여기서 확인할 수 있다.

옆에 있는 복사 버튼을 누른 뒤 클라이언트 ID를 복사해준다.

그 뒤 index.html 파일에 가서 이 코드를 추가해준다.

YOUR_CLIENT_ID에 복사한 클라이언트 ID를 붙여준다.

<script type="text/javascript" 
	src="https://openapi.map.naver.com/openapi/v3/maps.js?ncpClientId=YOUR_CLIENT_ID"></script>

이 코드를 추가해줘야하는데 head 태그와 body 태그에 추가하는 것에는 차이가 있다.

  • head 태그 내에 추가하기
    페이지의 HTML 구조가 완전히 로딩되기 전에 스크립트가 로딩된다.
    스크립트가 페이지의 렌더링을 차단할 수 있으므로, asyncdefer 속성을 사용하여 이를 해결할 수 있다.

  • body 태그 내에 추가하기
    일반적으로 </body> 태그 바로 앞에 스크립트를 추가하면, HTML 구조가 완전히 로딩된 후에 스크립트가 실행된다.
    이 방법은 페이지 렌더링을 차단하지 않는다.

네이버지도 API 같은 경우, 지도를 표시할 HTML 요소가 먼저 로딩되어야 하므로 <body> 태그 끝 부분에 스크립트를 추가하는 것이 일반적이다.

하지만 필요에 따라서는 <head> 에 추가하고 asyncdefer 을 사용할 수도 있다.

타입스크립트 적용

이제 세팅은 거의 다 끝났는데 타입스크립트 설정을 해줘야한다.

네이버지도는 타입스크립트 사용법에 대해 공식문서에 기술해두었다.

https://navermaps.github.io/maps.js.ncp/docs/tutorial-3-Using-TypeScript.html

우선 네이버지도 API 타입 정의 파일을 설치해야한다.

npm i -D @types/navermaps

or

yarn add @types/navermaps --dev

그 후 네이버지도 API 공식문서 예제를 보며 적용해볼 수 있다.

사용

지도와 마커

우선 나는 필요한 기능이 지도에 마커를 표시하고 확대와 축소가 되는 기능이었다.

https://navermaps.github.io/maps.js.ncp/docs/tutorial-1-marker-simple.example.html

우선 마커를 적용시켜보자.

위 링크에 있는 예제에 있는 대로 사용이 가능하다.

var map = new naver.maps.Map('map', {
    center: new naver.maps.LatLng(37.3595704, 127.105399),
    zoom: 15
});

var marker = new naver.maps.Marker({
    position: new naver.maps.LatLng(37.3595704, 127.105399),
    map: map
});

naver.maps.Map 를 이용해 map 인스턴스를 만들어준 뒤 centernaver.maps.Map.LatLng 를 이용해 좌표값(위도, 경도)을 설정해준다.

그리고 zoom은 확대의 정도인데 사용자의 편의에 맞춰 설정하면 된다.

값이 크면 더 확대가 많이 된다.

그리고 marker 인스턴스를 naver.maps.Marker 를 이용해 만들어준다.

position은 마커를 찍을 위치이기 때문에 위의 map 인스턴스에 적었던 좌표값을 똑같이 받아오면 된다.

그 후 map에는 위에서 선언해주었던 map 인스턴스를 가져와준다.

이렇게 되면 화면에 지도에 마커가 찍힌 채로 표시가 될 것이다.

마커 클릭 시 초기화

여기서 나는 추가하고 싶었던 기능이 있었는데 이동/확대/축소를 했을 때 마커를 클릭하면 처음 지도화면으로 다시 돌아가는 기능이다.

// Marker 클릭 시 지도 초기화
naver.maps.Event.addListener(marker, 'click', () => {
  map?.setCenter(new naver.maps.LatLng(latitude, longitude));
  map?.setZoom(15);
});

마커를 클릭하면 좌표값을 중앙에 두고 zoom의 정도도 초기화된다.

확대 / 축소

이제 확대랑 축소를 할 수 있는 컨트롤 옵션을 추가해야 한다.

https://navermaps.github.io/maps.js.ncp/docs/tutorial-2-control-options.example.html

위 링크에서 정확한 정보를 확인 가능하다.

var mapOptions = {
    zoomControl: true,
    zoomControlOptions: {
        style: naver.maps.ZoomControlStyle.SMALL,
        position: naver.maps.Position.TOP_RIGHT
    }
};

var map = new naver.maps.Map(document.getElementById('map'), mapOptions);

mapOptions 객체를 선언해줘야 한다.

zoomControl을 true로 해주고 zoomControlOptions로 style과 position을 설정해준다.

style에는 SMALLLARGE 옵션이 있다.

  • naver.maps.ZoomControlStyle.SMALL

  • naver.maps.ZoomControlStyle.LARGE

그리고 position 에서 지도 화면의 어디에 둘 건지 설정해준다.

naver.maps.Position.의 뒤에 위치를 적어준다.

마무리

이렇게 원하는 기능을 모두 넣었다.

import { useState, useEffect } from 'react';

let mapInstance: naver.maps.Map | null = null;

const loadScript = (src: string, callback: () => void) => {
  const script = document.createElement('script');
  script.type = 'text/javascript';
  script.src = src;
  script.onload = () => callback();
  document.head.appendChild(script);
};

function MapInformation({
  latitude,
  longitude,
}: {
  latitude: number;
  longitude: number;
}) {
  // 지도 로딩 상태
  const [isMapLoaded, setMapLoaded] = useState(false);

  const initMap = () => {
    // 추가 옵션 설정
    const mapOptions = {
      zoomControl: true,
      zoomControlOptions: {
        style: naver.maps.ZoomControlStyle.SMALL,
        position: naver.maps.Position.TOP_RIGHT,
      },
      center: new naver.maps.LatLng(latitude, longitude),
      zoom: 16,
    };

    // 지도 초기화 확인
    if (document.getElementById('map')) {
      mapInstance = new naver.maps.Map('map', mapOptions);
    }

    // Marker 생성
    const marker = new naver.maps.Marker({
      position: new naver.maps.LatLng(latitude, longitude),
      map: mapInstance,
    });

    // Marker 클릭 시 지도 초기화
    naver.maps.Event.addListener(marker, 'click', () => {
      mapInstance?.setCenter(new naver.maps.LatLng(latitude, longitude));
      mapInstance?.setZoom(16);
    });

    // 지도 로드 완료
    setMapLoaded(true);
  };

  useEffect(() => {
    // 스크립트 로딩 확인
    if (typeof naver === 'undefined') {
      loadScript(
        'https://openapi.map.naver.com/openapi/v3/maps.js?ncpClientId=YOUR_CLIENT_ID',
        initMap,
      );
    } else {
      initMap();
    }
  }, [latitude, longitude]);

  return (
    <>
      {/* 위치 정보(지도) */}
      <div className="mb-8 mt-40 flex w-screen flex-col items-center">
        <span className="sm:text-md font-Pretendard text-sm font-bold text-[#06439F] md:text-lg lg:text-xl xl:text-2xl 2xl:text-3xl">
          위치 안내
        </span>
        {isMapLoaded && (
          <div id="map" className="mt-4 h-[500px] w-11/12 sm:mt-6 lg:mt-8" />
        )}
      </div>
    </>
  );
}

export default MapInformation;

처음 지도 API를 사용해봤지만 참고할 만한 자료와 공식문서가 굉장히 잘 되어있어서 수월했다.

이렇게 지도를 추가해주면 직관성도 좋아지고 디자인적으로도 이뻐지는 것 같다.

다음엔 더 다양한 기능과 카카오지도, Google 지도도 활용해보도록 하자.

profile
프론트엔드 개발자..일걸요?

2개의 댓글

comment-user-thumbnail
2024년 8월 7일

client id 노출되었어요! 다른 사람이 사용할 수 있으니 가리는 게 좋을 거 같아요 : )

1개의 답글

관련 채용 정보