장소 검색 요소 및 Maps JavaScript API로 장소 찾기

목표

장소 검색 요소를 Google 지도와 통합하여 사용자가 주변 검색 또는 텍스트 검색을 통해 장소를 찾도록 지원하고 관심 장소를 탐색하는 기능을 개선하는 방법을 알아보세요. Place Details Compact Element를 사용하여 애플리케이션에 표시된 장소에 관한 세부정보를 제공합니다.

장소 검색 요소란 무엇인가요?

장소 검색 요소는 Maps JavaScript API의 장소 UI 키트의 일부입니다. 애플리케이션 내에서 장소 검색 결과를 목록 형식으로 직접 렌더링하는 HTML 요소입니다. 이 요소는 Nearby Search 또는 Text Search를 사용하여 찾은 장소를 표시하는 프로세스를 간소화하여 장소 검색을 위한 원활한 사용자 환경을 제공합니다. 사용자가 목록에서 장소를 선택하면 정보 창과 장소 세부정보 요소를 사용하여 지도에 세부정보를 표시할 수 있습니다.

장소 탐색 시각화

다음 이미지는 실제 장소 검색 요소의 예를 보여줍니다. 왼쪽에는 레스토랑 목록이 표시됩니다 (장소 검색 요소). 음식점을 선택하면 지도에 정보 창이 표시되고 지도가 해당 위치를 중심으로 표시됩니다.

이미지

장소 디스커버리 사용 사례

장소 검색 요소를 통합하면 다양한 산업의 여러 애플리케이션을 개선할 수 있습니다.

  • 여행 및 관광: 관광객이 특정 지역의 관광명소, 호텔 또는 특정 유형의 요리를 검색할 수 있습니다.
  • 부동산: 잠재 구매자 또는 임차인이 주변 학교, 슈퍼마켓 또는 대중교통 옵션을 찾을 수 있도록 지원합니다.
  • 물류 및 서비스: 운전자가 전기 자동차 충전소, 주유소 또는 특정 서비스 센터를 찾을 수 있도록 지원합니다.

솔루션 워크플로: 장소 검색 구현

이 섹션에서는 지도에서 장소를 검색하기 위한 장소 검색 요소를 통합하는 단계를 설명하며, 장소 UI 키트와 상호작용하기 위한 코드 스니펫도 포함되어 있습니다. 지도를 초기화하고 Nearby Search 및 Text Search 기능을 모두 구현하는 방법을 알아봅니다. 마지막으로 Place Details 요소를 사용하여 지도에서 핀을 클릭할 때 특정 장소에 관한 자세한 내용을 표시합니다.

기본 요건

다음 문서를 숙지하는 것이 좋습니다.

  • 장소 검색 요소 - 주변 검색 또는 텍스트 검색을 사용하여 장소를 노출하는 데 사용됩니다.
  • 장소 세부정보 요소 - 개별 장소의 세부정보를 표시하는 데 사용됩니다.
  • Maps JavaScript API - 페이지에 지도를 표시하고 Places UI Kit를 가져오는 데 사용됩니다.

프로젝트에서 Maps JavaScript APIPlaces UI Kit를 사용 설정합니다.

시작하기 전에 Maps JavaScript API를 로드하고 필수 라이브러리를 가져왔는지 확인하세요. 또한 이 문서에서는 HTML, CSS, JavaScript를 비롯한 웹 개발에 대한 실무 지식이 있다고 가정합니다.

페이지에 지도 추가

첫 번째 단계는 페이지에 지도를 추가하는 것입니다. 이 지도는 장소 검색 요소 결과를 선택 가능한 핀으로 표시하는 데 사용됩니다.

페이지에 지도를 추가하는 방법에는 두 가지가 있습니다.

  1. gmp-map HTML 웹 구성요소를 사용합니다.
  2. JavaScript 사용

이 페이지의 코드 스니펫은 JavaScript 지도를 사용하여 생성되었습니다.

지도는 사용자가 검색할 위치(예: 호텔)를 중심으로 하거나, 지도를 중앙에 배치하기 위해 사용자의 현재 위치를 묻도록 초기화할 수 있습니다. 이 문서에서는 검색을 고정하기 위해 고정된 위치를 사용합니다.

호텔과 같은 고정된 위치 주변의 장소를 시각화하는 경우 지도에 마커를 배치하여 이 장소를 나타냅니다. 예를 들면 다음과 같습니다.

이미지

지도는 샌프란시스코를 중심으로 표시되며, 파란색 핀은 주변에서 검색 중인 장소를 나타냅니다. PinElement를 사용하여 핀 색상이 맞춤설정되었습니다. 지도 유형 컨트롤이 UI에서 숨겨졌습니다.

장소 검색 요소 설정

