Integrasi Google Maps dengan Claude Code: panduan praktis Next.js
Gunakan Claude Code untuk Google Maps: Advanced Markers, geocoding, toko, Mapbox, dan jebakan produksi.
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.
| Kriteria | Google Maps | Mapbox GL JS |
|---|---|---|
| Store locator | Kuat dengan Geocoding dan Places | Bagus jika data utama milik sendiri |
| Kontrol visual | Cloud Styling sering cukup | Kontrol style dan layer lebih tinggi |
| Learning curve | Lebih mudah untuk app umum | Perlu memahami source, layer, style |
| Operasional | Key restriction dan billing alert | Token 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.
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.
Tentang penulis
Masa
Engineer yang berfokus pada workflow Claude Code praktis dan adopsi tim.
Artikel terkait
Workflow Obsidian ke CLAUDE.md untuk Claude Code
Ubah catatan kerja Obsidian menjadi operating note CLAUDE.md agar konteks tidak dijelaskan ulang.
Claude Code Revenue CTA Routing: dari artikel ke PDF, Gumroad, dan konsultasi
Workflow Claude Code untuk mengarahkan pembaca ke PDF gratis, Gumroad, atau konsultasi sesuai intent.
Aturan handoff tim Claude Code: bukti review, permission, rollback, dan jalur revenue
Format handoff Claude Code untuk tim: bukti, permission rule, rollback, PDF gratis, Gumroad, dan konsultasi.