맵 API를 프로젝트에 갖고와서 쓰게 되었다. 맛집 리뷰를 작성하게 하는데, 동명의 매장명이 존재할 수 있다는 문제와 똑같은 매장을 서로 다른 이름으로 작성할 수 있다는 문제를 해결해주어야 했다. 그래서 장소 검색을 제공하는 API를 찾게 되었다.
우선 크게 3개의 선택지가 있었다. 네이버, 카카오, 구글. 네이버는 네이버 플레이스를 제공하지 않는 문제로 제일 먼저 탈락했다. 그리고 카카오와 구글맵이 남았다. 우선 진행하는 프로젝트의 타겟이 국내의 매장들이라 생각되어서, 국내 매장 데이터가 많을 카카오를 선택해서 진행하였다.
하지만 다음과 같은 이슈가 발생했다. 카카오에서 갖고온 매장 데이터 중, 매장의 고유 ID값만 저장하고 사용하려 했는데, 매장 ID 값만으로는 더 이상 어떤 기능을 제공하지 못했다. 예를 들어 구현하려고 했던 기능은, 매장 ID값으로 매장 정보를 요청하면 다시 매장의 위경도 위치 등의 정보를 받아와 다시 맵 API에서 해당 매장의 위치를 표시해주는 것이다. 카카오에서는 매장 ID값만으로는 앞서 설명한 기능을 구현하지 못했다.
결국 마지막 선택지인 구글맵을 알아봤고, 구글맵에서는 매장ID 만으로 다시 매장에 대한 정보를 요청할 수 있는 API가 제공된다는 것을 찾았다. 카카오맵으로 다 구현을 해놓고도 다시 구글맵으로 엎게 되었다. 이번 교훈으로는 API 기능 제공
에 대해서 미리 자세히 알아볼 필요가 있다는 것이다.
사실 카카오맵을 통한 매장 검색 결과는 탁월했다. ID값 이외에도 매장 주소, 위경도 위치 등 검색 결과 값을 아낌없이 반환해준다. 사실 매장에 대한 위경도 위치를 데이터베이스에 따로 저장하면 카카오맵으로도 쉽게 풀리는 문제였다. 하지만 이번 프로젝트에서는 Web3적인 요소가 포함되어 있기 때문에 데이터베이스 테이블 구성이 이미 충분히 복잡했기 때문에 장소 관련 테이블을 따로 구성하지 않기로 했다. 결국 이번 API 선택을 좌지우지 한 것은 ID 값의 기능성이었다.
카카오맵에서 지원하던 방식이 잘 작동했기 때문에 카카오맵을 구현하던 방식으로 그대로 구글맵을 구현하면 잘 작동할 줄 알았다. 해당 구현은 다음과 같다.
[구글맵 API 연동]
// public > index.html
<script
src="https://maps.googleapis.com/maps/api/js?key={키값}&callback=initMap&v=weekly"
defer
></script>
위 스크립트를 React의 public 디렉터리에 존재하는 index.html
에 삽입하고 난 이후에 google 객체를 이용하면 바로 구글맵이 연동될 줄 알았다. 하지만 다음과 같은 에러가 발생했다.
TypeError: window.initMap is not a function
사실 initMap은 JavaScript
로 Map
객체를 생성하는 콜백함수를 할당하면 된다. 하지만 해당 사항은 기존 웹에서 통하는 방법이고, 리액트에서는 위 스크립트가 읽어들여진 이후에 실행되기 때문에 마땅히 해결할 방법이 떠오르지 않았다. 단순히 고정된 맵을 띄우는게 목표가 아니라 클라이언트의 행위에 따라 맵이 상호작용할 수 있게 만들어야 하기 때문이다.
분명 키값을 올바르게 넣었는데도 불구하고 브라우저에서는 google.maps 객체를 이용하지 못하였다. 구글링을 하다보니 다음과 같은 해결책을 던져주었다.
리액트 사용유저를 위한 라이브러리를 설치해봐
-- 스택오버플로우 은인
사실 공식문서에서도 이미 @googlemaps/react-wrapper
라이브러리를 튜토리얼로 잘 설명해주고 있었다. 다시 한 번, 공식문서를 잘 읽자는 교훈을 얻어간다.
[라이브러리 설치]
npm install @googlemaps/react-wrapper
[리액트 컴포넌트 작성]
import {Wrapper} from "@googlemaps/react-wrapper"
// ...생략
<Wrapper apiKey={"API KEY"} libraries=["places"]>
<커스텀 컴포넌트></커스텀 컴포넌트>
</Wrapper>
// ...생략
구글맵은 안에서도 다양한 라이브러리르 제공하고 있는데, 이 프로젝트에서는 장소검색
의 기능을 제공하기 위하여 places
라이브러리도 같이 포함시켰다. 여기까지가 재료준비 끝이다.
Wrapper
는 말그대로 Wrapper 일 뿐이다. 스크립트를 html 문서에 달아주는 방식을 리액트스럽게 바꿔준 방식이다. 구글맵을 초기화하는 것은 Wrapper
을 감싸고 있는 커스텀 컴포넌트에서 진행해야한다. 또한 실제 구글맵 자바스크립트 코드를 통해 HTML과 CSS가 주입되는 엘리먼트도 지정해주어야 한다. 이는 다음과 같은 코드로 진행이 된다.
[구글맵 UI 렌더링 컴포넌트]
import { useState, useEffect, useRef } from "react"
const 커스텀컴포넌트 = ()=>{
const [map, setMap] = useState(null);
const ref = useRef();
useEffect(()=>{
const newMap = new window.google.maps.Map(ref.current, {
center : { lat: 37.569227, lng: 126.9777256},
zoom : 16,
});
setMap(newMap);
},[])
return (
<div ref={ref} id="map" style={{width:"400px", height: "400px"}}></div>
)
}
export default 커스텀컴포넌트
이 커스텀 컴포넌트가 내 생각에는 initMap 콜백 함수의 역할을 하는 듯 하다. 이 코드를 사용하게 되면 광화문 광장에 위치한 구글맵을 확인할 수 있을 것이다.