Use Cases (Diperbarui: 2/6/2026)

Integrasi Google Maps dengan Claude Code: panduan praktis Next.js

Gunakan Claude Code untuk Google Maps: Advanced Markers, geocoding, toko, Mapbox, dan jebakan produksi.

Integrasi Google Maps dengan Claude Code: panduan praktis Next.js

Rancang fitur peta sebelum menampilkannya

Claude Code bisa membuat peta muncul dengan cepat. Namun kualitas produksi ditentukan oleh hal lain: pembatasan API key, alert biaya, pencarian alamat, layout mobile, privasi lokasi, dan pemisahan antara kode browser dan kode server.

Dalam prototipe store locator, masalah yang pernah ditemui Masa bukan marker, melainkan pencarian alamat. Geocoding berarti mengubah alamat menjadi latitude dan longitude. Kelihatannya hanya satu panggilan API, tetapi alamat bisa ambigu, request bisa menambah biaya, dan hasilnya terikat kebijakan Google Maps Platform.

Artikel ini memakai Claude Code sebagai partner implementasi, bukan sekadar generator snippet. Contohnya memakai Next.js App Router, React, Google Maps JavaScript API, Advanced Markers, Geocoding API, dan alternatif Mapbox GL JS. Untuk keamanan key, baca juga audit keamanan Claude Code. Untuk performa, lihat optimasi performa. Untuk layer data geografis, lihat visualisasi data.

Brief yang tepat untuk Claude Code

Jangan hanya menulis “tambahkan Google Maps”. Tulis batasan operasional agar Claude Code tidak berhenti di demo lokal.

Implementasikan halaman store locator dengan Next.js App Router.

Syarat:
- Gunakan Google Maps JavaScript API
- Gunakan AdvancedMarkerElement, bukan class Marker lama
- Baca browser key dari NEXT_PUBLIC_GOOGLE_MAPS_API_KEY
- Asumsikan HTTP referrer restriction dan API restriction sudah disiapkan
- Panggil Geocoding API dari server route dengan GOOGLE_MAPS_SERVER_KEY
- Jangan kirim server key ke browser bundle
- Sinkronkan address search, daftar toko, klik marker, dan selected state
- Jangan membaca window atau google selama SSR
- Tangani loading, error, empty, permission lokasi ditolak, dan mobile state
- Berikan checklist API restriction, budget alert, dan policy review

Pisahkan tanggung jawabnya seperti ini.

flowchart LR
  User["Pengguna"]
  Page["Halaman store locator"]
  Map["Google Maps JS API"]
  Route["/api/geocode"]
  Google["Geocoding API"]
  Store["Data toko"]
  Alerts["Alert biaya dan log"]

  User --> Page
  Page --> Map
  Page --> Store
  Page --> Route
  Route --> Google
  Route --> Alerts

Terjemahkan istilah teknis untuk tim. Geocoding adalah mengubah alamat menjadi koordinat. Reverse geocoding adalah menebak alamat dari koordinat. Map ID adalah identifier Google Cloud untuk style peta dan Advanced Markers.

Memuat Google Maps secara aman di Next.js

Pasang loader dan tipe. TypeScript membantu menangkap contoh lama yang mungkin diberikan Claude Code.

npm i @googlemaps/js-api-loader
npm i -D @types/google.maps

Advanced Markers membutuhkan map ID. DEMO_MAP_ID cukup untuk tes lokal, tetapi produksi harus memakai map ID dari Google Cloud Console. Browser key memang terlihat pada Maps JavaScript API, sehingga kontrol yang benar adalah membatasinya. Ikuti panduan keamanan Google Maps Platform dengan HTTP referrer restriction dan API restriction.

// src/lib/google-maps-loader.ts
import { Loader } from "@googlemaps/js-api-loader";

let googleMapsPromise: Promise<typeof google> | null = null;

export function loadGoogleMaps() {
  const apiKey = process.env.NEXT_PUBLIC_GOOGLE_MAPS_API_KEY;

  if (!apiKey) {
    throw new Error("NEXT_PUBLIC_GOOGLE_MAPS_API_KEY is missing");
  }

  if (!googleMapsPromise) {
    const loader = new Loader({
      apiKey,
      version: "weekly",
      libraries: ["marker", "places"],
    });

    googleMapsPromise = loader.load();
  }

  return googleMapsPromise;
}

Komponen peta harus client-side. API dimuat di dalamuseEffect, bukan di top-level file, agar tidak rusak saat SSR. Marker juga perlu dibersihkan saat komponen dilepas.

// src/components/GoogleBusinessMap.tsx
"use client";

import { useEffect, useRef } from "react";
import { loadGoogleMaps } from "@/lib/google-maps-loader";

export type MapPoint = {
  id: string;
  title: string;
  lat: number;
  lng: number;
  category?: "store" | "warehouse" | "property";
};

