Place Autocomplete Data API

Place Autocomplete Data API를 사용하면 프로그래매틱 방식으로 장소 예측을 가져와 자동 완성 위젯으로 가능한 것보다 더 세밀하게 제어할 수 있는 맞춤 자동 완성 환경을 만들 수 있습니다. 이 가이드에서는 Place Autocomplete Data API를 사용하여 사용자 쿼리를 기반으로 자동 완성 요청을 실행하는 방법을 알아봅니다.

다음 예는 간단한 자동 완성 통합을 보여줍니다. 검색어를 입력한 다음 을 클릭하여 원하는 결과를 선택합니다.

Autocomplete 요청

자동 완성 요청은 쿼리 입력 문자열을 사용하고 장소 예상 검색어 목록을 반환합니다. 자동 완성 요청을 하려면 fetchAutocompleteSuggestions()를 호출하고 필요한 속성이 포함된 요청을 전달합니다. input 속성에는 검색할 문자열이 포함됩니다. 일반적인 애플리케이션에서는 사용자가 쿼리를 입력할 때 이 값이 업데이트됩니다. 요청에는 결제 목적으로 사용되는 sessionToken가 포함되어야 합니다.

다음 스니펫은 요청 본문을 만들고 세션 토큰을 추가한 다음 fetchAutocompleteSuggestions()를 호출하여 PlacePrediction 목록을 가져오는 방법을 보여줍니다.

// Add an initial request body.
let request = {
  input: "Tadi",
  locationRestriction: {
    west: -122.44,
    north: 37.8,
    east: -122.39,
    south: 37.78,
  },
  origin: { lat: 37.7893, lng: -122.4039 },
  includedPrimaryTypes: ["restaurant"],
  language: "en-US",
  region: "us",
};
// Create a session token.
const token = new AutocompleteSessionToken();

// Add the token to the request.
// @ts-ignore
request.sessionToken = token;

자동 완성 예상 검색어 제한

기본적으로 Place Autocomplete는 사용자 위치 근처의 예상 검색어에 편중된 모든 장소 유형을 표시하며 사용자가 선택한 장소의 사용 가능한 모든 데이터 필드를 가져옵니다. 결과를 제한하거나 상세 검색하여 더 관련성 높은 예상 검색어를 표시하려면 Place Autocomplete 옵션을 설정하세요.

결과를 제한하면 자동 완성 위젯이 제한 지역 밖의 모든 결과를 무시합니다. 일반적인 방법은 결과를 지도 경계로 제한하는 것입니다. 결과를 상세 검색하면 자동 완성 위젯이 지정된 지역 내 결과를 표시하지만 일부 일치 항목이 해당 지역을 벗어날 수 있습니다.

origin 속성을 사용하여 목적지까지의 지오데시적 거리를 계산할 출발점을 지정합니다. 이 값을 생략하면 거리가 반환되지 않습니다.

includedPrimaryTypes 속성을 사용하여 최대 5개의 장소 유형을 지정합니다. 유형을 지정하지 않으면 모든 유형의 장소가 반환됩니다.

API 참조 보기

장소 세부정보 가져오기

장소 예측 결과에서 Place 객체를 반환하려면 먼저 toPlace()를 호출한 다음 결과 Place 객체에서 fetchFields()를 호출합니다 (장소 예측의 세션 ID가 자동으로 포함됨). fetchFields()를 호출하면 자동 완성 세션이 종료됩니다.

let place = suggestions[0].placePrediction.toPlace(); // Get first predicted place.

await place.fetchFields({
  fields: ["displayName", "formattedAddress"],
});

const placeInfo = document.getElementById("prediction");

placeInfo.textContent =
  "First predicted place: " +
  place.displayName +
  ": " +
  place.formattedAddress;

세션 토큰

세션 토큰은 사용자 자동 완성 검색의 쿼리 및 선택 단계를 결제 목적의 개별 세션으로 그룹화합니다. 세션은 사용자가 입력을 시작하면 시작됩니다. 세션은 사용자가 장소를 선택하고 Place Details 호출이 이루어지면 종료됩니다.

