사이트에 들어가서 앱 키를 발급받아야 API를 사용할 수 있다.
$ yarn create react-app cacaomap --template typescript
$ yarn start
<실제 지도를 그리는 Javascript API 불러오기>
index.html에 있는 head 와 body 가 모두 로딩된 다음,
body의 <div id="root"></div>
안에 작성한 리액트 프로젝트가 들어가게 된다.
index.html에다가 script 태그를 넣으면 리액트 프로젝트가 로딩되기 전에 카카오지도가 install 된다.
-script에 발급받은 key를 넣어도 인증이 되지 않았다고 오류창이 뜨는 경우 :
브라우저에서 개발자 도구를 이용해 누구나 HTML 코드를 볼 수 있어 다른 사람의 key를 탈취해
자신의 앱에 넣을 수 있음으로 카카오맵 이용권한을 뺏는 것을 방지하기 위해 자신이 등록한 사이트에서만
카카오맵키를 사용하는 것을 허용하고 있다.
-플랫폼 설정하기에서 자신의 사이트를 등록해주면 된다. [ex) http://localhost:3000]
<!-- public/index.html -->
<script
type="text/javascript"
src="//dapi.kakao.com/v2/maps/sdk.js?appkey=발급받은 APP KEY를 넣으시면 됩니다."
></script>
외부에서 CDN방식으로 <script>
태그로 가져오는 라이브러리들은 window.
을 통해 접근 가능하고
타입스크립트의 경우 window
에다가 declare
를 통해서 interface
안에 새로운 프로퍼티를 정의하여
함수들을 가져올 수 있다.
//src/App.tsx
import React, { useEffect } from "react";
import logo from "./logo.svg";
//타입스크립트에서 윈도우객체에 kakao 추가하는 방법
declare global {
interface Window {
kakao: any;
}
}
function App() {
//useEffect를 통해서 마크업들이 로딩된 이후에 실행될 수 있도록 코드 작성
useEffect(() => {
const container = document.getElementById("map");
var options = {
center: new window.kakao.maps.LatLng(33.450701, 126.570667),
level: 3,
};
var map = new window.kakao.maps.Map(container, options);
}, []);
return (
<div>
<div
id="map"
style={{
width: 300,
height: 300,
}}
></div>
</div>
);
}
export default App;
외부에서 정의된 함수를 가져올 때 코드 작성
-html script 함수를 먼저 작성, App.tsx 파일에서 불러온다.
<!-- public/index.html -->
<script
type="text/javascript"
src="//dapi.kakao.com/v2/maps/sdk.js?appkey=발급받은 APP KEY를 넣으시면 됩니다."
></script>
<script>
function loadMap() {
const container = document.getElementById("map");
var options = {
center: new window.kakao.maps.LatLng(33.450701, 126.570667),
level: 3,
};
var map = new window.kakao.maps.Map(container, options);
}
</script>
// src/App.tsx
import React, { useEffect } from "react";
import logo from "./logo.svg";
declare global {
interface Window {
loadMap: () => void;
}
}
function App() {
useEffect(() => {
window.loadMap();
}, []);
return (
<div>
<div
id="map"
style={{
width: 300,
height: 300,
}}
></div>
</div>
);
}
export default App;
함수들이 어떤 타입의 매개변수를 원하는지 미리 파악하여 응용하기
*react에서는 id 쓰는 것을 권장하지 않는다
-> getElementById 대신 useRef를 통해 엘레먼트 가져오기
// src/App.tsx
import React, { useEffect, useRef } from "react";
import logo from "./logo.svg";
declare global {
interface Window {
kakao: any;
}
}
function App() {
const mapRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (mapRef.current) {
var options = {
center: new window.kakao.maps.LatLng(33.450701, 126.570667),
level: 3,
};
var map = new window.kakao.maps.Map(mapRef.current, options);
}
}, []);
return (
<div>
<div
ref={mapRef}
style={{
width: 300,
height: 300,
}}
></div>
</div>
);
}
export default App;
리액트스럽게 코드 변경하기
-index.html에 script를 넣게되면 지도를 사용하지 않을 때에도 지도 자바스크립트를 로딩해야되는
이슈가 있고, index.html에 있는 외부 함수를 window를 통해 접근하는 것이 오류의 원인이기도 한다
[디버깅 할 때 검증하기 어려움] -> 성능 저하, 앱크기 증가 등 악영향이 있을 수 있다.
-동적으로 로딩하게 되면 필요할 때 지도가 보이는 페이지에서만 로딩할 수 있다.
-반드시 script가 로딩 된 이후에 카카오지도가 로딩되는 코드가 실행되어야 한다.
// src/App.tsx
declare global {
interface Window {
kakao: any;
}
}
function App() {
const mapRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const script = document.createElement("script");
script.src = document.head.appendChild(script);
script.onload = () => {
window.kakao.maps.load(() => {
if (mapRef.current) {
var options = {
center: new window.kakao.maps.LatLng(
33.450701,
126.570667
),
level: 3,
};
var map = new window.kakao.maps.Map(
mapRef.current,
options
);
}
});
};
return () => script.remove();
}, []);
return (
<div>
<div
ref={mapRef}
style={{
width: 300,
height: 300,
}}
></div>
</div>
);
}
export default App;
useRef를 이용하여 지역 이동 버튼 구현하기
-값이 새로 설정된다고 해도 리렌더링이 일어나지 않고 UI에 영향을 주지 않게 구현
// src/App.tsx
.
.
function App() {
const mapRef = useRef<HTMLDivElement>(null);
//useRef는 리액트가 리렌더링 되더라도 데이터를 계속 보관하고 있다.
const map = useRef<any>(null);
useEffect(() => {
const script = document.createElement("script");
script.src =
"//dapi.kakao.com/v2/maps/sdk.js?appkey=발급받은 APP KEY를 넣으시면 됩니다&autoload=false";
document.head.appendChild(script);
script.onload = () => {
window.kakao.maps.load(() => {
if (mapRef.current) {
var options = {
center: new window.kakao.maps.LatLng(
33.450701,
126.570667
),
level: 10,
draggable: false,
};
//current안에 있는 값은 리렌더링이 되어도 초기화되지 않는다.
map.current = new window.kakao.maps.Map(mapRef.current, options)
}
});
};
return () => script.remove();
}, []);
return (
<div>
{/* 지역이동하는 버튼만들기 */}
<button
onClick={() => {
map.current.setCenter(
new window.kakao.maps.LatLng(37.5665, 126.9780)
);
}}
>
서울
</button>
<button
onClick={() => {
map.current.setCenter(
new window.kakao.maps.LatLng(35.1796, 129.0756)
);
}}
>
부산
</button>
<div
ref={mapRef}
style={{
width: 300,
height: 300,
}}
></div>
</div>
);
}
export default App;
-지도에 마커 추가하기
-마커 추가할 때 타이틀 입력 기능 추가
-여러번 추가 했을 때 리스트 생성
-마커 삭제 기능 추가
// src/App.tsx
function App() {
const mapRef = useRef<HTMLDivElement>(null);
//마커들의 정보를 저장해서 아래에 띄워주기
const [markerList, setMarkerList] = useState<any[]>([]);
const map = useRef<any>(null);
useEffect(() => {
const script = document.createElement("script");
script.src =
"//dapi.kakao.com/v2/maps/sdk.js?appkey=발급받은 APP KEY를 넣으시면 됩니다&autoload=false";
document.head.appendChild(script);
script.onload = () => {
window.kakao.maps.load(() => {
if (mapRef.current) {
var options = {
center: new window.kakao.maps.LatLng(
33.450701,
126.570667
),
level: 10,
};
map.current = new window.kakao.maps.Map(
mapRef.current,
options
);
//마우스 오른쪽 버튼을 클릭하면 지도에 마크 생기게 하는 로직
window.kakao.maps.event.addListener(
map.current,
"rightclick",
(mouseEvent: any) => {
const latlng = mouseEvent.latLng;
//우클릭을 했을 때 타이틀 작성해주기
const title =
prompt("마커의 타이틀을 입력해주세요.");
//마커 생성하기
var marker = new window.kakao.maps.Marker({
map: map.current,
position: latlng,
title,
});
//마커가 추가될때마다 setMarkerList에 추가
setMarkerList((prev) => [...prev, marker]);
}
);
}
});
};
return () => script.remove();
}, []);
return (
<div>
<button
onClick={() => {
map.current.setMapTypeId(
window.kakao.maps.MapTypeId.HYBRID
);
}}
>
지도 타입 변경
</button>
<div
ref={mapRef}
style={{
width: 300,
height: 300,
}}
></div>
{
// 만들어진 div가 click되었을 때 마커 제거
markerList.map((value) => (
<div
onClick={() => {
value.setMap(null);
//state에서도 마커 제거
setMarkerList(
markerList.filter((v) => v !== value)
);
}}
>
{value.getTitle()}
</div>
))
}
</div>
);
}
export default App;
// src/App.tsx