type Props = {
  points: MapPoint[];
  center: google.maps.LatLngLiteral;
  zoom?: number;
  onSelect?: (point: MapPoint) => void;
};

export function GoogleBusinessMap({ points, center, zoom = 13, onSelect }: Props) {
  const mapRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    let cancelled = false;
    let markers: google.maps.marker.AdvancedMarkerElement[] = [];

    async function renderMap() {
      await loadGoogleMaps();
      if (!mapRef.current || cancelled) return;

      const { Map } = (await google.maps.importLibrary("maps")) as google.maps.MapsLibrary;
      const { AdvancedMarkerElement, PinElement } = (await google.maps.importLibrary(
        "marker",
      )) as google.maps.MarkerLibrary;

      const map = new Map(mapRef.current, {
        center,
        zoom,
        mapId: process.env.NEXT_PUBLIC_GOOGLE_MAPS_MAP_ID ?? "DEMO_MAP_ID",
        fullscreenControl: false,
        gestureHandling: "cooperative",
      });

      markers = points.map((point, index) => {
        const pin = new PinElement({
          glyph: String(index + 1),
          background: point.category === "warehouse" ? "#0f766e" : "#2563eb",
          borderColor: "#ffffff",
          glyphColor: "#ffffff",
        });

        const marker = new AdvancedMarkerElement({
          map,
          position: { lat: point.lat, lng: point.lng },
          title: point.title,
          content: pin.element,
        });

        marker.addListener("click", () => onSelect?.(point));
        return marker;
      });
    }

    renderMap().catch((error) => console.error("Failed to render Google Map", error));

    return () => {
      cancelled = true;
      markers.forEach((marker) => {
        marker.map = null;
      });
    };
  }, [center.lat, center.lng, points, zoom, onSelect]);

  return <div ref={mapRef} className="h-[420px] w-full rounded-lg border" />;
}

Jangan membangun HTML string dari input pengguna untukInfoWindow. Nama toko, alamat, dan catatan bisa datang dari CMS atau admin panel; render sebagai teks.

Pindahkan geocoding ke server route

Pisahkan key browser dan key server. Key browser dibatasi dengan referrer. Key server dibatasi sesuai environment. Struktur response dan status dapat dicek di dokumentasi resmi Geocoding request and response.

// src/app/api/geocode/route.ts
import { NextResponse } from "next/server";

type GeocodeResponse = {
  status: string;
  error_message?: string;
  results: Array<{
    formatted_address: string;
    place_id: string;
    geometry: { location: { lat: number; lng: number } };
  }>;
};

const endpoint = "https://maps.googleapis.com/maps/api/geocode/json";

export async function GET(request: Request) {
  const key = process.env.GOOGLE_MAPS_SERVER_KEY;
  const { searchParams } = new URL(request.url);
  const address = searchParams.get("address")?.trim();

  if (!key) return NextResponse.json({ error: "Server key is missing" }, { status: 500 });
  if (!address || address.length > 180) {
    return NextResponse.json({ error: "Address is required" }, { status: 400 });
  }

  const params = new URLSearchParams({ address, key, language: "id", region: "id" });
  const response = await fetch(`${endpoint}?${params}`, { cache: "no-store" });
  const data = (await response.json()) as GeocodeResponse;
  const first = data.results[0];

  if (!response.ok || data.status !== "OK" || !first) {
    return NextResponse.json(
      { error: data.error_message ?? data.status },
      { status: data.status === "ZERO_RESULTS" ? 404 : 502 },
    );
  }

  return NextResponse.json({
    formattedAddress: first.formatted_address,
    placeId: first.place_id,
    location: first.geometry.location,
  });
}

Jangan menambahkan cache panjang hanya karena takut biaya. Penyimpanan hasil geocoding bergantung pada terms dan policy Google Maps Platform. Untuk store locator, koordinat resmi toko sebaiknya menjadi data milik aplikasi; query pengguna diproses sesuai kebutuhan minimum.

Use case yang paling sering muncul

Use case pertama adalah store locator untuk cabang, klinik, kelas, showroom, atau venue acara. Fitur intinya adalah pencarian alamat, lokasi saat ini, jam buka, nomor telepon, dan CTA reservasi. Pada prototipe Masa, map terlalu tinggi di mobile dan daftar toko tenggelam. Versi yang lebih baik menampilkan daftar lebih dulu, lalu menggeser pusat peta saat toko dipilih.

Use case kedua adalah pencarian properti atau penginapan. Harga, ketersediaan, luas, jarak jalan kaki, filter, dan batas peta harus sinkron. Menampilkan semua marker sekaligus bukan pilihan yang baik. Minta Claude Code mengambil data sesuai viewport, melakukan clustering untuk area padat, dan menjaga daftar tetap sesuai dengan peta.

