React + TypeScript 네이버 지도 API 적용 가이드

두밥비·2025년 5월 19일

article

목록 보기
14/23

최근 위치 기반 서비스나 지도 UI가 점점 중요해지고 있는 가운데, 네이버 지도는 국내 사용자에게 친숙한 API입니다. 이번 글에서는 React + TypeScript 환경에서 네이버 지도 API를 적용하고, 사용자 위치 기반 마커 표시 및 주소 역변환 기능을 구현하는 방법을 아주 상세하게 소개합니다.


1. 프로젝트 초기 설정

1-1. 필요한 패키지 설치

npm install react-naver-maps

단, 이 패키지는 타입 선언이 없으므로 아래와 같은 추가 작업이 필요합니다.


2. .env 설정

먼저 네이버 클라우드 콘솔에서 지도 APIReverse Geocode API 사용을 위해 애플리케이션을 생성하고, 다음과 같이 환경변수를 정의합니다.

# .env
VITE_NAVER_MAPS_KEY=네이버클라우드 Client ID
VITE_NAVER_MAPS_API_KEY=네이버클라우드 Client Secret

.gitignore.env가 포함되어야 하며, 깃허브에는 절대 올라가면 안 됩니다.


3. 전역 스크립트 로딩

main.tsx에서 지도 API를 사용할 수 있도록 네이버 지도 스크립트document.head에 수동으로 추가합니다.

// src/main.tsx
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import App from './App';
import './index.css';

const script = document.createElement('script');
script.src = `https://oapi.map.naver.com/openapi/v3/maps.js?ncpKeyId=${import.meta.env.VITE_NAVER_MAPS_KEY}`;
script.type = 'text/javascript';
document.head.appendChild(script);

createRoot(document.getElementById('root')!).render(
  <StrictMode>
    <App />
  </StrictMode>,
);

ncpKeyId를 꼭 스크립트 URL에 붙여야 하며, 오류가 난다면 인증 키나 등록된 도메인을 확인해야 합니다.


4. 타입 에러 해결

react-naver-maps는 타입 정의가 제공되지 않기 때문에 다음과 같이 직접 타입을 선언해야 합니다.

// src/types/react-naver-maps.d.ts
declare module 'react-naver-maps';

그리고 tsconfig.jsontypeRoots 설정이 잘 되어 있는지도 확인합니다.


5. 지도 페이지 컴포넌트 구현

이제 본격적으로 지도를 렌더링하는 MapPage.tsx를 구현합니다. 주요 기능은 다음과 같습니다:

기능 요약

  • 사용자 현재 위치 탐지
  • 위치 기반 중심 지도 렌더링
  • 주요 장소 마커 렌더링
  • 마커 클릭 시 로그 출력
  • 지도 클릭 시 역지오코딩으로 주소 출력

5-1. 마커 및 전시 데이터 정의

const markers = [
  {
    id: 1,
    name: '응접소파전시장',
    lat: 35.18654444,
    lng: 129.1224847,
  },
  ...
];

const EXHIBITIONS = [
  {
    id: 1,
    title: '전시의 전시',
    date: '2025.09.04. – 2026.01.04.',
    place: '국립현대미술관',
    distance: '1.9km',
    image: map1,
  },
  ...
];

5-2. 현재 위치 감지 및 지도 렌더링

useEffect(() => {
  if (!navigator.geolocation) {
    alert('이 브라우저에서는 위치 기능을 사용할 수 없습니다.');
    return;
  }

  navigator.geolocation.getCurrentPosition(
    pos => {
      const { latitude, longitude } = pos.coords;
      setMyLocation({ lat: latitude, lng: longitude });
    },
    err => {
      console.error('위치 접근 실패:', err);
      alert('위치 정보를 가져오는 데 실패했습니다.');
    },
  );
}, []);

5-3. 네이버 지도 컴포넌트

<NaverMap
  onClick={handleMapClick}
  defaultCenter={myLocation}
  defaultZoom={16}
  onLoad={() => setIsMapReady(true)}
>
  {/* 내 위치 표시 마커 */}
  <Marker
    position={myLocation}
    icon={{
      content: `
        <div style="...커스텀 마커 스타일..." />
      `,
    }}
  />

  {/* 다른 장소 마커 */}
  {markers.map(marker => (
    <Marker
      key={marker.id}
      position={{ lat: marker.lat, lng: marker.lng }}
      title={marker.name}
      icon={{
        content: `<img src="${mapIcon}" style="width: 24px; height: 24px;" />`,
      }}
      onClick={() => console.log(`클릭한 장소: ${marker.name}`)}
    />
  ))}
</NaverMap>

5-4. 지도 클릭 시 주소 가져오기 (역지오코딩)

const handleMapClick = (e: naver.maps.PointerEvent) => {
  const lat = e.coord.y;
  const lng = e.coord.x;

  fetch(
    `https://naveropenapi.apigw.ntruss.com/map-reversegeocode/v2/gc?coords=${lng},${lat}&orders=roadaddr,addr&output=json`,
    {
      method: 'GET',
      headers: {
        'X-NCP-APIGW-API-KEY-ID': import.meta.env.VITE_NAVER_MAPS_KEY,
        'X-NCP-APIGW-API-KEY': import.meta.env.VITE_NAVER_MAPS_API_KEY,
      },
    },
  )
    .then(res => res.json())
    .then(data => {
      const result = data?.results?.[0];
      if (result) {
        const address = `${result.region.area1.name} ${result.region.area2.name} ${result.region.area3.name}`;
        setAddress(address);
      }
    })
    .catch(err => console.error('주소 조회 실패:', err));
};

함께 구현한 UI 요소

  • 검색바(SearchBar)
  • 전시 필터 버튼
  • 주변 전시 리스트 바텀시트
  • 현재 선택된 주소 하단 고정 텍스트
<DraggableBottomSheet height={80} minHeight={82}>
  {/* 전시 리스트 렌더링 */}
</DraggableBottomSheet>

<div className="fixed ...">
  선택된 주소: {address}
</div>

테스트 포인트

  • 실제 사용자 위치 기반 지도가 중앙에 표시되는지?
  • 마커가 정상적으로 렌더링되는지?
  • 지도 클릭 시 주소가 표시되는지?
  • API 키가 올바르게 적용되었는지?

마무리

React와 TypeScript 환경에서 네이버 지도 API를 적용하는 것은 생각보다 간단하면서도 강력한 기능을 제공합니다. 특히 사용자 현재 위치 탐지, 지도 클릭 이벤트, 마커 렌더링, 역지오코딩 등을 조합하면 위치 기반 웹앱의 핵심 기능을 구현할 수 있습니다.

이 글에서 소개한 코드는 컴포넌트 분리와 상태 관리 개선 등을 통해 더욱 확장 가능합니다. 추후 검색 결과 기반 마커 필터링, 마커 클릭 시 상세 모달 등으로 발전시켜보세요.

전체 구조 요약

📦 src
├── main.tsx (스크립트 삽입)
├── App.tsx
├── pages/
│   └── MapPage.tsx (지도 페이지)
├── types/
│   └── react-naver-maps.d.ts (타입 정의)
├── components/
│   └── SearchBar.tsx
│   └── DraggableBottomSheet.tsx
├── constants/
│   └── filters.ts
│   └── filterColors.ts
profile
개발새발

0개의 댓글