웹사이트에 인터랙티브 지도 추가하기 | JavaScript 튜토리얼 (2026)
Tutorial

웹사이트에 인터랙티브 지도 추가하기 | JavaScript 튜토리얼 (2026)

JavaScript를 사용하여 몇 분 안에 웹사이트에 인터랙티브 지도를 추가하세요. 마커, 팝업, 지오코딩 검색, MapAtlas를 사용한 React/Next.js 통합을 다루는 단계별 JavaScript 튜토리얼입니다.

MapAtlas Team16 min read
#maps#javascript#api#react#next.js#geocoding

모든 물류 대시보드, 부동산 목록, 음식 배달 앱에는 공통점이 있습니다. 바로 지도입니다. 웹사이트에 지도가 아직 없다면 중요한 정보를 놓치고 있는 것입니다. 사용자가 한눈에 위치, 근접성, 공간적 관계를 이해하는 데 도움이 되는 정보 말입니다.

이 튜토리얼은 모든 JavaScript 프로젝트에 완전히 인터랙티브한 지도를 추가하는 방법을 안내합니다. 렌더링된 지도로 시작하여 마커와 팝업을 추가하고, 지오코딩 API로 주소 검색을 연결한 다음, Next.js에 깔끔하게 통합되는 본격적인 React 컴포넌트로 마무리합니다.

최종적으로 구축할 내용은 다음과 같습니다:

  • API 키로 인증된 실시간 벡터 지도
  • 사용자 정의 팝업 콘텐츠가 있는 클릭 가능한 마커
  • 지오코딩 지원 주소 검색 기능
  • 적절한 정리 및 Next.js SSR 처리가 있는 재사용 가능한 React 컴포넌트
  • 본격적인 환경을 위한 성능 최적화 체크리스트

사전 요구사항

시작하기 전에 다음이 있는지 확인하세요:

  • MapAtlas API 키 (무료 가입, 신용카드 불필요). 이 단일 키는 모든 MapAtlas 서비스를 인증합니다: 타일, 지오코딩, 라우팅.
  • JavaScript 프로젝트. 순수 HTML, React, Vue, Svelte 모두 작동합니다.
  • Node.js 18 이상(npm으로 설치할 경우).

단계 1: MapAtlas SDK 설치하기

npm으로 SDK를 프로젝트에 가져오세요:

npm install @mapmetrics/mapmetrics-gl

일반 HTML 페이지를 사용 중이라면, <head>에 CDN 링크를 붙여넣으세요:

<link rel="stylesheet" href="https://unpkg.com/@mapmetrics/mapmetrics-gl/dist/mapmetrics-gl.css" />
<script src="https://unpkg.com/@mapmetrics/mapmetrics-gl/dist/mapmetrics-gl.js"></script>

CSS 가져오기를 건너뛰지 마세요. 이것 없이는 지도 컨트롤과 팝업이 스타일이 없이 렌더링됩니다. 기능하지만 시각적으로 깨져 있습니다.

단계 2: 지도 컨테이너 만들기

SDK는 지정한 요소를 채우므로 명시적 높이를 가진 <div>이 필요합니다. 이것이 가장 일반적인 설정 실수입니다. 컨테이너에 height: 0이 있으면 지도가 초기화되지만 보이지 않습니다.

<div id="map" style="width: 100%; height: 500px;"></div>

고정 픽셀 값이나 뷰포트 단위(100vh, 50vh) 모두 작동합니다. 백분율 높이는 부모 요소도 정의된 높이를 가질 때만 작동합니다.

단계 3: 첫 번째 인터랙티브 지도 렌더링하기

단 3줄의 설정으로 충분합니다: 컨테이너, API 키가 있는 스타일 URL, 시작 위치입니다.

import mapmetricsgl from '@mapmetrics/mapmetrics-gl';
import '@mapmetrics/mapmetrics-gl/dist/mapmetrics-gl.css';

const map = new mapmetricsgl.Map({
  container: 'map',
  style: 'https://tiles.mapatlas.eu/styles/basic/style.json?key=YOUR_API_KEY',
  center: [4.9041, 52.3676],
  zoom: 12,
});

페이지를 열면 드래그, 스크롤, 핀치로 네비게이션할 수 있는 벡터 지도가 표시됩니다. 타일은 MapAtlas 서버에서 필요할 때 로드되므로 무거운 초기 다운로드가 없습니다.

지도 스타일 선택하기

스타일 URL의 경로 세그먼트를 바꿔서 모양을 완전히 변경할 수 있습니다:

스타일URL 경로최적의 용도
Basic/styles/basic/style.json범용 앱
Bright/styles/bright/style.json데이터 시각화 오버레이
Dark/styles/dark/style.json대시보드, 다크모드, 분석

빠른 승리: 관리 패널과 저조도 환경에서 사용되는 도구에 다크 스타일을 사용하세요. 눈의 피로를 줄이고 히트맵, 경로 선 같은 데이터 레이어가 배경에 대해 시각적으로 눈에 띕니다.

단계 4: 지도에 마커와 팝업 추가하기

