Probar la API de Kakao Map en Nextjs

힘센캥거루
2025년 1월 20일(수정됨)
56
nextjs
Probar la API de Kakao Map en Nextjs-1

A veces hay días así.

'Uh... creo que pasé rápido frente a una cámara de control de velocidad hace un rato.'

Sin embargo, no pude encontrar fácilmente la ubicación de la cámara de control de velocidad.

Así que decidí intentar hacerlo.

1. Datos públicos de Gyeonggi

Probar la API de Kakao Map en Nextjs-2

Dado que suelo ir a la provincia de Gyeonggi, pensé en usar los datos públicos de Gyeonggi.

Al entrar, encontré un ejemplo ya implementado con la API de Kakao Map.

Parecía que esto era lo que quería hacer.

Probar la API de Kakao Map en Nextjs-3

Al revisar los datos, vi que las cámaras de control no solo capturan exceso de velocidad sino también otras infracciones.

Tipos: 01: velocidad, 02: señal, 03: violación de tránsito, 04: estacionamiento ilegal, 99: otros, en total 5 tipos.

Ahora, dado que es TypeScript, tengo que especificar el tipo de retorno...

Probar la API de Kakao Map en Nextjs-4

¿En total hay 24 elementos? Es un montón.

Descargué un archivo json y lo cargué en Ruiten para imprimir el tipo.

AI es realmente útil.

Probar la API de Kakao Map en Nextjs-5
interface cameraDataType {
  MNLSS_TEFCM_INFO: string;
  SIDO_NM: string;
  SIGUN_NM: string;
  ROAD_KIND_NM: string;
  ROUTE_MANAGE_NO_INFO: string | null; // Se permite null por valores vacíos
  ROUTE_NM: string;
  ROUTE_INFO: string;
  REFINE_ROADNM_ADDR: string;
  REFINE_LOTNO_ADDR: string;
  REFINE_WGS84_LAT: string;
  REFINE_WGS84_LOGT: string;
  INSTL_PLC: string;
  REGLT_DIV_NM: string;
  LIMITN_SPEED_INFO: string;
  PRTCAREA_DIV: string;
  INSTL_YY: string;
  MNGINST_NM: string;
  MNGINST_TELNO: string;
  DATA_STD_DE: string;
}

Voy a especificar así el tipo y leer el archivo json para mostrarlo en el mapa.

Como las cámaras de control no aparecen de repente, solo para probar está bien.

2. API de Kakao Map

Ahora es el momento de hacer las configuraciones básicas para usar la API de Kakao Map.

Entra en el Kakao Developers.

Probar la API de Kakao Map en Nextjs-6

Entra en mi aplicación.

Si no tienes una aplicación, crea una.

Probar la API de Kakao Map en Nextjs-7

Y registra la plataforma.

Por defecto, el uso de la API está restringido a los dominios del sitio registrados aquí.

Probar la API de Kakao Map en Nextjs-8

Luego activa la configuración en Kakao Map en la parte inferior.

Probar la API de Kakao Map en Nextjs-9

Por último, verifica tu clave API.

Probar la API de Kakao Map en Nextjs-10

Ahora copia esta clave en mi .env.

Como es una clave que se expondrá, se debe configurar como nextpublic, pero no importa ponerla directamente en JavaScript.

3. Implementación del mapa usando el SDK de la API de Kakao Map

Quería hacerlo rápidamente, así que decidí probar una biblioteca de Kakao Map que encontré en GitHub.

Enlazo al GitHub de JaeSeoKim.

Primero, instalo la biblioteca.

yarn add react-kakao-maps-sdk

Dicha biblioteca ofrece una variedad de tipos.

Configura tsconfig.json de la siguiente manera.

{
  ...,
  "compilerOptions": {
    ...,
    "types": [
      ...,
      "kakao.maps.d.ts"
    ]
  }
}

Si typeRoots está presente, esto no funcionará.

