여행 플래너 서비스 - Google Places API

Yunsung Lee·2024년 12월 3일

server.js

require("dotenv").config();

const express = require("express");
const axios = require("axios");
const app = express();
const PORT = process.env.PORT || 3000;
const API_KEY = process.env.API_KEY;

app.set("view engine", "ejs");

app.get("/", (req, res) => {
  res.render("index", { places: [], message: null });
});

app.get("/search-place", async (req, res) => {
  const query = req.query.query;
  const googlePlacesUrl = `https://maps.googleapis.com/maps/api/place/textsearch/json?query=${query}&region=kr&language=ko&key=${API_KEY}`;

  try {
    const response = await axios.get(googlePlacesUrl);
    console.log("API Response:", response.data);

    // API 응답이 성공적이고 결과가 있을 때
    if (response.data.status === "OK" && response.data.results.length > 0) {
      // 장소의 상세 정보 요청
      const placeDetails = await getPlaceDetails(response.data.results);

      res.render("index", { places: placeDetails, message: null });
    } else {
      res.render("index", { places: [], message: "검색 결과가 없습니다." });
    }
  } catch (error) {
    console.error("API Error:", error);
    res.render("index", {
      places: [],
      message: "검색 중 오류가 발생했습니다.",
    });
  }
});

// 장소의 상세 정보를 비동기적으로 가져오는 함수
const getPlaceDetails = async (places) => {
  try {
    const placeDetailsPromises = places.map(
      (place) =>
        axios
          .get(
            `https://maps.googleapis.com/maps/api/place/details/json?place_id=${place.place_id}&language=ko&key=${API_KEY}`
          )
          .then((response) => response.data.result)
    );
    return await Promise.all(placeDetailsPromises);
  } catch (error) {
    console.error("Failed to fetch place details:", error);
    return [];
  }
};

app.listen(PORT, () => {
  console.log(`Server running on http://localhost:${PORT}`);
});

index.ejs

<!DOCTYPE html>
<html lang="ko">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Place Search</title>
  </head>
  <body>
    <h1>장소 검색</h1>
    <form action="/search-place" method="GET">
      <input
        type="text"
        name="query"
        placeholder="장소를 입력하세요"
        required
      />
      <button type="submit">검색</button>
    </form>

    <% if (message) { %>
    <p><%= message %></p>
    <% } %> <% if (places && places.length > 0) { %>
    <h2>검색 결과</h2>
    <ul>
      <% places.forEach((place) => { %>
      <li>
        <strong><%= place.name %></strong><br />
        <p><%= place.formatted_address %></p>
      </li>
      <% }); %>
    </ul>
    <% } %>
  </body>
</html>

<h1>장소 검색</h1>
    <form action="/search-place" method="GET">
      <input
        type="text"
        name="query"
        placeholder="장소를 입력하세요"
        required
      />
      <button type="submit">검색</button>
    </form>

장소 입력을 받는 input

여기서 작성된 input value는 서버("/search-place")로 넘어감


if (response.data.status === "OK" && response.data.results.length > 0)

"OK" 는 요청이 성공적으로 처리되었고, 결과가 정상적으로 반환되었을 때다

response.data.results.length > 0 여기는 누가봐도 데이터가 존재하는지 체크하는 부분

이 두 조건을 모두 충족했을 때 조건문이 실행된다


조건문 내부

const placeDetails = await getPlaceDetails(response.data.results);

상세 정보 요청 함수의 return 값을 변수에 담는다

getPlaceDetails() 를 들여다보자


const getPlaceDetails = async (places) => {
  try {
    const placeDetailsPromises = places.map(
      (place) =>
        axios.get(
            `https://maps.googleapis.com/maps/api/place/details/json?place_id=${place.place_id}&language=ko&key=${API_KEY}`
          )
          .then((response) => response.data.result)
    );
    return await Promise.all(placeDetailsPromises);
  } catch (error) {
    console.error("Failed to fetch place details:", error);
    return [];
  }
};

places 파라미터로 Object 뭉탱이(response.data.results)를 받는다

placeDetailsPromises 변수에 요소들의 세부 정보 mapping 한다

mapping이 끝나면 placeDetailsPromisesreturn 한다


res.render("index", { places: placeDetails, message: null });

서버에서 작업이 끝난 뭉탱이를 클라이언트로 전송한다


<% if (message) { %>
    <p><%= message %></p>
    <% } %> <% if (places && places.length > 0) { %>
    <h2>검색 결과</h2>
    <ul>
      <% places.forEach((place) => { %>
      <li>
        <strong><%= place.name %></strong><br />
        <p><%= place.formatted_address %></p>
      </li>
      <% }); %>
    </ul>
<% } %>

클라이언트에서 EJS 문법을 이용해서 서버에서 받은 값들을 화면에 표시한다

정상적으로 데이터가 넘어왔다면 message 는 빈 상태일 것

places 가 제대로 넘어왔는지 if문으로 검사를 거친 후

placesforeach 반복문을 적용해서 모든 값을 순차적으로 화면에 뿌려준다


하루 종일 한 결과물이 이거다

이제 장소 검색 단 하나 구현했다

내일은 소요 시간을 구현해야 한다

다 하고나니 문제가 생겼다...

현재 한국에서 구글맵은 대중교통 경로만 지원하고 나머지는 지원하지 않는다고 한다

아 다시 만들어야되네

0개의 댓글