마커 없는 지도는 배경 이미지일 뿐입니다. 마커는 정적 뷰를 사용자가 상호작용할 수 있는 것으로 변환합니다.

팝업이 있는 단일 마커

const popup = new mapmetricsgl.Popup().setHTML(`
  <strong>Amsterdam Central</strong>
  <p>Stationsplein, 1012 AB Amsterdam</p>
`);

new mapmetricsgl.Marker({ color: '#97C70A' })
  .setLngLat([4.9001, 52.3791])
  .setPopup(popup)
  .addTo(map);

마커를 클릭하면 팝업이 열립니다. 내부에 모든 HTML을 넣을 수 있습니다: 주소, 썸네일, CTA 버튼, UI가 필요한 모든 것.

데이터에서 여러 마커 그리기

대부분의 실제 앱은 두 개 이상의 핀이 필요합니다. 배열을 반복하고 각 항목에 대해 마커를 만들세요:

const locations = [
  { name: 'Amsterdam', coords: [4.9041, 52.3676] },
  { name: 'Rotterdam', coords: [4.4777, 51.9244] },
  { name: 'Utrecht',   coords: [5.1214, 52.0907] },
];

locations.forEach(({ name, coords }) => {
  const popup = new mapmetricsgl.Popup().setHTML(`<strong>${name}</strong>`);
  new mapmetricsgl.Marker({ color: '#97C70A' })
    .setLngLat(coords)
    .setPopup(popup)
    .addTo(map);
});

성능 참고: 대략 100~200개 마커를 초과하면 확대 축소된 뷰에서 렌더링이 눈에 띄게 느려집니다. 해결책은 GeoJSON 소스 클러스터링을 활성화하는 것입니다(SDK에서 기본 지원). 낮은 줌 레벨에서 근처 마커를 그룹화합니다. 클러스터링 구성은 SDK 문서를 확인하세요.

단계 5: 지오코딩 API로 주소 검색 추가하기

지오코딩 API는 텍스트 쿼리(거리 주소, 도시명, 또는 랜드마크)를 좌표로 변환합니다. 이를 사용하여 팬하거나 마킹하거나 라우팅 요청에 입력할 수 있습니다.

async function searchAddress(query) {
  const url = new URL('https://api.mapatlas.eu/geocoding/v1/search');
  url.searchParams.set('text', query);
  url.searchParams.set('key', 'YOUR_API_KEY');
  const res  = await fetch(url);
  const data = await res.json();
  if (!data.features.length) return;
  const [lng, lat] = data.features[0].geometry.coordinates;
  const label      = data.features[0].properties.label;
  map.flyTo({ center: [lng, lat], zoom: 14 });
  new mapmetricsgl.Marker({ color: '#97C70A' })
    .setLngLat([lng, lat])
    .setPopup(new mapmetricsgl.Popup().setHTML(`<strong>${label}</strong>`))
    .addTo(map);
}
searchAddress('Rijksmuseum, Amsterdam');

결과는 GeoJSON 기능으로 반환되므로 모든 GeoJSON 호환 레이어, 데이터 테이블, 또는 다운스트림 API 호출에 직접 연결됩니다.

30줄 미만으로 실시간 검색 표시줄을 만드세요: 텍스트 필드의 input 이벤트에 searchAddress를 첨부하고 300ms로 디바운스하면, 추가 종속성 없이 자동완성 스타일의 지도 검색이 완성됩니다.

React와의 인터랙티브 지도 통합

재사용 가능한 지도 컴포넌트

지도 초기화를 useEffect에 래핑하여 DOM이 마운트한 후 실행되도록 하고, 언마운트 시 메모리 누수를 방지하는 정리 함수를 반환합니다:

import { useEffect, useRef } from 'react';
import mapmetricsgl from '@mapmetrics/mapmetrics-gl';
import '@mapmetrics/mapmetrics-gl/dist/mapmetrics-gl.css';

export function MapAtlasMap({ apiKey, center = [4.9041, 52.3676], zoom = 12 }) {
  const containerRef = useRef(null);
  useEffect(() => {
    const map = new mapmetricsgl.Map({
      container: containerRef.current,
      style: `https://tiles.mapatlas.eu/styles/basic/style.json?key=${apiKey}`,
      center, zoom,
    });
    return () => map.remove();
  }, [apiKey]);
  return <div ref={containerRef} style={{ width: '100%', height: '500px' }} />;
}

컴포넌트 트리의 어디든 사용하세요:

<MapAtlasMap apiKey={process.env.NEXT_PUBLIC_MAPATLAS_KEY} center={[4.9041, 52.3676]} zoom={13} />

Next.js 서버 측 렌더링 처리하기

지도 SDK는 SSR 중에 존재하지 않는 브라우저 API(window, document)에 의존합니다. SSR을 비활성화한 상태로 컴포넌트를 동적으로 가져오세요:

import dynamic from 'next/dynamic';
const MapAtlasMap = dynamic(
  () => import('./MapAtlasMap').then(m => m.MapAtlasMap),
  { ssr: false, loading: () => (<div style={{ height: 500, background: '#f0f1f3', borderRadius: 12 }} />) }
);

