지도 생성하기

조다영·2026년 5월 18일

강의

목록 보기
5/6

지도 API 불러오기

네이버 지도 api를 가져올 예정이다.

  1. 네이버 개발자 센터 접속
  2. 이 중에서 네이버 클라우드 플랫폼 클릭
  3. AI NAVER API 클릭
  4. 어플리케이션 등록 클릭

어플리케이션 이름은 자유롭게, maps 중에서 web dynamic map 체크
어떤 웹에서 이 API를 사용할 지 기입해야 하는데, 우리는 로컬 서버에서 사용할 예정이므로
web 서비스 URL에 http://localhost:3000이라고 입력한 뒤 추가 클릭
다 했으면 등록

이러면 플랫폼에서 클라이언트 아이디랑 서버..??를 제공해준다.

사이트 상단바-튜토리얼에 API 활용 가이드/샘플코드가 있으니 잘 활용하면 좋다고 한다.....


mern_client/public/index.html 파일을 확인해보면, 웹의 head에 meta나 title을 넣을 수 있는 고런 내용이 있는 걸 확인해볼 수 있다.

여기에 위의 튜토리얼에서 샘플코드를 복붙해옴

  • 강의가 3년전꺼라 좀 바뀐 부분이 있어 수정했다...
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
    <!--
      Notice the use of %PUBLIC_URL% in the tags above.
      It will be replaced with the URL of the `public` folder during the build.
      Only files inside the `public` folder can be referenced from the HTML.

      Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
      work correctly both with client-side routing and a non-root public URL.
      Learn how to configure a non-root public URL by running `npm run build`.
    -->
    <script
      type="text/javascript"
      src="https://oapi.map.naver.com/openapi/v3/maps.js?ncpKeyId=YOUR_CLIENT_ID"
      ></script>
    <title>React App</title>
  </head>

여기 YOUR_CLIENT_ID 부분에 아까 발급받은 client id를 넣어주면 됨


홈페이지/업로드 페이지에서 사용할 Map 폴더를 mern_boiler/mern_client/src/common 디렉토리 아래에 하나 생성해준다.
그 안에 index.tsx 파일 하나 만들고, rfce를 쳐서 기본 컴포넌트를 생성함.

네이버지도를 띄우는 방법은 아주 간단하다.(라고 한다.)

id가 map인 div 태그 하나 생성 -> 스타일 지정 -> 스크립트 파일에서 options, map 이라는 객체를 생성해주면 끝임.
이게 튜토리얼에 코드가 있는데, html 버전으로 적혀있으니 이 강의에서는 react 버전으로 바꿔서 진행하였다.

컴포넌트가 렌더링된 후 사용해야 하는 함수이기 때문에 useEffect를 사용함

useEffect

  • React 컴포넌트가 렌더링될 때마다 특정 작업을 실행할 수 있도록 하는 Hook.
  • 컴포넌트가 마운트/언마운트/업데이트됐을 때 특정 작업을 처리할 수 있음.
의존성 배열 형태실행 시점주요 용도
useEffect(() => { ... })(배열 없음)컴포넌트가 렌더링될 때마다 매번거의 사용되지 않음 (디버깅용)
useEffect(() => { ... }, [])(빈 배열)컴포넌트 마운트 시 딱 1번초기 API 호출, 외부 라이브러리 연동
useEffect(() => { ... }, [dep])(값이 있는 배열)마운트 시 + dep이 변경될 때마다특정 조건에 따른 데이터 업데이트, 검색 등
import React from 'react';

interface MapProps {
    width: string;
    height: string;
}

function Map({ width, height }: MapProps) {

    useEffect(() => {
        const mapOptions = {
            center: new NavigationPrecommitController.amps.LatLng(37.3595704, 127.105399),
            zoom: 10,
        };
    
        const map = new naver.maps.Map("map", mapOptions);

    }, []);

  return <div id="map" style={{width, height }}></div>; 
}

export default Map;

해당 맵 컴포넌트의 높이와 넓이를 설정해주는 컨테이너도 만들어줘야 한다고 한다...
mern_client/src/components/MapContainer.tsx 파일을 하나 생성해준다....

import React from 'react'
import Map from "./common/Map"

function MapContainer() {
    return <Map width="100%" height="100%" />;
}

export default MapContainer;

그리고 이것을 홈페이지에서 사용하기 위해 mern_client/src/Pages/Home/index.tsx 파일에 MapContainer 태그를 추가해준다.

import MapContainer from "../../components/MapContainer";
import Navigation from "../components/Navigation"

function Home() {
    return <>
        <Navigation />
        <MapContainer />
        </>;
}

export default Home;

감싸고 있던 <div> 태그를 빈 태그로 바꿔줬는데 왜 그랬는지는 잘 모르겟음.............

이 과정을 끝내고 로컬 서버로 웹사이트에 접속해보면 지도가 띄워진 것을 확인할 수 있다.

Map 객체 관리(Jotai)

map 객체를 전역 상태로 관리해야 하는 이유 -> map 객체를 그냥 네이버 지도 띄우는 데에만 사용하는게 아니라, 마커 등의 컴포넌트를 생성해서 사용할 때 map이 옵션으로 활용되기 때문에....
이런 객체를 전역 상태로 관리하면 좀 더 쉽게 해당 객체를 가져오고 관리할 수 있음