En ese caso, configúralo así.

{
  "compilerOptions": {
     ...
    "typeRoots": [
      "./node_modules/kakao.maps.d.ts/@types"
    ]
     ...
  }
}

Ahora que hemos terminado la configuración básica, hagamos un componente para invocar Kakao Map.

Dará un error si se llama al mapa antes de que se carguen todos los scripts.

Así que hacemos que la visualización del mapa se realice después de comprobar el estado del script con onReady.

"use client";

import { Map, MapTypeControl, ZoomControl } from "react-kakao-maps-sdk";
import { useEffect, useState } from "react";
import { cameraCustomOverlay } from "@/types/allTypes";
import Script from "next/script";

export default function KakaoMapPage({
  data,
}: {
  data: cameraCustomOverlay[];
}) {
  const [scriptLoad, setScriptLoad] = useState<boolean>(false);

  return (
    <div className="items-center">
      <Script
        src={`//dapi.kakao.com/v2/maps/sdk.js?appkey=${process.env.NEXT_PUBLIC_KAKAO_API_KEY}&autoload=false&libraries=clusterer`}
        onReady = {() => setScriptLoad(true)}
      />
      {scriptLoad ? (
        <Map // Contenedor para mostrar el mapa
          style={{ height: "calc(100svh - 100px)" }}
          className="w-full"
          id="map"
          center={{ lat: 37.44749167, lng: 127.1477194 }}
          level={3} // Nivel de zoom del mapa
        >
          <MapTypeControl position={"TOPRIGHT"} />
          <ZoomControl position={"RIGHT"} />
        </Map>
      ) : (
        <div></div>
      )}
    </div>
  );
}

Ahora se puede invocar esto desde una página o colocarlo directamente en la página para que se ejecute como se muestra a continuación.

Ya que el mapa ha aparecido, se podría decir que es medio éxito.

Probar la API de Kakao Map en Nextjs-11

4. Crear marcadores

Ahora es el momento de extraer la latitud, longitud y datos de límite de velocidad de los datos de la cámara y mostrarlos en el mapa.

El método para mostrar marcadores en el SDK es bastante simple.

Solo introduce la latitud y longitud del lugar donde deseas mostrarlo y listo.

<MapMarker position={{ lat: 37, lng: 128 }} />

Al ingresar una latitud y longitud al azar, el pin se colocó en una montaña en Chungju.

Probar la API de Kakao Map en Nextjs-12

Y al colocar un ReactNode dentro de la etiqueta, también muestra texto.

<MapMarker position={{ lat: 37, lng: 128 }}>
  <div>Uh.. ¿Está aquí?</div>
  <div>¿Es esta la latitud 37 grados?</div>
</MapMarker>
Probar la API de Kakao Map en Nextjs-13

En realidad, no es muy bonito.

Por lo tanto, también se admite la superposición personalizada.

Usa customOverlayMap y luego úsalo como se indica a continuación.

<MapMarker position={{ lat: 37, lng: 128 }}></MapMarker>
<CustomOverlayMap position={{ lat: 37, lng: 128 }} yAnchor={2}>
<div>
    <div className="rounded-lg p-2 border border-red-500 bg-violet-600 text-white">
    <div>Uh.. ¿Está aquí?</div>
    <div>¿Es esta la latitud 37 grados?</div>
    </div>
</div>
</CustomOverlayMap>
Probar la API de Kakao Map en Nextjs-14

Lo más importante es que debe estar fuera de la etiqueta MapMarker.

Si la declaras dentro de MapMarker, permanecerá una caja blanca.

Ahora hagamos una función para esto.

Creé un tipo llamado cameraCustomOverlay que coincide con los datos que necesito que entren en la función.

interface cameraCustomOverlay {
  lat: number;
  lng: number;
  title: string;
  limit: number;
}

