본문 바로가기
게임서버-스파르타코딩NodeJs_7기/CH6 최종 프로젝트

사용자위치와 다수의 북마커 관리

by GREEN나무 2025. 2. 26.
728x90

사용자가 100개 이상의 북마커를 관리하며,

  1. 사용자 반경 500m 내의 북마커만 지도에 표시
  2. 지도 화면에 보이는 북마커만 표시
    이 두 가지 버전을 토글 버튼으로 전환하는 기능을 구현하는 방법을 정리해볼게.

1. 개요 (전체 흐름)

  1. 프론트엔드:
    • navigator.geolocation.watchPosition()으로 사용자의 위치를 실시간으로 감지
    • 카카오맵 API를 사용하여 지도를 표시하고 북마커 렌더링
    • 토글 버튼 클릭 시 반경 500m 또는 화면 내 북마커 모드 전환
  2. 백엔드 (NestJS):
    • geolib을 사용하여 반경 500m 이내의 북마커 필터링
    • 클라이언트에서 요청 시 현재 지도 화면 내 북마커만 반환

2. 백엔드 (NestJS) 구현

(1) geolib을 사용하여 반경 500m 내 북마커 필터링

import { Controller, Get, Query } from '@nestjs/common';
import { getDistance, isPointWithinRadius } from 'geolib';

// 북마커 데이터 (DB에서 가져올 수도 있음)
const BOOKMARKS = [
  { id: 1, name: "A 북마커", latitude: 37.5665, longitude: 126.9780 },
  { id: 2, name: "B 북마커", latitude: 37.5651, longitude: 126.9768 },
  { id: 3, name: "C 북마커", latitude: 37.5642, longitude: 126.9755 },
  // ... 100개 이상 추가
];

@Controller('bookmarks')
export class BookmarkController {
  // 반경 500m 내 북마커 필터링
  @Get('nearby')
  getNearbyBookmarks(
    @Query('latitude') latitude: string,
    @Query('longitude') longitude: string
  ) {
    const userLocation = { latitude: parseFloat(latitude), longitude: parseFloat(longitude) };

    const nearbyBookmarks = BOOKMARKS.filter(marker =>
      isPointWithinRadius(
        { latitude: marker.latitude, longitude: marker.longitude },
        userLocation,
        500 // 500m 이내 필터링
      )
    );

    return nearbyBookmarks;
  }

  // 현재 지도 화면 내 북마커 필터링
  @Get('visible')
  getVisibleBookmarks(
    @Query('minLat') minLat: string,
    @Query('maxLat') maxLat: string,
    @Query('minLng') minLng: string,
    @Query('maxLng') maxLng: string
  ) {
    return BOOKMARKS.filter(marker =>
      marker.latitude >= parseFloat(minLat) &&
      marker.latitude <= parseFloat(maxLat) &&
      marker.longitude >= parseFloat(minLng) &&
      marker.longitude <= parseFloat(maxLng)
    );
  }
}

📌 두 가지 API 제공:

  • /bookmarks/nearby → 사용자 반경 500m 이내 북마커 반환
  • /bookmarks/visible → 현재 지도 화면에 보이는 북마커 반환

3. 프론트엔드 (카카오맵 API) 구현

(1) 카카오맵 API 로드

<script type="text/javascript" src="//dapi.kakao.com/v2/maps/sdk.js?appkey=YOUR_KAKAO_MAP_API_KEY"></script>

(2) 지도 표시 + 토글 버튼 생성

<button id="toggleMode">모드 변경</button>
<div id="map" style="width: 100%; height: 500px;"></div>

(3) JavaScript로 지도 초기화

let map;
let markers = [];
let mode = "nearby"; // 기본 모드는 반경 500m

function initMap() {
  if (navigator.geolocation) {
    navigator.geolocation.getCurrentPosition(position => {
      const userLat = position.coords.latitude;
      const userLng = position.coords.longitude;

      const mapContainer = document.getElementById('map');
      const mapOptions = {
        center: new kakao.maps.LatLng(userLat, userLng),
        level: 5
      };
      map = new kakao.maps.Map(mapContainer, mapOptions);

      // 초기 모드의 북마커 로드
      loadBookmarks(userLat, userLng);

      // 지도 이동 이벤트 리스너 추가
      kakao.maps.event.addListener(map, 'bounds_changed', () => {
        if (mode === "visible") {
          loadVisibleBookmarks();
        }
      });

      // 토글 버튼 클릭 시 모드 변경
      document.getElementById("toggleMode").addEventListener("click", () => {
        mode = mode === "nearby" ? "visible" : "nearby";
        loadBookmarks(userLat, userLng);
      });
    });
  }
}

window.onload = initMap;

4. 북마커 로드 함수

(1) 반경 500m 내 북마커 로드

function loadNearbyBookmarks(userLat, userLng) {
  fetch(`https://your-api.com/bookmarks/nearby?latitude=${userLat}&longitude=${userLng}`)
    .then(response => response.json())
    .then(bookmarks => {
      clearMarkers();
      bookmarks.forEach(bookmark => addMarker(bookmark));
    });
}

(2) 지도 화면 내 북마커 로드

function loadVisibleBookmarks() {
  const bounds = map.getBounds();
  const minLat = bounds.getSouthWest().getLat();
  const maxLat = bounds.getNorthEast().getLat();
  const minLng = bounds.getSouthWest().getLng();
  const maxLng = bounds.getNorthEast().getLng();

  fetch(`https://your-api.com/bookmarks/visible?minLat=${minLat}&maxLat=${maxLat}&minLng=${minLng}&maxLng=${maxLng}`)
    .then(response => response.json())
    .then(bookmarks => {
      clearMarkers();
      bookmarks.forEach(bookmark => addMarker(bookmark));
    });
}

(3) 공통 마커 추가 및 삭제 함수

function addMarker(bookmark) {
  const marker = new kakao.maps.Marker({
    position: new kakao.maps.LatLng(bookmark.latitude, bookmark.longitude),
    map: map
  });
  markers.push(marker);
}

function clearMarkers() {
  markers.forEach(marker => marker.setMap(null));
  markers = [];
}

5. 최종 동작 방식

  1. 사용자가 토글 버튼 클릭 시 mode 변경 (nearby ↔ visible)
  2. nearby 모드
    • 현재 사용자 위치 기준 500m 이내 북마커 표시
  3. visible 모드
    • 현재 지도 화면에 보이는 북마커만 표시
    • 지도 이동 시 자동 업데이트

6. 최적화 및 추가 고려사항

백엔드 캐싱 적용:

  • 요청이 많아지면 Redis 같은 캐시 시스템 활용 가능
    프론트엔드 위치 업데이트 간격 조정:
  • 너무 자주 업데이트하면 배터리 소모 증가 (예: 10초마다 업데이트)
    사용자 위치 기반 마커 클러스터링 적용:
  • 지도에 너무 많은 마커가 표시되면 성능 저하 가능, 카카오맵 클러스터 기능 활용

7. 결론

🚀 NestJS와 카카오맵 API를 활용하여
✅ 반경 500m 내 북마커 표시
✅ 현재 지도 화면 내 북마커 표시
✅ 토글 버튼으로 모드 전환
하는 기능을 깔끔하게 구현할 수 있어!