📍 지도 API
📍 모든 것이 처음이라
📍 기업 분석
📍 지도 API
내가 참여하게된 기업은 네이버 지도에 데이터를 시각화하여 사용자에게 정보를 전달하는 서비스를 진행하고 있었다.
지도는 단 한번도 사용하지 않았던 API이다. 하지만 모든 플랫폼이나 서비스에서 지도를 사용하고 있다. 그만큼 수요가 많기 때문에 꼭 도전해보고 싶었다. 하지만 어렵다는 소문을 익히 들어서 조금은 긴장할 수 밖에 없었다.
📍 모든 것이 처음이라
기업 협업은 지금까지 진행했던 프로젝트와 달리 기업에서 사이드 프로젝트로 진행하는 프로그램이기 때문에 기획부터 UI 디자인, 배포까지 전 과정을 나와 팀원의 결정에 의해 이루어 진다. 자율성이 보장되고 우리가 해보고 싶었던 모든 열정을 쏟아 부어서 좋지만 그만큼 걱정도 된다. 기간은 딱 3주..그 안에 기획부터 가능할지가... 그래도 한 번 해보자!
📍 기업 분석
기업 협업이라는 소중한 기회가 왔을 때 최선을 다해야 한다. 그러기 위해서는 기업이 어떤 서비스를 진행중인지, 어떤 가치관을 가지고 소비자에게 다가가는지 파악해야 한다. 기업을 분석하면 사이드로 진행될 우리의 프로젝트를 이해하는데 시간을 단축할 수 있다. 기업 공식 홈페이지, 유튜브, 소셜 미디어 모두 찾아 보면서 대표님의 인터뷰, 기사 등을 읽었다. 과연 나 같은 2개월차 개발자가 이런 대단한 분들의 사이에서 어떤 코드를 작성할 수 있을까 설레며 긴장되었다.
📍 구현 내용
📍 Errors & Blockers
Github 프로젝트 repository의 README.md에서 자세한 코드 설명을 볼 수 있다.
📍 구현 내용
공식 문서 Naver Maps JavaScript API v3에서 확인할 수 있듯 리액트에서 작성하기 위해서는 따로 라이브러리 설치가 요구되었다. 공식 문서를 통해 속성 및 기능에 대해 참고하고 npm install react-naver-maps
진행하였다.
⛳ 자바스크립트 참고하며 React 컴포넌트 만들기
👉 Naver Map Javascript v3에서 제공하는 자바스크립트로 Map 이용하는 방법
var map = new naver.maps.Map('map', {
center: new naver.maps.LatLng(37.3595704, 127.105399),
zoom: 15
});
👉 Naver Map Javascript v3에서 Map 코드 라이브러리를 통해 React에서 사용하는 방법
npm install react-naver-maps
설치
import {
RenderAfterNavermapsLoaded,
NaverMap,
} from 'react-naver-maps';
import './Map.scss';
const Map = () => {
return (
<RenderAfterNavermapsLoaded
ncpClientId="네이버 클라우드 플랫폼에서 제공한 client key"
error={<p>Maps Load Error</p>}
loading={<p>Maps Loading...</p>}
>
<NaverMapAPI />
</RenderAfterNavermapsLoaded>
);
};
const NaverMapAPI = () => {
return (
<>
<NaverMap
id="react-naver-maps-introduction"
style={{ width: '100%', height: '90vh'}}
defaultCenter={{ lat: 37.497175, lng: 127.027926 }}
//초기 화면 지도의 중앙 좌표
defaultZoom={13}
//축소, 확대 기준
>
</NaverMap>
</>
⛳ Marker 표시하기
react-naver-maps에서 기본적으로 제공하는 컴포넌트를 import 한다.
네이버 맵 공식문서에서 제공하는 다양한 속성을 파악한다.
import React, { useEffect, useState } from 'react';
import './Map.scss';
import {
RenderAfterNavermapsLoaded,
NaverMap,
Marker,
} from 'react-naver-maps';
function NaverMapAPI() {
const navermaps = window.naver.maps;
const [mydata, setMyData] = useState([]);
useEffect(() => {
fetch('/data/lineTwo.json')
.then(res => res.json())
.then(data => {
setMyData(data.line);
});
}, []);
if (mydata.length === 0) return;
return (
<NaverMap
id="react-naver-maps-introduction"
style={{ width: '100%', height: '90vh', borderTop: 'transparent' }}
defaultCenter={{ lat: 37.497175, lng: 127.027926 }}
defaultZoom={14}
>
{mydata.map(input => (
<Marker
key={input.station}
position={new navermaps.LatLng(...input.code)}
animation={2}
title={input.station}
icon={{
content:
`<button class="markerBox">
<div class="totalOrder">${input.order}</div>
${input.station}</button>`,
}}
/>
))}
</NaverMap>
);
}
const Map = () => {
return (
<RenderAfterNavermapsLoaded
ncpClientId="발급받은 client key"
error={<p>Maps Load Error</p>}
loading={<p>Maps Loading...</p>}
>
<NaverMapAPI />
</RenderAfterNavermapsLoaded>
);
};
export default Map;
position : Marker가 표시될 좌표
icon : Marker의 아이콘 커스터마이징(스타일은 scss로 진행)
title : Marker가 가지고 있는 기본적인 Marker 이름
animation : Marker가 나타날 때 보여지는 애니메이션(1,2,3)
new navermaps.LatLng(...input.code) 좌표를 그려주는 함수
네이버 지도에서 기본적으로 제공하는 좌표를 화면에 나타내는 메서드는 위와 같다. new navermaps.LatLng([x축 좌표, y축 좌표])
의 기본값에서 Mockdata의 데이터를 매핑을 통해 구현했다.
페이지 상단에는 매장 전체보기, A매장, B매장, C매장 버튼이 있다.
header 바로 아래 왼쪽에는 전체, 배달의 민족, 요기요 버튼이 있다.
각각의 버튼을 클릭했을 때, A매장에 해당하는 정보가 지도에 그려지게 필터링을 한다.
useSearchParams를 활용하여 Query String의 형태로 서버에 GET 요청을 한다.
서버가 요청때 맞춰달라는 Query String 형식
⛳ filter를 위한 hook 따로 생성하기
useFilter.js
import { useSearchParams, useLocation } from 'react-router-dom';
const useFilter = () => {
const location = useLocation();
//url 정보를 가져오기 위함
const [searchParams, setSearchParams] = useSearchParams();
//주소창에 삽입할 쿼리 스트링을 searchParams에 저장
const sortStore = storeName => {
if (location.search.includes('application')) {
const application = searchParams.get('application');
setSearchParams({ store: `${storeName}`, application });
} else {
setSearchParams({ store: `${storeName}` });
//key는 쿼리 스트링에서 ? 다음, value 는 = 이후
//즉, ?store=${storeName}으로 url이 설정된다.
}
};
/*if문을 작성하여 사용자가 배달 플랫폼을 클릭한 상태 or 클릭하지 않은 상태에서의
url 설정을 다르게 해야하기 때문*/
const showAllStoreData = () => {
location.search.includes('store', 'application') &&
searchParams.delete('store', 'application');
setSearchParams(searchParams);
};
//전제 매장 보기 버튼을 클릭하면 모든 쿼리스트링이 지워지게 설정
return { sortStore, showAllStoreData };
};
export default useFilter;
⛳ 매장 필터링
Main.js
import React, { useEffect, useState } from 'react';
import { useLocation } from 'react-router-dom';
import Header from '../Header/Header';
import Map from '../Map/Map';
import './Main.scss';
const Main = () => {
const location = useLocation();
const [dongData, setDongData] = useState([]);
const url = location.search;
useEffect(() => {
fetch(`http://서버/regions${url}`)
.then(res => res.json())
.then(data => {
setDongData(data.result);
});
}, [url]);
if (dongData.length === 0) return;
return (
<>
<Header dongData={dongData} url={url} />
<Map />
</>
);
};
export default Main;
최상위 컴포넌트인 Main.js 에서 데이터를 넘겨주기 위해 fetch 함수를 작성한다.
서버에서 Query String으로 데이터를 GET 방식으로 요청하라고 했기에 url 즉, location.search을 그대로 보낸다.
받은 데이터는 그대로 dongData를 state에 저장하여 Header.js, Map.js에 넘겨준다.
**📍 Errors & Blockers
📢 Blocker
Naver Map에서 hover 이벤트는 onMouseover이다. onMouseOver라고 작성하면 작동하지 않는다
📢 과정 정리하기
역삼동 Marker에 mouseover 시 역삼동 좌표의 폴리곤과 폴리라인이 그려져야 한다. 역삼동의 좌표 데이터를 넘겨준다.
onMouseover 의 콜백함수로 usePloygon hook에서 import 한 handleHoverCoordinate 함수를 작성한다.
전달되는 인자는 dongData의 input 즉, 각각의 데이터이다.
const handleHoverCoordinate = data => {
setPoly(data.coordinate.coordinates[0][0]);
};
//매개변수 data가 인자인 dongData의 input을 받는다.
//따라서 mouseover되는 객체의 데이터가 함수의 인자가 된다.
Marker에 mouseover 될 때 폴리곤과 폴리라인이 생성되고 mouseout되면 생성되지 않는다.
모달창과 원리는 같다. 조건부 연산자를 사용하여 true 라면 폴리라인과 폴리곤이 보여지게 되고 false라면 보이지 않게 된다.
const [isMouseOn, setIsMouseOn] = useState(false);
초기값은 mouseover 되지 않은 상태에는 보이지 않기 때문에 false 값
mouseover되면 isMouseOn은 true 값을 가져 폴리곤과 폴리라인이 그려진다.
{isMouseOn && (
<>
<Polyline
clickable={true}
strokeColor="rgb(17, 135, 207)"
strokeStyle="solid"
strokeWeight={2}
path={onHoverPaths}
/>
<Polygon
fillColor="rgb(17, 135, 207)"
fillOpacity={0.35}
clickable={true}
paths={onHoverPaths}
/>
</>
)}
📢 Blocker
초기화면에서 매장, 플랫폼, 전체버튼을 2번 눌러야 지도에 알맞게 마커가 표시된다. 첫번째 눌렀을 때 주소창에는 해당하는 Query String이 데이터를 잘 요청하고 있었다. dongData에도 버튼에 따른 정확한 데이터가 저장되었다. 문제는 UI가 변경되지 않는다. 의존성 배열에 dongData를 명시해도 비동기적으로 동작하기 때문에 문제를 해결하지 못했다.
📢 해결!!
과정을 다시 생각하자
버튼 클릭 ▶️ url 변경 ▶️ 데이터 요청 ▶️ 화면에 구현
의존성 배열에 변수 url을 작성한다.
간단하게 url이 변경될 때마다 fetch 함수를 요청하는 것이기 때문이다. 그럼 의존성 배열에 url을 삽입하면 url이 변경될 때마다 즉, 버튼이 클릭될 때마다 데이터를 요청하고 어차피 dongData에 변경된 데이터가 담기기 때문에 state는 자동으로 업데이트 된다. 그럼 UI도 변경될 것이다.
📍 힘들었던 점
📍 스스로를 칭찬하고 싶었던 점
📍 프로젝트 전 / 후의 나
📍 힘들었던 점
사실 모든 과정이 힘들었다. 물론 뿌듯함이 더 컸지만 기획단계에서 백엔드와 마찰이 있었고 기존의 데이터와 공공 API를 접목시켜 새로운 인사이트를 도출하는 기획이 가장 힘들었다. 서울 열린 데이터 광장이나 오픈 API 사이트에서 여러 주제들을 확인할 수 있었지만 우리의 기준에 딱 맞는 API를 찾기 어려웠다. 크게 관련은 없지만 배달지의 연령, 성별 인구 데이터를 찾아 새로운 데이터를 나타냈다. 해당 데이터도 행정동이 아닌 법정동 코드가 필요했기에 까다로웠다.
방대한 데이터를 다루다 보니 리액트의 기본을 정확히 알고 있지 않으면 데이터를 효율적으로 뿌려주기 어려웠다. 이 부분에서 심각하게 상심을 하고 퇴근 후 위워크로 달려가 나머지 공부를 하다 항상 그렇듯 막차를 타고 집에 갔다.
📍 스스로를 칭찬하고 싶었던 점
한 달 내내 퇴근 후 위워크에서 나머지 공부와 오늘 추가한 기능에 대한 기술 블로그를 작성했다. 칼퇴란 내 인생에 없는 것처럼 매일 노력하니 이해되지 않았던 코드, 생각이 나지 않았던 로직이 하나 둘 내 것이 되기 시작했다. 역시 practice makes improvment를 항상 되내이고 있으면 성장하기 마련이다. 포기하지 않고 동료들과 소통을 하며 끝까지 프로젝트를 완성시켰다.
📍 프로젝트 전 / 후의 나
배포의 배도 몰랐는데 어느새 인스턴스를 생성하고 우분투 서버에 접속하여 배포를 하고 있는 내 모습에 놀랐다. 가장 크게는 리액트에 대한 이해가 깊어졌고 재미도 생겼다. 클론 코딩으로 다져진 코딩 실력에서 기획이 더해지면서 소비자 입장에서 먼저 생각하게 되는 개발자로 한층 성장했다. 이렇게 로직을 짜면 사용자들이 사용하는데 불편함이 있을까 등을 고민했다. 테크리드님께서 정말 많이 도와주시고 조언해주셨다. 진심으로 감사드린다. 현업에서는 이럴 때는 이렇게 처리하고 서비스적인 마인드를 어떻게 갖고 개발을 해야 하는 지 등 학원에서는 절대 배울 수 없는 부분을 많이 습득했다. 기업 협업으로 코딩 실력도 늘었지만 서비스 전달에 대해서 깊게 생각하는 계기가 되었다.