const EventMarkerContainer = ({ data }: { data: cameraCustomOverlay }) => {
  const [isVisible, setIsVisible] = useState(false);
  const position = { lat: data.lat, lng: data.lng };

  return (
    <>
      <MapMarker
        position={position} // Posición del marcador
        onClick={() => setIsVisible(!isVisible)}
      />
      {isVisible ? (
        <CustomOverlayMap
          position={position}
          yAnchor={1.5}
          key={`overlay-${data.lat}-${data.lng}`}
        >
          <div>
            <div className="bg-gray-50 text-black border-gray-500 rounded-lg border-2 relative">
              <div className="bg-slate-500 text-white p-2">Información de la cámara</div>
              <div className="absolute top-0 right-0 p-2">
                <XCircleIcon
                  className="size-6 text-white"
                  onClick={() => setIsVisible(!isVisible)}
                />
              </div>
              <div className="p-2">
                <div className="break-words">{data.title}</div>
                <div>Velocidad límite : {data.limit}</div>
              </div>
            </div>
          </div>
        </CustomOverlayMap>
      ) : null}
    </>
  );
};

Cuando intenté usar el evento de hover, no funcionó en el móvil.

Así que utilicé useState para poder abrir y cerrar la superposición.

Luego, simplemente procesa el json y añade las etiquetas con map.

5. Cargar datos

Lee y analiza el json, elimina los controles de estacionamiento con un filtro.

Luego, pasa la información necesaria como latitud, longitud, lugar de instalación y límite de velocidad al componente correspondiente.

import KakaoMapPage from "@/components/KakaoMapPage";
import fs from "fs";
import { cameraJsonPath } from "@/data/filePaths";
import { cameraDataType } from "@/types/allTypes";

export default function Page() {
  const dataJson = fs.readFileSync(cameraJsonPath, "utf-8");
  const allData: cameraDataType[] = JSON.parse(dataJson);
  const filterdMarkerData = allData.filter(
    (data) => !data.REGLT_DIV_NM.includes("4")
  );
  const markerData = filterdMarkerData.map((data) => {
    return {
      lat: Number(data.REFINE_WGS84_LAT),
      lng: Number(data.REFINE_WGS84_LOGT),
      title: data.INSTL_PLC,
      limit: Number(data.LIMITN_SPEED_INFO),
    };
  });

  return (
    <div className="items-center">
      <KakaoMapPage data={markerData} />
    </div>
  );
}

Ahora, en el componente, utiliza esos datos para mostrar los marcadores.

