원래 프론트엔드를 공부하던 사람으로써 배운 내용을 응용해서 뭐하나 만들어보려고 했던 내용을 일기처럼 가볍게 기록해보려고 한다.
처음에 뭘 만들어볼까 조금 고민했다.
사실 웹 어플리케이션은 이미 너무 많아서 새로운 아이디어를 찾는건 버겁다고 느꼈다. 물론 아직 개발 실력도 부족할 것이다..
난 요즘 새로운 곳을 갈 때마다 네이버 지도 어플을 항상 달고사는데, 근처 음식점이나 병원, 그리고 카페를 찾을 때도 항상 네이버 지도를 통해서 검색한다.
그저께 오랜만에 군대 후임들 만나서 술을 마시는데, 이동할 때마다 근처 술집 찾는게 너무 귀찮았다 😅
그래서 지금까지 배운내용이랑 내가 알고있는 개념을 사용해서 근처 OO 찾기 웹 어플리케이션을 만들어봤다.
(사실 나중에 반응형 웹 적용하고 vercel로 프론트만 배포해서 나 혼자만 써먹을 생각이었다)
우선 OO 찾기에서 가장 데이터가 많을 것 같은 "카페"로 개발하기 시작했다.
일단 테스트부터 시작하고, 쿼리는 나중에 필요에따라 로직을 수정해주면 되니까
우선, 기본적인 구상은 다음과 같았다.
구조
네이버 지도 API 적용 -> 네이버 지도 API를 통해 OO 검색 -> 검색 결과로 나오는 주변 OO들을 파싱 -> 내가 쓰기 편한 UI로 뿌려주기
사실 프론트엔드 개발 경험도 별로 없어서 이런 API를 사용하는건 처음이다
그래서 구글링부터 시작했다.
구글링해보니까 뭔가뭔가였다.
내가 필요한 정보가 있는듯 없는듯 했다. (내가 못찾는건가?)
이런 API를 사용하려면 우선 등록할게 있었다.
여기서 필요한 정보를 다 입력하고 등록했다.
등록을 다 하니까 화면처럼 Client ID랑 Client Secret이 발급되더라
이제 이걸 넘겨주면 네이버 Map을 내 개발 화면에 그릴 수 있다.
<script
type="text/javascript"
src="https://oapi.map.naver.com/openapi/v3/maps.js?ncpClientId=%VITE_NAVER_MAP_CLIENT_ID%&submodules=geocoder"
/>
위 스크립트 코드처럼 ClientId를 넘겨주면 된다.
나는 .env.development에서 환경변수로 이전에 발급받은 ID와 Secret을 관리하고 있었기 때문에 %%
문법을 사용해서 API를 불러왔다.
참고로 뒤에있는 submodules는 주소를 좌표료 변경해주는 모듈인데 필요해서 넣었다. (이건 나중에 소개하자)
이제 스크립트 코드는 추가했는데, 화면에 어떻게 그리지 싶었다.
조금 찾아봤는데, DOM요소에 접근하기 위해 useRef를 사용하면 되는 것 같았다.
이전에 정리한 내용이라 그런지 위화감(?)은 없었다.
Naver Map 그리기
import { useEffect } from "react"; import { useRef } from "react"; const NaverMap = () => { const mapElement = useRef(null); const { naver } = window; useEffect(() => { new naver.maps.Map(mapElement.current, { center: new naver.maps.LatLng(37.3595704, 127.105399), zoom: 16, }); }); return ( <div ref={mapElement} style={{ width: "100%", height: 400 }}> NaverMap </div> ); }; export default NaverMap;
Result View
위 코드처럼 작성하고 App.jsx에서 인풋박스를 추가한 화면이 바로 Result View이다.
우선 naver API를 사용하기 위해 스크립트로 추가한 naver를 window로부터 구조 분해 할당으로 받아온다.
다음으로 useRef를 사용해서 DOM 요소에 직접 접근할 수 있도록 ref에 할당시켜준다.
이후, 이전에 받아온 naver
를 사용해서 지도를 조작하면 된다.
위 코드에서는 새로운 지도 인스턴스를 생성하고, 첫번째 매개변수로 DOM 요소를 넘겨준다. 그러면 그 위치에 지도가 생긴다.
두번째 매개변수는 객체인데, 여기서 지도의 기본적인 세팅이 들어간다.
좌표는 네이버 본사 주소의 위도 경도를 찍어서 넘겼다.
이제 다음으로 내가 검색한 내용에 맞춰서 지도의 위치가 렌더링되어야 한다.
그런데 여기서부터 조금씩 꼬이기 시작한다
우선 코드를 보자
const NaverMap = ({ searchKeyword }) => {
const mapElement = useRef(null);
const mapInstance = useRef(null);
const { naver } = window;
const [address, setAddress] = useState({
x: 127.105399,
y: 37.3595704,
});
useEffect(() => {
if (searchKeyword) {
naver.maps.Service.geocode(
{
query: searchKeyword,
},
function (status, res) {
console.log(res);
if (res.v2.addresses.length === 0) {
alert("검색 결과가 없습니다.");
} else {
const resAddress = res.v2.addresses[0];
const x = parseFloat(resAddress.x);
const y = parseFloat(resAddress.y);
setAddress({ x, y });
}
}
);
}
}, [searchKeyword]);
useEffect(() => {
if (!mapElement.current) return;
if (mapInstance.current) {
mapInstance.current.destroy();
}
mapInstance.current = new naver.maps.Map(mapElement.current, {
center: new naver.maps.LatLng(address.y, address.x),
zoom: 16,
});
}, [address]);
return (
<div ref={mapElement} style={{ width: "100%", height: 400 }}>
NaverMap
</div>
);
};
export default memo(NaverMap);
뭔가 좀 복잡해진 것 같다.
위 코드는 크게 2파트로 나뉘는데 지도 인스턴스 생성
그리고 검색 결과 저장
이다.
우선 추가된 내용과 그 이유를 설명하면 다음과 같다.
mapInstance라는 useRef의 리턴값을 추가했는데, 찾아보니 지도가 렌더링될 때마다 지도 인스턴스가 계속 생겨난다고 한다.
그러면 당연히 메모리가 계속 차니까 성능적으로 문제가 발생할 수 있다.
그래서 생성한 지도 인스턴스를 관리하기 위해 mapInstance 변수를 생성한다.
(검색어가 입력되어 새로운 지도를 그리기 전에 이전 인스턴스를 없애려고 만들었다)
인스턴스 없애는 부분
useEffect(() => { if (!mapElement.current) return; if (mapInstance.current) { mapInstance.current.destroy(); } mapInstance.current = new naver.maps.Map(mapElement.current, { center: new naver.maps.LatLng(address.y, address.x), zoom: 16, }); }, [address]);
- destroy로 없애고 새로운 주소에 맞춰 다시 그린다.
다음으로 전체적인 흐름은 다음과 같다.
로직 흐름
1. 검색한다.
2. response가 온다.
3. 그 결과를 위도, 경도로 변경해서 저장한다.
4. Naver Map에 뿌려준다.
위도 경도는 한번에 업데이트 되니까 address 상태변수를 추가해서 관리하려고 했다.
키워드 검색 -> 검색결과 위도/경도 저장
useEffect(() => { if (searchKeyword) { naver.maps.Service.geocode( { query: searchKeyword, // 검색 }, function (status, res) { console.log(res); if (res.v2.addresses.length === 0) { alert("검색 결과가 없습니다."); } else { // 반환된 내용으로 주소 변환 const resAddress = res.v2.addresses[0]; const x = parseFloat(resAddress.x); const y = parseFloat(resAddress.y); setAddress({ x, y }); } } ); } }, [searchKeyword]);
위 코드는 props로 searchKeyword를 넘겨받아서 쿼리에 넣고 결과의 주소를 저장하는 로직이다.
코드를 실행시켜본 결과는 다음과 같다.
잘 보일지 모르겠지만 처음에는서울
로 검색하고 두번째는 롯데월드
로 검색한다.
그런데 롯데월드
로 검색하면 address가 넘어오지 않는다.
알고보니 Naver Map API는 주소만 입력받는다고 한다.
즉, 주소를 입력 받으면 위도 / 경도로 변경해서 그 위치를 화면에 렌더링하는 로직이 내장되어있는 것이다.
이 문제를 해결하기 위해 장소명으로 검색했을 때 위도 경도로 변경해주는 서브모듈이 있는지 찾아봤다.
그런데 없더라.......
나는 네이버 지도 어플과 동일하게 롯데월드 근처 카페
와 같은 쿼리를 날리면 그에 맞는 지점이 response로 날아오는 줄 알았다.
하지만, 지도의 위도/경도를 기준으로 화면에 보여주는 기능만 제공한다.
따라서, 기존 구조가 아래처럼 변경되었다.
구조
네이버 지도 API 적용 ->네이버 지도 API를 통해 OO 검색->검색 결과로 나오는 주변 OO들을 파싱-> 내가 쓰기 편한 UI로 뿌려주기
사실 변경이라기보다는 삭제당한게 맞다.
그래서 어플리케이션의 목표를 조금 수정했다.
수정된 목표
1. 기본적으로 내 위치를 기반으로 OO을 찾는다.
2. 도로명주소를 대충 입력해도 그 근처의 OO을 지도에 표시한다.
다음 포스팅에서 이걸 어떻게 해결했는지 이어서 작성할 예정이다.
참고
[React/Typescript] 네이버 지도 API (1편) - 서비스 신청 및 연동하기
네이버 API 명세