loading 자리표시자는 지도 번들이 다운로드되는 동안 레이아웃을 안정적으로 유지하여 누적 레이아웃 이동(CLS)을 방지합니다. 이것은 사용자 경험과 Core Web Vitals 모두에 중요합니다.

본격적인 환경 성능 체크리스트

배포하기 전에 다음 최적화를 검토하세요:

  • 뷰포트 아래의 지도를 지연 로드하세요. IntersectionObserver를 사용하여 컨테이너가 뷰로 스크롤될 때만 지도를 초기화하세요. 이렇게 하면 초기 페이지 로드에서 약 200KB의 JavaScript가 지연됩니다.
  • 벡터 타일을 고수하세요. 벡터 타일은 모든 화면 밀도로 깔끔하게 확장되고, 래스터 이미지보다 빠르게 로드되며, 추가 서버 요청 없이 클라이언트 측에서 완전히 다시 스타일링할 수 있습니다. MapAtlas는 기본적으로 벡터 타일을 제공합니다.
  • 큰 마커 세트를 클러스터링하세요. 100~200개 마커를 초과하면, 확대 축소된 뷰에서 클러스터링되지 않은 렌더링이 눈에 띄는 프레임 드롭을 유발합니다. 클러스터링이 이를 완전히 해결합니다.
  • API 키를 서버 측에 유지하세요. 공개 저장소에 키를 커밋하지 마세요. 환경 변수(Next.js의 NEXT_PUBLIC_MAPATLAS_KEY) 또는 민감한 작업을 위해 백엔드를 통해 요청을 프록시하세요.
  • 지역 앱에 대해 maxBounds를 설정하세요. 사용자가 한 지역만 신경 쓴다면, 뷰포트를 제한하여 그 지역 외부의 타일은 요청되지 않습니다. 네트워크 호출이 줄고 로드가 빨라집니다.

다음에 구축할 내용

지도가 렌더링되고, 마커를 표시하고, 주소를 검색하고, React와 통합됩니다. 여기서 나아갈 방향:

  • 라우팅 API: 두 좌표 사이의 턴바이턴 방향을 요청하세요. 경로 폴리라인, 총 거리, 예상 소요 시간을 반환합니다.
  • 등시선 API: n분 내에 도달 가능한 모든 지점을 포함하는 폴리곤을 생성하세요. 배달 지역, 서비스 커버리지 지도, 유역 영역 분석에 사용됩니다.
  • 행렬 API: 단일 요청에서 여러 오리진과 목적지 간의 이동 시간과 거리를 계산하세요. 차량 배차 및 물류 최적화에 필수입니다.

전체 SDK 참조, 스타일 설명서, API 가이드는 docs.mapatlas.xyz에서 볼 수 있습니다.


자주 묻는 질문

무료로 웹사이트에 인터랙티브 지도를 추가할 수 있나요?

네. MapAtlas는 가입 시 신용카드가 필요 없는 무료 계층을 제공합니다. 벡터 타일 렌더링, 지오코딩 API, 라우팅 API가 포함됩니다. 이는 개발 및 소규모 본격적 환경 사용에 충분합니다.

React 또는 Next.js 앱에 지도를 어떻게 포함하나요?

지도 초기화를 useEffect 훅에 래핑하여 DOM이 마운트한 후 실행되도록 하세요. Next.js에서는 서버 측 렌더링 오류를 피하려면 dynamic()ssr: false로 사용하세요. 두 접근 방식 모두 이 튜토리얼에서 복사-붙여넣기 예제로 다루어집니다.

벡터 타일이 무엇이고, 래스터 대신 왜 사용해야 하나요?

벡터 타일은 사전 렌더링된 픽셀 이미지가 아닌 수학적 기하학으로 지도 기능(도로, 건물, 레이블)을 설명합니다. 모든 화면 밀도로 선명하게 확장되고, 래스터보다 빠르게 로드되며, 추가 서버 왕복 없이 클라이언트 측에서 완전히 다시 스타일링할 수 있습니다.

성능이 저하되기 전에 몇 개의 마커를 추가할 수 있나요?

렌더링은 일반적으로 낮은 줌 레벨에서 100~200개 마커를 초과하면 저하됩니다. 해결책은 클러스터링입니다: MapAtlas SDK는 GeoJSON 소스 클러스터링을 기본 지원하여 낮은 줌 레벨에서 근처 마커를 그룹화하고 사용자가 줌인할 때 확장합니다.

MapAtlas를 사용하려면 GIS 경험이 필요한가요?

아니요. SDK는 GIS 전문가가 아닌 웹 개발자를 위해 설계되었습니다. 좌표와 줌 레벨로 지도를 초기화하고, 경도/위도 쌍으로 마커를 추가하고, 지오코딩 API를 일반 텍스트로 호출합니다. 공간 데이터베이스나 GIS 도구가 필요하지 않습니다.

유용하셨나요? 공유해 보세요.

Back to Blog