새 세션 토큰을 만들고 요청에 추가하려면 AutocompleteSessionToken 인스턴스를 만든 다음 다음 스니펫과 같이 토큰을 사용하도록 요청의 sessionToken 속성을 설정합니다.

// Create a session token.
const token = new AutocompleteSessionToken();

// Add the token to the request.
// @ts-ignore
request.sessionToken = token;

fetchFields()가 호출되면 세션이 종료됩니다. Place 인스턴스를 만든 후에는 세션 토큰이 자동으로 처리되므로 fetchFields()에 세션 토큰을 전달할 필요가 없습니다.

await place.fetchFields({
  fields: ["displayName", "formattedAddress"],
});
await place.fetchFields({
    fields: ['displayName'],
  });

AutocompleteSessionToken의 새 인스턴스를 만들어 다음 세션의 세션 토큰을 만듭니다.

세션 토큰 권장사항:

  • 모든 Place Autocomplete 호출에 세션 토큰을 사용합니다.
  • 각 세션에 대해 새 토큰을 생성합니다.
  • 새 세션마다 고유한 세션 토큰을 전달합니다. 두 개 이상의 세션에 동일한 토큰을 사용하면 각 요청에 대해 개별적으로 요금이 청구됩니다.

원하는 경우 요청에서 자동 완성 세션 토큰을 생략할 수 있습니다. 세션 토큰이 생략되면 각 요청에 대해 별도로 요금이 청구되어 자동 완성 - 요청별 SKU가 트리거됩니다. 세션 토큰을 재사용하면 세션이 유효하지 않은 것으로 간주되어 세션 토큰이 제공되지 않은 것처럼 요청에 대해 요금이 청구됩니다.

사용자가 검색어를 입력하면 문자별이 아닌 몇 번의 키 입력마다 자동 완성 요청이 호출되고 가능한 결과 목록이 반환됩니다. 사용자가 결과 목록에서 항목을 선택하면 선택사항이 요청으로 집계되고 검색 중에 이루어진 모든 요청이 번들로 묶여 단일 요청으로 집계됩니다. 사용자가 장소를 선택하면 검색어는 무료로 사용할 수 있으며 장소 데이터 요청에 대해서만 요금이 청구됩니다. 사용자가 세션이 시작된 후 몇 분 이내에 선택하지 않으면 검색어 요금만 청구됩니다.

앱의 관점에서 이벤트 흐름은 다음과 같습니다.

  1. 사용자가 '프랑스 파리'를 검색하기 위해 검색어를 입력하기 시작합니다.
  2. 사용자 입력을 감지하면 앱은 새 세션 토큰 '토큰 A'를 만듭니다.
  3. 사용자가 입력하면 API는 몇 자마다 자동 완성 요청을 실행하여 각 문자에 대해 가능한 결과의 새 목록을 표시합니다.
    "P"
    "Par"
    "Paris,"
    "Paris, Fr"
  4. 사용자가 선택하면 다음과 같이 처리됩니다.
    • 쿼리에서 발생한 모든 요청은 그룹화되어 '토큰 A'로 표시된 세션에 단일 요청으로 추가됩니다.
    • 사용자의 선택사항은 장소 세부정보 요청으로 집계되고 '토큰 A'로 표시된 세션에 추가됩니다.
  5. 세션이 종료되고 앱에서 '토큰 A'를 삭제합니다.
세션 요금 청구 방식 알아보기

예시 코드 작성

이 섹션에는 Place Autocomplete Data API를 사용하는 방법을 보여주는 완전한 예가 포함되어 있습니다 .

장소 자동 완성 예상 검색어

다음 예는 입력 'Tadi'에 대해 fetchAutocompleteSuggestions()를 호출한 다음 첫 번째 예측 결과에 대해 toPlace()를 호출한 후 fetchFields()를 호출하여 장소 세부정보를 가져오는 방법을 보여줍니다.

TypeScript

/**
 * Demonstrates making a single request for Place predictions, then requests Place Details for the first result.
 */