이제 장소 검색 요소를 표시하도록 HTML과 CSS를 설정할 수 있습니다. 이 예에서는 지도의 왼쪽 위에 요소를 배치하지만 애플리케이션에 맞게 다양한 레이아웃을 시도하는 것이 좋습니다.

장소 검색 요소는 선언적 접근 방식을 사용합니다. JavaScript에서 완전히 구성하는 대신 기본 <gmp-place-search> 구성요소 내에 요청 요소(예: <gmp-place-nearby-search-request>)를 중첩하여 HTML에서 직접 검색 유형을 정의합니다.

HTML 코드 내에서 <gmp-place-search> 요소를 초기화합니다. selectable 속성을 사용하여 결과에서 클릭 이벤트를 사용 설정합니다. 내부에 <gmp-place-nearby-search-request>를 추가하여 이 요소가 근처 검색에 사용됨을 지정합니다.

<gmp-place-search selectable>
  <gmp-place-nearby-search-request>
  </gmp-place-nearby-search-request>
</gmp-place-search>

초기 검색을 실행하고 결과를 표시하기 위해 JavaScript를 사용하여 중첩된 요청 요소에 대한 참조를 가져오고 속성을 설정합니다. 이전 단계의 마커 위치를 중심점으로 사용하여 locationRestriction로 사용할 원을 초기화합니다. 그런 다음 요청 요소에서 locationRestrictionincludedPrimaryTypes 속성을 설정하여 검색을 트리거합니다.

이 코드 스니펫은 다음과 같습니다.

// Get references to the Place Search and its nested request element
const placeSearchElement = document.querySelector("gmp-place-search");
const placeSearchRequestElement = document.querySelector("gmp-place-nearby-search-request");

// Define the location restriction for the search
const circleRestriction = new Circle({
    center: marker.position,
    radius: 500
});

// Set the properties on the request element to perform an initial search for restaurants.
placeSearchRequestElement.locationRestriction = circleRestriction;
placeSearchRequestElement.includedPrimaryTypes = ['restaurant'];

이 단계에서 애플리케이션이 어떻게 표시될 수 있는지 보여주는 예는 다음과 같습니다.

이미지

장소 검색 요소를 사용하면 다음 두 가지 검색 옵션을 사용할 수 있습니다.

  • <gmp-place-nearby-search-request> - 장소 유형을 사용하여 장소 주변 검색의 검색 결과를 렌더링합니다.
  • <gmp-place-text-search-request> - '샌프란시스코의 스시'와 같은 자유 형식 텍스트 입력을 사용하여 장소 텍스트 검색의 검색 결과를 렌더링합니다.

<gmp-place-search> 내부에 중첩된 요소입니다. 그런 다음 JavaScript를 사용하여 중첩된 요청 요소의 속성을 설정하여 검색을 트리거합니다.

이 섹션에서는 두 메서드를 모두 구현하는 방법을 설명합니다.

이미지

사용자가 주변 검색을 실행할 수 있도록 하려면 먼저 장소 유형을 선택할 수 있는 UI 요소가 필요합니다. 애플리케이션에 가장 적합한 선택 방법을 선택합니다(예: 장소 유형 선택으로 채워진 드롭다운 목록).

사용 사례와 관련된 유형의 하위 집합을 선택하는 것이 좋습니다. 예를 들어 호텔 근처의 관광 명소를 보여주는 애플리케이션을 개발하는 경우 bakery, coffee_shop, museum, restaurant, tourist_attraction을 선택할 수 있습니다.

HTML에는 <gmp-place-nearby-search-request>이 중첩된 <gmp-place-search> 요소가 포함되어야 합니다.

<gmp-place-search selectable>
  <gmp-place-nearby-search-request>
  </gmp-place-nearby-search-request>
</gmp-place-search>

다음으로 장소 유형 선택기에서 change 이벤트의 JavaScript 리스너를 만듭니다. 이 리스너는 <gmp-place-nearby-search-request> 요소의 속성을 업데이트하는 함수를 호출하며, 이 함수는 자동으로 새 검색을 트리거하고 목록을 업데이트합니다.

// Get a reference to the nested request element
const placeSearchRequestElement = document.querySelector('gmp-place-nearby-search-request');

// Function to update the place search based on the selected type
function updatePlaceList() {
    const selectedType = placeTypeSelect.value;
    if (!selectedType) {
        // If no type is selected, don't perform a search.
        // You could optionally hide the list or clear previous results here.
        placeSearchElement.style.display = 'none';
        return;
    }
    placeSearchElement.style.display = 'block';

    // Set properties on the request element to trigger a new search
    placeSearchRequestElement.locationRestriction = searchCircle;
    placeSearchRequestElement.maxResultCount = 8;
    placeSearchRequestElement.includedPrimaryTypes = [selectedType];
}

