서비스 이용 신청도 했고 프로젝트에 연동도 했으니 이제 실제로 활용해볼 차례이다.
가자여기에서 네이버 지도를 활용하는 경우는 3가지이다.
const mapElement = useRef<HTMLDivElement>(null);
const mapInstance = useRef<any>(null);
useEffect(() => {
const loadNaverMapScript = () => {
const script = document.createElement('script');
script.src = `https://openapi.map.naver.com/openapi/v3/maps.js?ncpClientId=${naverMapApiKey}&submodules=geocoder`;
script.async = true;
script.onload = () => {
console.log("Naver Map Script Loaded");
initializeMap();
};
script.onerror = () => {
console.error("Failed to load Naver Map Script");
};
document.head.appendChild(script);
};
const initializeMap = () => {
if (mapElement.current && window.naver && window.naver.maps) {
mapInstance.current = new window.naver.maps.Map(mapElement.current, {
scaleControl: false,
logoControl: true,
mapDataControl: true,
zoomControl: false,
minZoom: 6,
center: new window.naver.maps.LatLng(userLocation.latitude, userLocation.longitude),
zoom: 15,
});
console.log("지도 초기화 완료");
loadNaverMapScript();
}, []);
실제로 화면에 지도를 띄우기 위한 코드이다.
mapElement는 지도 자체가 렌더링될 HTML 요소를 참조하고 mapInstance는 렌더링된 지도 객체 자체를 참조하며, 이 객체를 통해 지도와 상호작용할 수 있게 해주는 요소이다.
표시된 지도이다.
네이버 지도에는 여러가지 컨트롤을 지원하는데 나는 네이버 로고를 표시하는 로고 컨트롤, 지도 데이터의 저작권을 표시하는 데이터 컨트롤을 제외한 나머지 컨트롤은 모두 false로 설정해 꺼주었다.
window.naver.maps.LatLng(userLocation.latitude, userLocation.longitude)
코드를 보면 지도의 위치를 사용자의 위치로 지정해주었다.
사용자의 위치를 지정하는 방법은 geolocation 모듈을 사용하였는데
const getLocation = () => {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(
(position) => {
setUserLocation({
latitude: position.coords.latitude,
longitude: position.coords.longitude
});
},
(error) => {
setError(error.message);
}
);
} else {
setError("현재 브라우저에서는 Geolocation을 지원하지 않습니다.");
}
};
useEffect를 이용해 페이지가 렌더링될 때 사용자의 위치 정보를 가져오고 해당 좌표를 Recoil을 이용해 저장한 후 이용하는 방법을 사용하였다.
geolocation 모듈을 사용할 때는 브라우저에서 사용자의 위치 권한 사용 허가 메시지가 표시된다.
geolocation 모듈을 이용해 사용자의 위치 정보를 가져왔으면 해당 좌표를 따로 작성한 웹 서버에 제공해 웹 서버에서 Tour API에 요청을 보내 데이터를 받아온다.
public String getTourInfo(PostDto postDto) {
String urlRoot = "http://apis.data.go.kr/B551011/KorService1/locationBasedList1";
UriComponentsBuilder uriBuilder = UriComponentsBuilder.fromHttpUrl(urlRoot)
.queryParam("serviceKey", apiKey)
.queryParam("pageNo", 1)
.queryParam("numOfRows", 10)
.queryParam("MobileOS", "ETC")
.queryParam("MobileApp", "AppTest")
.queryParam("_type", "json")
.queryParam("mapX", postDto.getPostxpoint())
.queryParam("mapY", postDto.getPostypoint())
.queryParam("radius", 1000)
.queryParam("contentTypeId", 12);
URI uri;
try {
uri = new URI(uriBuilder.toUriString());
} catch (URISyntaxException e) {
throw new RuntimeException("Error creating URI", e);
}
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set("Accept", "*/*;q=0.9");
HttpEntity<String> httpRequest = new HttpEntity<>(null, headers);
ResponseEntity<String> response;
try {
response = restTemplate.exchange(uri, HttpMethod.GET, httpRequest, new ParameterizedTypeReference<String>() {});
} catch (RestClientException e) {
throw new RuntimeException("Error fetching tour info", e);
}
return response.getBody();
}
Spring을 이용해 별도로 작성한 코드이다. geolocation을 통해 얻어온 사용자의 좌표 정보를 입력해 해당 위치에서 가까운 관광지 정보를 얻어온다.
지도에 너무 많은 정보를 띄우지 않게 하기위해 데이터의 최대 갯수는 10개로 지정했고
.queryParam("numOfRows", 10)
해당 위치에서 가까운 곳의 관광지를 알기 위해 거리 범위는 우선 1km로 정했다.
.queryParam("numOfRows", 10)
서버로부터 Tour API에서 제공하는 관광지 정보를 받아왔다면 지도에 표시할 차례이다.
if (tourAPIAttraction) {
tourAPIAttraction.forEach(attraction => {
new window.naver.maps.Marker({
position: new window.naver.maps.LatLng(Number(attraction.mapy), Number(attraction.mapx)),
map: mapInstance.current
});
});
} else {
alert('해당 지역에서 장소 검색에 실패하였습니다.');
}
서버에서 받아온 관광지 정보를 tourAPIAttraction이라는 배열에 저장했고 해당 배열이 존재하면(관광지 정보가 존재한다면) 해당 관광지 정보에서 제공하는 좌표 정보를 이용해 좌표에 해당하는 위치에 마커를 표시하였다.
가자여기는 사용자가 주소를 검색해 해당 주소에 위치한 관광지 정보를 알려주는 기능도 제공한다.
이를 활용하기 위해 Geocoder 모듈을 사용하였는데, Geocoding 주소를 이용하여 좌표를 비롯한 정보를 제공하는 모듈이고, Reverse Geocoding은 반대로 좌표를 이용해 주소 정보를 제공하는 기능이다.
Reverse Geocoding 모듈은 추후에 또 사용하는 기능이므로 나중에 설명하겠다.
Geocoder 모듈에 대한 자세한 설명은
이곳을 참고하자.
const handleSearch = (keyword: string) => {
if (window.naver && window.naver.maps && window.naver.maps.Service) {
window.naver.maps.Service.geocode(
{ query: keyword },
(status: any, res: any) => {
if (status === window.naver.maps.Service.Status.OK) {
if (res.v2.addresses.length > 0) {
const resAddress = res.v2.addresses[0];
const lng = parseFloat(resAddress.x);
const lat = parseFloat(resAddress.y);
mapInstance.current.setCenter(new window.naver.maps.LatLng(lat, lng));
setUserLocation({
latitude: lat,
longitude: lng
});
} else {
alert("검색 실패");
}
} else {
alert("검색 실패");
}
}
);
} else {
alert("Naver Maps Service is not available");
}
};
Geocoding을 구현한 코드이다.
사용자는 검색창에 검색할 키워드(주소)를 입력하면 해당 키워드를 네이버 지도 서비스에 요청해 좌표 정보를 찾아온다.
좌표 찾기에 성공했으면 지도의 center를 해당 좌표로 이동시키고 해당 좌표로 tour API에 요청을 보내 반경 1km 이내의 관광지 정보를 받아와 유저에게 제공한다.
주소 검색이 아닌 사용자가 직접 지도를 움직여 해당 위치에서의 관광지 정보를 얻기 위해 커스텀 컨트롤을 추가하였다.
// 커스텀 컨트롤
const locationBtnHtml = `
<div class="relative">
<div id="connectionInfo" class="absolute flex items-center bg-black opacity-0 rounded-full px-3 py-1 mt-1 transition-opacity duration-300 ease-in-out left-0 transform -translate-x-full whitespace-nowrap pointer-events-none">
<span class="text-white text-sm font-bold">현재 위치에서 찾기</span>
</div>
<a href="#" class="inline-flex items-center bg-white hover:bg-gray-200 text-gray-800 font-bold py-1 px-2 ml-2 rounded-lg shadow-md">
<span class="text-2xl">👀</span>
</a>
</div>
`;
naver.maps.Event.once(mapInstance.current, 'init', function() {
var customControl = new window.naver.maps.CustomControl(locationBtnHtml, {
position: window.naver.maps.Position.RIGHT_CENTER,
});
customControl.setMap(mapInstance.current);
const buttonElement = customControl.getElement().querySelector('a');
if (buttonElement) {
naver.maps.Event.addDOMListener(buttonElement, 'mouseover', function() {
const connectionInfo = document.getElementById('connectionInfo');
if (connectionInfo) {
connectionInfo.style.opacity = '1';
}
});
naver.maps.Event.addDOMListener(buttonElement, 'mouseout', function() {
const connectionInfo = document.getElementById('connectionInfo');
if (connectionInfo) {
connectionInfo.style.opacity = '0';
}
});
naver.maps.Event.addDOMListener(buttonElement, 'click', () => {
const center = mapInstance.current.getCenter();
const lat = center.lat();
const lng = center.lng();
setUserLocation({
latitude: lat,
longitude: lng
});
});
}
});
html 태그를 이용해 버튼을 디자인하고 해당 버튼에 클릭 이벤트를 추가하여 버튼을 클릭하면 현재 지도의 center에 해당하는 곳의 좌표를 얻어와 해당 좌표로 Tour API에 관광지 정보를 요청하는 방식이다.