async function init() {
    // @ts-ignore
    const { Place, AutocompleteSessionToken, AutocompleteSuggestion } = await google.maps.importLibrary("places") as google.maps.PlacesLibrary;

    // Add an initial request body.
    let request = {
        input: "Tadi",
        locationRestriction: { west: -122.44, north: 37.8, east: -122.39, south: 37.78 },
        origin: { lat: 37.7893, lng: -122.4039 },
        includedPrimaryTypes: ["restaurant"],
        language: "en-US",
        region: "us",
    };

    // Create a session token.
    const token = new AutocompleteSessionToken();
    // Add the token to the request.
    // @ts-ignore
    request.sessionToken = token;
    // Fetch autocomplete suggestions.
    const { suggestions } = await AutocompleteSuggestion.fetchAutocompleteSuggestions(request);

    const title = document.getElementById('title') as HTMLElement;
    title.appendChild(document.createTextNode('Query predictions for "' + request.input + '":'));

    for (let suggestion of suggestions) {
        const placePrediction = suggestion.placePrediction;

        // Create a new list element.
        const listItem = document.createElement('li');
        const resultsElement = document.getElementById("results") as HTMLElement;
        listItem.appendChild(document.createTextNode(placePrediction.text.toString()));
        resultsElement.appendChild(listItem);
    }

    let place = suggestions[0].placePrediction.toPlace(); // Get first predicted place.
    await place.fetchFields({
        fields: ['displayName', 'formattedAddress'],
    });

    const placeInfo = document.getElementById("prediction") as HTMLElement;
    placeInfo.textContent = 'First predicted place: ' + place.displayName + ': ' + place.formattedAddress;

}

init();

JavaScript

/**
 * Demonstrates making a single request for Place predictions, then requests Place Details for the first result.
 */
async function init() {
  // @ts-ignore
  const { Place, AutocompleteSessionToken, AutocompleteSuggestion } =
    await google.maps.importLibrary("places");
  // Add an initial request body.
  let request = {
    input: "Tadi",
    locationRestriction: {
      west: -122.44,
      north: 37.8,
      east: -122.39,
      south: 37.78,
    },
    origin: { lat: 37.7893, lng: -122.4039 },
    includedPrimaryTypes: ["restaurant"],
    language: "en-US",
    region: "us",
  };
  // Create a session token.
  const token = new AutocompleteSessionToken();

  // Add the token to the request.
  // @ts-ignore
  request.sessionToken = token;

  // Fetch autocomplete suggestions.
  const { suggestions } =
    await AutocompleteSuggestion.fetchAutocompleteSuggestions(request);
  const title = document.getElementById("title");

  title.appendChild(
    document.createTextNode('Query predictions for "' + request.input + '":'),
  );

  for (let suggestion of suggestions) {
    const placePrediction = suggestion.placePrediction;
    // Create a new list element.
    const listItem = document.createElement("li");
    const resultsElement = document.getElementById("results");

    listItem.appendChild(
      document.createTextNode(placePrediction.text.toString()),
    );
    resultsElement.appendChild(listItem);
  }

  let place = suggestions[0].placePrediction.toPlace(); // Get first predicted place.

  await place.fetchFields({
    fields: ["displayName", "formattedAddress"],
  });

  const placeInfo = document.getElementById("prediction");

  placeInfo.textContent =
    "First predicted place: " +
    place.displayName +
    ": " +
    place.formattedAddress;
}

init();

CSS

/* 
 * Always set the map height explicitly to define the size of the div element
 * that contains the map. 
 */
#map {
  height: 100%;
}

/* 
 * Optional: Makes the sample page fill the window. 
 */
html,
body {
  height: 100%;
  margin: 0;
  padding: 0;
}

HTML