설정 단계의 동일한 searchCirclelocationRestriction에 사용됩니다. includedPrimaryTypes 속성은 사용자의 선택에서 가져온 값으로 설정됩니다. 결과 수를 제한하기 위해 선택사항인 maxResultCount도 설정됩니다.

이미지

텍스트 검색을 사용 설정하려면 HTML 구성을 변경해야 합니다. 근처 검색 요청 대신 <gmp-place-text-search-request> 요소를 중첩합니다.

<gmp-place-search selectable>
  <gmp-place-text-search-request>
  </gmp-place-text-search-request>
</gmp-place-search>

UI에 텍스트 입력과 검색 버튼을 추가합니다. 버튼의 click 이벤트에 대한 JavaScript 리스너를 만듭니다. 이벤트 핸들러는 사용자의 입력을 가져와 검색을 실행하도록 <gmp-place-text-search-request> 요소의 속성을 업데이트합니다.

// Get a reference to the text search request element
const placeTextSearchRequestElement = document.querySelector('gmp-place-text-search-request');
const textSearchInput = document.getElementById('textSearchInput');
const textSearchButton = document.getElementById('textSearchButton');

textSearchButton.addEventListener('click', performTextSearch);

function performTextSearch() {
    const query = textSearchInput.value.trim();
    if (!query) {
        console.log("Search query is empty.");
        return;
    }
    // Set properties on the request element to trigger a new search
    placeTextSearchRequestElement.textQuery = query;
    placeTextSearchRequestElement.locationBias = map.getBounds();
    placeTextSearchRequestElement.maxResultCount = 8;
}

여기서는 사용자의 입력으로 textQuery 속성을 설정합니다. 또한 현재 지도 경계를 사용하는 locationBias도 제공합니다. 이는 API에 해당 영역 내의 결과를 엄격하게 제한하지 않고 선호하도록 지시합니다. 선택사항인 maxResultCount는 반환되는 결과 수를 제한합니다.

장소 핀 및 세부정보 표시

이제 애플리케이션이 장소 검색을 실행하고 요소를 채울 수 있습니다. 다음 단계에서는 다음을 통해 기능을 개선합니다.

  • 장소 검색 요소에 입력된 각 장소에 대해 지도에 핀을 표시합니다.
  • 사용자가 핀 또는 장소 검색 요소 내의 장소를 클릭하여 특정 장소에 대한 세부정보를 표시할 수 있도록 허용합니다.

이 단계의 원리는 애플리케이션이 근처 검색을 사용하는지 텍스트 검색을 사용하는지에 관계없이 동일합니다.

먼저 JavaScript 코드에 전역 변수를 추가하여 장소 마커를 저장합니다. 이렇게 하면 검색이 변경될 때 이를 삭제하고 클릭 이벤트를 처리할 수 있습니다.

let markers = {};

지도에 마커를 추가하는 함수를 만듭니다. 이 함수는 새 검색 결과가 로드될 때마다 호출됩니다. 다음과 같은 작업을 실행합니다.

  • 지도에서 기존 장소 마커를 삭제합니다.
  • 장소 검색 요소의 결과를 반복하고 각 결과에 마커를 추가합니다.
  • 새 마커가 모두 표시되도록 지도의 경계를 조정합니다.

검색 결과가 제공되는 시점을 수신하려면 <gmp-place-search> 요소에 gmp-load 이벤트 리스너를 추가합니다. 이 이벤트는 검색이 완료되고 결과가 렌더링된 후에 발생합니다.

검색 함수 내에 리스너를 추가합니다 (예: updatePlaceList)를 사용하고 { once: true } 옵션을 사용하여 현재 검색 결과에 대해서만 트리거되도록 합니다.

// In your search function, after setting the request properties:
placeSearchElement.addEventListener('gmp-load', addMarkers, { once: true });

addMarkers 함수는 다음과 같습니다.

async function addMarkers() {
    const { LatLngBounds } = await google.maps.importLibrary("core");
    const bounds = new LatLngBounds();

    if (placeSearchElement.places.length > 0) {
        // Remove existing markers
        for (const m in markers) {
            markers[m].map = null;
        }
        markers = {};

        // Loop through each place from the search results
        // and add a marker for each one.
        for (const place of placeSearchElement.places) {
            const marker = new google.maps.marker.AdvancedMarkerElement({
                map: map,
                position: place.location,
            });

            markers[place.id] = marker;
            bounds.extend(place.location);
            marker.collisionBehavior = google.maps.CollisionBehavior.REQUIRED_AND_HIDES_OPTIONAL;

            // Add a click listener for each marker.
            marker.addListener('gmp-click', (event) => {
                // The main logic for showing details will go here.
            });
        }
        // Position the map to display all markers.
        map.setCenter(bounds.getCenter());
        map.fitBounds(bounds);
    }
}