export default function KakaoMapPage({
  data,
}: {
  data: cameraCustomOverlay[];
}) {
  ...
      {scriptLoad ? (
        <Map // Contenedor para mostrar el mapa
          style={{ height: "calc(100svh - 100px)" }}
          className="w-full"
          id="map"
          center={{lat: 37.44749167,lng: 127.1477194}}
          level={3} // Nivel de zoom del mapa
        >
          {data.map((camera, idx) => (
            <EventMarkerContainer // Crear marcador
              key={idx}
              data={camera}
            />
          ))}
          <MapTypeControl position={"TOPRIGHT"} />
          <ZoomControl position={"RIGHT"} />
        </Map>
      ) : (
        <div></div>
   ...
  );
}

De esta manera, los marcadores se mostrarán y las superposiciones personalizadas aparecerán al hacer clic en ellos.

Al probarlo, funciona bien.

Probar la API de Kakao Map en Nextjs-15

6. Obtener información de ubicación del usuario

El uso de navigator permite obtener la información de GPS del usuario.

Utiliza useState para configurar la ubicación por defecto y, en useEffect, usar navigator para obtener información de ubicación del usuario y reasignar la ubicación del mapa.

  ...
  const [mapCenter, setMapCenter] = useState({
    lat: 37.44749167,
    lng: 127.1477194, // Ubicación predeterminada en Seongnam, Gyeonggi
  });
  useEffect(() => {
    const { geolocation } = navigator;
    geolocation.getCurrentPosition(
      (position) => {
        const { latitude, longitude } = position.coords;
        setMapCenter({
          lat: latitude,
          lng: longitude, // Reasignar según la ubicación del usuario
        });
      },
      (err) => console.log(err),
      { enableHighAccuracy: true, timeout: 5000, maximumAge: 0 }
    );
  }, []);
  ...

7. Problemas

El mapa se volvió demasiado lento debido a la gran cantidad de datos de CCTV.

Vamos a resolver este problema mediante el uso de clústeres.

<MarkerClusterer averageCenter={true} minLevel={6}>
  {data.map((camera, idx) => (
    <EventMarkerContainer // Crear marcador
      key={idx}
      data={camera}
    />
  ))}
</MarkerClusterer>

En el SDK, simplemente envolverlo con la etiqueta MarkerClusterer es suficiente.

Sin embargo, el problema es que la etiqueta CustomOverlayMap también se reconoce como una etiqueta de marcador, y la respuesta al clic cambia un poco.

Si deseas usar marcadores y superposiciones personalizadas junto con el clúster, debes separar las etiquetas como se muestra a continuación.

<MarkerClusterer averageCenter={true} minLevel={6}>
  {data.map((camera, idx) => (
    <MapMarker // Crear marcador
      key={idx}
      position={{ lat: camera.lat, lng: camera.lng }}
    />
  ))}
</MarkerClusterer>;
{
  data.map((camera, idx) => (
    <CustomOverlayMap key={idx} position={{ lat: camera.lat, lng: camera.lng }}>
      Contenido deseado
    </CustomOverlayMap>
  ));
}

Ahora, debemos hacer que cada MapMarker y CustomOverlayMap compartan el useState.

Esto se puede lograr utilizando la clave.

 // Primero, utiliza useState para especificar la clave del marcador activado.
  const [activeMarkerId, setActiveMarkerId] = useState<null|number>(null);
  ...
    <MarkerClusterer averageCenter={true} minLevel={6}>
    {data.map((camera, idx) => (
        <MapMarker // Crear marcador
        key={idx}
        position={{ lat: camera.lat, lng: camera.lng }}
        onClick={()=> {
            setActiveMarkerId(activeMarkerId === idx ? null : idx);
            // Al hacer clic en un marcador, especificar el índice del marcador como clave de activación.
            // Si la clave actual es igual a la clave del marcador, desactivarla.
        }}
        />
    ))}
    </MarkerClusterer>
    {data.map((camera, idx) => (
        // Solo se muestra la superposición personalizada si coincide con la clave activada.
    activeMarkerId === idx && (
    <CustomOverlayMap
        key={idx}
        yAnchor={1.5}
        position={{ lat: camera.lat, lng: camera.lng }}
    >
        <div>
        <div className="bg-gray-50 text-black border-gray-500 rounded-lg border-2 relative">
            <div className="bg-slate-500 text-white p-2">Información de la cámara</div>
            <div className="absolute top-0 right-0 p-2">
            <XCircleIcon className="size-6 text-white" 
            onClick={() => setActiveMarkerId(null)}
            />
            </div>
            <div className="p-2">
            <div className="break-words">{camera.title}</div>
            <div>Velocidad límite : {camera.limit}</div>
            </div>
        </div>
        </div>
    </CustomOverlayMap>
    )
    ))}

Como profesor de geociencias, creo que he profundizado demasiado en esto.

Ahora hagamos una última prueba.

Probar la API de Kakao Map en Nextjs-16

8. Reseña

He estado experimentando con el SDK de la API de Kakao Map durante dos días.

Fue frustrante encontrar lo que no funcionaba, pero al final sentir la satisfacción de resolverlo era genial.

Si tuviera tiempo, también me gustaría probar con la propia API, pero parece que no tengo suficiente tiempo.

Creo que esto es todo para mí...

Aún así, fue un momento agradable aprender algo nuevo.

댓글을 불러오는 중...