<html>
  <head>
    <title>Place Autocomplete Data API Predictions</title>

    <link rel="stylesheet" type="text/css" href="./style.css" />
    <script type="module" src="./index.js"></script>
  </head>
  <body>
    <div id="title"></div>
    <ul id="results"></ul>
    <p><span id="prediction"></span></p>
    <img
      class="powered-by-google"
      src="https://storage.googleapis.com/geo-devrel-public-buckets/powered_by_google_on_white.png"
      alt="Powered by Google"
    />

    <!-- prettier-ignore -->
    <script>(g=>{var h,a,k,p="The Google Maps JavaScript API",c="google",l="importLibrary",q="__ib__",m=document,b=window;b=b[c]||(b[c]={});var d=b.maps||(b.maps={}),r=new Set,e=new URLSearchParams,u=()=>h||(h=new Promise(async(f,n)=>{await (a=m.createElement("script"));e.set("libraries",[...r]+"");for(k in g)e.set(k.replace(/[A-Z]/g,t=>"_"+t[0].toLowerCase()),g[k]);e.set("callback",c+".maps."+q);a.src=`https://maps.${c}apis.com/maps/api/js?`+e;d[q]=f;a.onerror=()=>h=n(Error(p+" could not load."));a.nonce=m.querySelector("script[nonce]")?.nonce||"";m.head.append(a)}));d[l]?console.warn(p+" only loads once. Ignoring:",g):d[l]=(f,...n)=>r.add(f)&&u().then(()=>d[l](f,...n))})
        ({key: "AIzaSyB41DRUbKWJHPxaFjMAwdrzWzbVKartNGg", v: "weekly"});</script>
  </body>
</html>

샘플 사용해 보기

세션을 사용한 Place Autocomplete 예상 검색어

이 예에서는 사용자 쿼리를 기반으로 fetchAutocompleteSuggestions()를 호출하고, 응답으로 예상 장소 목록을 표시하고, 마지막으로 선택한 장소의 장소 세부정보를 가져오는 방법을 보여줍니다. 또한 이 예에서는 세션 토큰을 사용하여 사용자 쿼리를 최종 장소 세부정보 요청과 그룹화하는 방법을 보여줍니다.

TypeScript

let title;
let results;
let input;
let token;

// Add an initial request body.
let request = {
    input: "",
    locationRestriction: { west: -122.44, north: 37.8, east: -122.39, south: 37.78 },
    origin: { lat: 37.7893, lng: -122.4039 },
    includedPrimaryTypes: ["restaurant"],
    language: "en-US",
    region: "us",
};

async function init() {
    token = new google.maps.places.AutocompleteSessionToken();

    title = document.getElementById('title');
    results = document.getElementById('results');
    input = document.querySelector("input");
    input.addEventListener("input", makeAcRequest);
    request = refreshToken(request) as any;
}

async function makeAcRequest(input) {
    // Reset elements and exit if an empty string is received.
    if (input.target.value == '') {
        title.innerText = '';
        results.replaceChildren();
        return;
    }

    // Add the latest char sequence to the request.
    request.input = input.target.value;

    // Fetch autocomplete suggestions and show them in a list.
    // @ts-ignore
    const { suggestions } = await google.maps.places.AutocompleteSuggestion.fetchAutocompleteSuggestions(request);

    title.innerText = 'Query predictions for "' + request.input + '"';

    // Clear the list first.
    results.replaceChildren();

    for (const suggestion of suggestions) {
        const placePrediction = suggestion.placePrediction;

        // Create a link for the place, add an event handler to fetch the place.
        const a = document.createElement('a');
        a.addEventListener('click', () => {
            onPlaceSelected(placePrediction!.toPlace());
        });
        a.innerText = placePrediction!.text.toString();

        // Create a new list element.
        const li = document.createElement('li');
        li.appendChild(a);
        results.appendChild(li);
    }
}

// Event handler for clicking on a suggested place.
async function onPlaceSelected(place) {
    await place.fetchFields({
        fields: ['displayName', 'formattedAddress'],
    });
    let placeText = document.createTextNode(place.displayName + ': ' + place.formattedAddress);
    results.replaceChildren(placeText);
    title.innerText = 'Selected Place:';
    input.value = '';
    refreshToken(request);
}