이 단계를 완료하면 애플리케이션이 다음과 같이 표시되며, 장소 검색 요소에서 반환된 각 장소의 마커를 표시할 수 있습니다.

이미지

이제 지도에 마커가 있으므로 마지막 단계는 마커와 요소 클릭 이벤트를 처리하여 장소 세부정보 요소에서 제공하는 장소 세부정보가 포함된 정보 창을 표시하는 것입니다. 이 예에서는 장소 세부정보 컴팩트 요소를 사용합니다.

다음과 같이 코드에 장소 세부정보 콤팩트 요소 HTML을 추가합니다.

<gmp-place-details-compact orientation="vertical" style="display: none;">
    <gmp-place-all-content></gmp-place-all-content>
    <gmp-place-attribution light-scheme-color="gray" dark-scheme-color="white"></gmp-place-attribution>
</gmp-place-details-compact>

style이(가) display: none로 설정되어 있으며 필요할 때까지 표시되지 않습니다. gmp-place-all-content는 모든 요소 콘텐츠를 렌더링하기 위해 전달됩니다. 렌더링할 콘텐츠를 선택하려면 장소 세부정보 컴팩트 요소 문서를 참고하세요.

JavaScript에서 장소 세부정보 콤팩트 요소를 참조하는 전역 변수를 만들고 초기화 코드에서 이를 채웁니다. 예를 들면 다음과 같습니다.

let placeDetailsElement;
...
placeDetailsElement = document.querySelector('gmp-place-details-compact');

addMarkers 함수 내에서 각 마커에 gmp-click 이벤트 리스너를 추가하고 현재 마커의 장소 ID를 전달하여 장소 세부정보를 표시하도록 장소 세부정보 컴팩트 요소를 구성합니다.

이 작업이 완료되면 정보 창이 열려 마커에 고정된 장소 세부정보 콤팩트 요소를 표시합니다.

마지막으로 지도가 선택한 장소의 표시 영역에 배치되어 표시됩니다.

async function addMarkers() {
          ...
            marker.addListener('gmp-click', (event) => {
                //Set up Place Details Compact Widget
                placeDetailsElement.style.display = "block";
                // Remove any existing place request element
                const existingPlaceRequest = placeDetailsElement.querySelector('gmp-place-details-place-request');
                if (existingPlaceRequest) {
                    existingPlaceRequest.remove();
                }
                // Create and configure the new place request element
                const placeRequestElement = new google.maps.places.PlaceDetailsPlaceRequestElement({ place: place.id });
                // Prepend the new place request element to the main widget
                placeDetailsElement.prepend(placeRequestElement);
                if (infoWindow.isOpen) {
                    infoWindow.close();
                }
                infoWindow.setOptions({
                    content: placeDetailsElement
                });
                infoWindow.open({
                    anchor: marker,
                    map: map
                });
                // Position the map to show the selected place
                placeDetailsElement.addEventListener('gmp-load', () => {
                    map.fitBounds(place.viewport, { top: 500, left: 400 });
                });
            });
          ...
        });
    }
}

사용자가 장소 목록 요소에서 장소를 클릭하여 장소 세부정보 콤팩트 요소를 표시할 수 있도록 하려면 configureFromSearchNearbyRequest 호출 후 JavaScript 코드에 다음을 추가합니다.

placeSearchElement.addEventListener("gmp-select", ({ place }) => {
    if (markers[place.id]) {
        markers[place.id].click();
    }
});

이 단계를 완료하면 애플리케이션에서 Nearby Search 또는 Text Search를 사용하여 장소 목록 요소를 채울 수 있습니다. 이 작업의 결과로 지도에 핀이 표시되며, 핀이나 장소 목록 요소의 장소를 클릭하면 장소 세부정보 콤팩트 요소에서 제공하는 장소 세부정보가 포함된 정보 창이 표시됩니다.

애플리케이션은 다음과 같이 표시됩니다.

이미지

결론

장소 검색 요소와 장소 세부정보 컴팩트 요소를 결합하면 Google Maps Platform 애플리케이션에 다양한 장소 검색 기능을 간소화된 방식으로 추가할 수 있습니다.

지금 장소 UI 키트를 사용해 보세요. 사용자가 주변 지역 검색과 텍스트 검색을 모두 사용하여 장소를 찾고 탐색할 수 있으며, 풍부한 장소 세부정보를 표시하여 장소 검색 사용 사례와의 상호작용을 개선할 수 있습니다.

참여자

헨리크 밸브 | DevX 엔지니어