Use case ketiga adalah operasi lapangan: delivery, sales visit, atau maintenance. Di sini peta adalah alat kerja. Tentukan frekuensi update lokasi, siapa yang boleh melihat, masa simpan, dan perilaku saat izin lokasi ditolak. Jika menambah optimasi rute, cek biaya Routes API atau Directions API sebelum coding.

Use case keempat adalah konten editorial seperti panduan wisata, review area, atau liputan event. Peta membantu eksplorasi, tetapi tidak menggantikan teks yang berguna. Tetap tulis arah, konteks lokal, aksesibilitas, dan alasan rekomendasi di body artikel.

Kapan memilih Mapbox

Google Maps kuat untuk pencarian toko, alamat, Places, dan pengalaman yang familiar. Mapbox GL JS lebih cocok jika Anda punya dataset geografis sendiri, perlu style peta khusus, atau ingin layer WebGL. Mulailah dari panduan Mapbox GL JS.

KriteriaGoogle MapsMapbox GL JS
Store locatorKuat dengan Geocoding dan PlacesBagus jika data utama milik sendiri
Kontrol visualCloud Styling sering cukupKontrol style dan layer lebih tinggi
Learning curveLebih mudah untuk app umumPerlu memahami source, layer, style
OperasionalKey restriction dan billing alertToken restriction dan attribution
// src/components/MapboxPreview.tsx
"use client";

import { useEffect, useRef } from "react";
import mapboxgl from "mapbox-gl";
import "mapbox-gl/dist/mapbox-gl.css";

export function MapboxPreview() {
  const containerRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    const token = process.env.NEXT_PUBLIC_MAPBOX_TOKEN;
    if (!containerRef.current || !token) return;

    mapboxgl.accessToken = token;

    const map = new mapboxgl.Map({
      container: containerRef.current,
      style: "mapbox://styles/mapbox/streets-v12",
      center: [106.8456, -6.2088],
      zoom: 12,
    });

    map.addControl(new mapboxgl.NavigationControl(), "top-right");
    return () => map.remove();
  }, []);

  return <div ref={containerRef} className="h-[420px] w-full rounded-lg border" />;
}

Instruksi untuk Claude Code sebaiknya membagi peran: Google Maps untuk address search dan discovery toko, Mapbox untuk data layer milik sendiri. Ini lebih mudah dipelihara daripada membuat dua implementasi penuh.

Jebakan sebelum produksi

Jebakan pertama adalah API key tanpa restriction. Browser key memang terlihat, tetapi harus dibatasi referrer dan API. Server key tidak boleh masuk bundle browser.

Jebakan kedua adalah contohMarkerlama. Google mendorong Advanced Markers; dokumentasi Advanced Markers menunjukkan alurAdvancedMarkerElement.

Jebakan ketiga adalah SSR. Di Next.js, memakaigoogle.maps.Mapdi top-level file akan memunculkangoogle is not defined. Gunakan client component dan load API diuseEffect.

Jebakan keempat adalah alamat ambigu. Nama stasiun, kawasan, atau jalan bisa mengembalikan banyak kandidat. Gunakan language, region, country, input terstruktur, atau pilihan kandidat. Jangan menjadikan raw address sebagai primary key.

Jebakan kelima adalah performa. Ratusan marker dan card membuat halaman lambat. Gunakan clustering, query berdasarkan viewport, pagination, atau server search.

Jebakan keenam adalah privasi. Lokasi saat ini membutuhkan izin pengguna. UI harus tetap jalan saat izin ditolak; jika lokasi disimpan, jelaskan tujuan dan masa simpan.

Ringkasan dan hasil cek

Integrasi Google Maps yang aman dengan Claude Code memisahkan rendering peta, geocoding, key management, billing, privasi, dan mobile UX. Google Maps kuat untuk alamat dan toko. Mapbox kuat untuk data layer sendiri dan kontrol visual.

Pada update ini, bagian yang bisa dicek tanpa API key nyata sudah diperiksa: pemisahan tanggung jawab Next.js, loading yang aman untuk SSR, cabang error, dan format code block. Sebelum memakai key nyata, konfigurasikan Maps JavaScript API, Geocoding API, HTTP referrer restriction, server key restriction, dan budget alert di Google Cloud Console. Mulai dari tiga data toko, uji search, klik marker, mobile layout, dan metrik billing, lalu perluas ke data produksi.

#Claude Code #Google Maps #Mapbox #peta #geolocation
Gratis

PDF gratis: cheatsheet Claude Code

Masukkan email dan unduh satu halaman berisi command, kebiasaan review, dan workflow aman.

Kami menjaga datamu dan tidak mengirim spam.

Masa

Tentang penulis

Masa

Engineer yang berfokus pada workflow Claude Code praktis dan adopsi tim.