상태 관리 라이브러리
local / global / server와 같은 state를 동적으로 관리하기 위해 사용하는 라이브러리.

방식대표 라이브러리특징
FluxRedux, Zustand- 중앙 집중식. 하나의 스토어를 이용해서 전역 상태 관리
- 내부적으로 context api 사용 X(Provider 필요X)
- 렌더링 최적화를 위해 수동으로 selector를 통해 필요한 상태만 불러와야함
ProxyMobx, Valtio잘 안쓰는듯ㅎ
AtomicJotai, Recoil- 별도의 수동 렌더링 최적화 필요 X
- context api 사용
- 여러 Provider 컴포넌트를 통해 각 서브트리 하위에서 별도의 상태를 관리할 수 있음

Jotai는 Redux보다 더 가볍다는 점에서 장점이 있는 듯 하다...

atoms를 생성하고.... 그 옆에 초기화하는 값을 써주고....이걸 하나의 atom이라는 상수에 담고..UseAtom이라는 hook을 가져와서 hook을 바탕으로 state를 설정하는 SetState 함수를 불러오고...
어쩌구저쩌구하는걸 공식문서에서 확인해볼 수 있는데
지금 이해는 잘 안가니까 일단 패스.....
나중에 정 궁금하면 jotai 공식문서에서 열심히 탐색해보자...



하여튼 현재 프로젝트에 이 라이브러리를 적용하기 위해 mern_client/src에 atoms 라는 이름의 폴더를 하나 만들어주고, 그 안에 map.ts 파일을 만든다.

import { atom } from "jotai"

export const mapAtom = atom<naver.maps.Map | null>(null); 
// 현재는 null이지만 나중에 state로 naver.maps.Map 타입이 들어갈 예정이므로 <>로 타입을 지정해줌

그리고 src/MapContainer.tsx 파일로 다시 가서 useSetAtom을 이용하여 상태를 지정해준다.
map 컴포넌트에서 사용할 수 있는 함수도 생성해준다.

import { useSetAtom } from "jotai";
import React from 'react';
import { mapAtom } from "../atoms/map";
import Map from "./common/Map";

function MapContainer() {
    const setMap = useSetAtom(mapAtom);

    const initMap = (map:naver.maps.Map) => {
        setMap(map);
    };

    return <Map width="100%" height="100%" initMap={initMap} />;
}

export default MapContainer;

그리고 다시 mern_client/src/components/common/Map/index.tsx 파일로 돌아가서 map을 props 형식으로 정의할 수 있게 코드를 추가해줘야 함....

import React, { useEffect } from 'react';

interface MapProps {
    width: string;
    height: string;
    initMap?: (map: naver.maps.Map) => void;
}

function Map({ width, height, initMap }: MapProps) {

    useEffect(() => {
        const mapOptions = {
            center: new NavigationPrecommitController.amps.LatLng(37.3595704, 127.105399),
            zoom: 10,
        };
    
        const map = new naver.maps.Map("map", mapOptions);
        if (initMap) {
            initMap(map);
        }

    }, []);

  return <div id="map" style={{width, height }}></div>; 
}

export default Map;

이렇게 하면 map 객체를 전역에서 사용할 수 있게 설정된 것이다...............

맵 클릭 이벤트 추가

네이버 공식 문제의 예제를 보고 진행한다.
네이버 지도 API 이벤트 기본예제

이 부분의 코드를 사용하면 되는데, addListener가 뭔지 모르니 검색해본다.
공식문서를 보면서 하나하나 검색해보며 코딩하면 좋다는 걸 알려주려는 듯하다.

검색해보면 naver.maps.KVO라는 클래스에 대한 문서가 나온다.
이 문서에 따르면, addListener(eventName, listener)는 현재 객체의 이벤트 알림을 받아 핸들러를 호출하는 리스너를 등록하는 메서드라고 한다..
하 근데 이것도 강의 찍을때랑 달라졌네.........


Event의 addListener 메서드를 상속받는다고 한다.... Event에 있는 메서드는 target 인자가 존재하는데 KVO에 있는 이거는 target 인자가 없다...
아무튼 그렇다.

아무튼 그래서 .../src/components/common/MapContainer.tsx 파일의 MapContainer() 함수에 아래와 같이 addListener 메서드를 추가해주었다.

function MapContainer() {
    const setMap = useSetAtom(mapAtom);

    const initMap = (map:naver.maps.Map) => {
        setMap(map);
        NavigationPrecommitController.maps.Event.addListener(map, "click", () => {
            console.log("맵 클릭!!");
        });
    };

    return <Map width="100%" height="100%" initMap={initMap} />;
}

이렇게 하면 지도를 클릭했을 때 리스너가 실행된다.
아직은 이 뒤에 어떤 동작을 연결하지 않아서 실제로 웹사이트에서 지도를 클릭했을 때 콘솔 로그에 "맵 클릭!!"이라고만 뜬다.


일단 웬만한 오류도 해결하고...지도 api도 잘 발급받았고...그런데 회사 컴퓨터 메모리 용량이 너무 작아서 이 작은 프로젝트만으로도 메모리가 터진다ㅎㅎ;;

대책이 필요할듯....

0개의 댓글