기능 | 지도를 활용한 위치 - ssseok/wedding.invitation GitHub Wiki

목적

  • 지도를 활용하여 정확한 위치를 시각적으로 보여줍니다.

설치

bunx --bun shadcn@latest add separator
bunx --bun shadcn@latest add dropdown-menu

설정

API KEY 설정하는 방법 API KEY를 설정하셨다면 NAVER MAP에 대한 설정을 알려드리겠습니다.

<script
  type="text/javascript"
  src="https://oapi.map.naver.com/openapi/v3/maps.js?ncpClientId=%VITE_NAVER_MAP_CLIENT_ID%"
></script>
  • 최상위 루트에 있는 index.html에 script를 넣어줍니다. html에 변수를 넣을 때는 %% 기호를 사용하여 넣어줍니다.
  • Vite를 사용하면 .env에 변수 설정을 VITE_으로 시작하여야 합니다.(저는 .env에 변수이름을 VITE_NAVER_MAP_CLIENT_ID이라고 했습니다.)

코드

import {
  WEDDING_ADDRESS,
  WEDDING_MAP_LAT,
  WEDDING_MAP_LOT,
} from '../../config';
import { useEffect, useRef } from 'react';

export default function MapNaver() {
  const mapRef = useRef<HTMLDivElement>(null);

  const lot = WEDDING_MAP_LOT;
  const lat = WEDDING_MAP_LAT;

  useEffect(() => {
    if (!mapRef.current) return;

    const position = new window.naver.maps.LatLng(lot, lat);

    // 지도 생성
    const map = new window.naver.maps.Map(mapRef.current, {
      center: position,
      zoom: 15,
      // zoomControl: true,
      // zoomControlOptions: {
      //   position: window.naver.maps.Position.TOP_RIGHT,
      // },
    });

    // 마커 생성 (기본 디자인)
    const marker = new window.naver.maps.Marker({
      position: position,
      map: map,
    });

    // 정보창 생성
    const infoWindow = new window.naver.maps.InfoWindow({
      content: `
        <div class="p-4 bg-white  shadow-lg text-center">
          <h3 class="font-bold text-lg">결혼식장</h3>
          <p class="text-gray-600">${WEDDING_ADDRESS}</p>
        </div>
      `,
      borderColor: 'transparent',
    });

    // 마커 클릭 시 정보창 표시
    window.naver.maps.Event.addListener(marker, 'click', () => {
      if (infoWindow.getMap()) {
        infoWindow.close();
      } else {
        infoWindow.open(map, marker);
      }
    });

    return () => {
      map.destroy();
    };
  }, []);

  return (
    <div className='w-full'>
      <div ref={mapRef} className='w-full h-[400px] shadow-md' />
    </div>
  );
}
  1. Naver 지도를 택한 이유?
  • 사실 Kakao 지도 API는 한번 사용해봤기에 사용안해본 Naver 지도를 택했습니다.
  1. 위도(lat), 경도(lot) 좌표 찾는 법
  1. 마커 정보창
  • Naver map에 InfoWindow 함수를 이용하여 정보창을 커스텀 가능합니다.
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuTrigger,
} from '@/common/components/ui/dropdown-menu';
import { Separator } from '@/common/components/ui/separator';
import { copy } from '@/lib/copy';
import {
  WEDDING_ADDRESS,
  WEDDING_LOCATION_BUS,
  WEDDING_LOCATION_NAME,
  WEDDING_LOCATION_SUBWAY,
  WEDDING_LOCATION_TEL,
  WEDDING_PARKING,
} from '../../config';
import { Map, MoveUpRight } from 'lucide-react';

export default function MapInfo() {
  return (
    <div className='mt-10 mx-8'>
      <div className='flex justify-between items-center'>
        <p>
          {WEDDING_ADDRESS}
          <br />
          <strong>{WEDDING_LOCATION_NAME}</strong>
        </p>
        <DropdownMenu>
          <DropdownMenuTrigger>
            <Map className='w-5 h-5' />
          </DropdownMenuTrigger>
          <DropdownMenuContent>
            <DropdownMenuItem onSelect={() => copy(WEDDING_ADDRESS)}>
              주소복사하기
            </DropdownMenuItem>
            <DropdownMenuItem
              onSelect={() => {
                window.open(
                  `https://map.naver.com/p/search/${WEDDING_ADDRESS}`,
                  '_blank',
                );
              }}
              className='flex justify-between'
            >
              네이버 지도 <MoveUpRight className='w-4 h-4' />
            </DropdownMenuItem>
            <DropdownMenuItem
              onSelect={() => {
                window.open(
                  `https://map.kakao.com/?q=${WEDDING_ADDRESS}`,
                  '_blank',
                );
              }}
              className='flex justify-between'
            >
              카카오 지도 <MoveUpRight className='w-4 h-4' />
            </DropdownMenuItem>
          </DropdownMenuContent>
        </DropdownMenu>
      </div>
      <p className='text-xs mt-2 text-gray-500 dark:text-foreground'>
        {WEDDING_LOCATION_TEL}
      </p>

      <div className='mt-10 space-y-8 text-gray-500 dark:text-foreground'>
        <div className='flex items-center'>
          <span className='w-14 text-foreground'>지하철</span>
          <Separator orientation='vertical' className='bg-foreground' />
          <div className='text-xs space-y-1'>
            <p>{WEDDING_LOCATION_SUBWAY}</p>
          </div>
        </div>
        <div className='flex items-center'>
          <span className=' w-14 text-foreground'>버스</span>
          <Separator orientation='vertical' className='bg-foreground' />
          <div className='text-xs space-y-1'>
            {WEDDING_LOCATION_BUS.map((bus, index) => (
              <p key={index}>{bus}</p>
            ))}
          </div>
        </div>
        <div className='flex items-center'>
          <span className='w-14 text-foreground'>주차</span>
          <Separator orientation='vertical' className='bg-foreground' />
          <div className='text-xs space-y-1'>
            <p>{WEDDING_PARKING}</p>
            <div className='flex gap-0.5'>
              *
              <span>
                주차장 이용이 혼잡하오니 불편하시더라도 대중교통 이용을 권장
                드립니다.
              </span>
            </div>
          </div>
        </div>
      </div>
    </div>
  );
}
⚠️ **GitHub.com Fallback** ⚠️