// Helper function to refresh the session token.
async function refreshToken(request) {
    // Create a new session token and add it to the request.
    token = new google.maps.places.AutocompleteSessionToken();
    request.sessionToken = token;
    return request;
}

declare global {
    interface Window {
      init: () => void;
    }
  }
  window.init = init;

JavaScript

let title;
let results;
let input;
let token;
// Add an initial request body.
let request = {
  input: "",
  locationRestriction: {
    west: -122.44,
    north: 37.8,
    east: -122.39,
    south: 37.78,
  },
  origin: { lat: 37.7893, lng: -122.4039 },
  includedPrimaryTypes: ["restaurant"],
  language: "en-US",
  region: "us",
};

async function init() {
  token = new google.maps.places.AutocompleteSessionToken();
  title = document.getElementById("title");
  results = document.getElementById("results");
  input = document.querySelector("input");
  input.addEventListener("input", makeAcRequest);
  request = refreshToken(request);
}

async function makeAcRequest(input) {
  // Reset elements and exit if an empty string is received.
  if (input.target.value == "") {
    title.innerText = "";
    results.replaceChildren();
    return;
  }

  // Add the latest char sequence to the request.
  request.input = input.target.value;

  // Fetch autocomplete suggestions and show them in a list.
  // @ts-ignore
  const { suggestions } =
    await google.maps.places.AutocompleteSuggestion.fetchAutocompleteSuggestions(
      request,
    );

  title.innerText = 'Query predictions for "' + request.input + '"';
  // Clear the list first.
  results.replaceChildren();

  for (const suggestion of suggestions) {
    const placePrediction = suggestion.placePrediction;
    // Create a link for the place, add an event handler to fetch the place.
    const a = document.createElement("a");

    a.addEventListener("click", () => {
      onPlaceSelected(placePrediction.toPlace());
    });
    a.innerText = placePrediction.text.toString();

    // Create a new list element.
    const li = document.createElement("li");

    li.appendChild(a);
    results.appendChild(li);
  }
}

// Event handler for clicking on a suggested place.
async function onPlaceSelected(place) {
  await place.fetchFields({
    fields: ["displayName", "formattedAddress"],
  });

  let placeText = document.createTextNode(
    place.displayName + ": " + place.formattedAddress,
  );

  results.replaceChildren(placeText);
  title.innerText = "Selected Place:";
  input.value = "";
  refreshToken(request);
}

// Helper function to refresh the session token.
async function refreshToken(request) {
  // Create a new session token and add it to the request.
  token = new google.maps.places.AutocompleteSessionToken();
  request.sessionToken = token;
  return request;
}

window.init = init;

CSS

/* 
 * Always set the map height explicitly to define the size of the div element
 * that contains the map. 
 */
#map {
  height: 100%;
}

/* 
 * Optional: Makes the sample page fill the window. 
 */
html,
body {
  height: 100%;
  margin: 0;
  padding: 0;
}

a {
  cursor: pointer;
  text-decoration: underline;
  color: blue;
}

input {
  width: 300px;
}

HTML

<html>
  <head>
    <title>Place Autocomplete Data API Session</title>

    <link rel="stylesheet" type="text/css" href="./style.css" />
    <script type="module" src="./index.js"></script>
  </head>
  <body>
    <input id="input" type="text" placeholder="Search for a place..." />
    <div id="title"></div>
    <ul id="results"></ul>
    <img
      class="powered-by-google"
      src="https://storage.googleapis.com/geo-devrel-public-buckets/powered_by_google_on_white.png"
      alt="Powered by Google"
    />

    <!-- 
      The `defer` attribute causes the script to execute after the full HTML
      document has been parsed. For non-blocking uses, avoiding race conditions,
      and consistent behavior across browsers, consider loading using Promises. See
      https://developers.google.com/maps/documentation/javascript/load-maps-js-api
      for more information.
      -->
    <script
      src="https://maps.googleapis.com/maps/api/js?key=AIzaSyB41DRUbKWJHPxaFjMAwdrzWzbVKartNGg&callback=init&libraries=places&v=weekly"
      defer
    ></script>
  </body>
</html>

샘플 사용해 보기