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

어플리케이션 이름은 자유롭게, maps 중에서 web dynamic map 체크
어떤 웹에서 이 API를 사용할 지 기입해야 하는데, 우리는 로컬 서버에서 사용할 예정이므로
web 서비스 URL에 http://localhost:3000이라고 입력한 뒤 추가 클릭
다 했으면 등록
이러면 플랫폼에서 클라이언트 아이디랑 서버..??를 제공해준다.
사이트 상단바-튜토리얼에 API 활용 가이드/샘플코드가 있으니 잘 활용하면 좋다고 한다.....
mern_client/public/index.html 파일을 확인해보면, 웹의 head에 meta나 title을 넣을 수 있는 고런 내용이 있는 걸 확인해볼 수 있다.
여기에 위의 튜토리얼에서 샘플코드를 복붙해옴
<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 객체를 전역 상태로 관리해야 하는 이유 -> map 객체를 그냥 네이버 지도 띄우는 데에만 사용하는게 아니라, 마커 등의 컴포넌트를 생성해서 사용할 때 map이 옵션으로 활용되기 때문에....
이런 객체를 전역 상태로 관리하면 좀 더 쉽게 해당 객체를 가져오고 관리할 수 있음
상태 관리 라이브러리
local / global / server와 같은 state를 동적으로 관리하기 위해 사용하는 라이브러리.
| 방식 | 대표 라이브러리 | 특징 |
|---|---|---|
| Flux | Redux, Zustand | - 중앙 집중식. 하나의 스토어를 이용해서 전역 상태 관리 - 내부적으로 context api 사용 X(Provider 필요X) - 렌더링 최적화를 위해 수동으로 selector를 통해 필요한 상태만 불러와야함 |
| Proxy | Mobx, Valtio | 잘 안쓰는듯ㅎ |
| Atomic | Jotai, 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도 잘 발급받았고...그런데 회사 컴퓨터 메모리 용량이 너무 작아서 이 작은 프로젝트만으로도 메모리가 터진다ㅎㅎ;;
대책이 필요할듯....