Skip to content

정보 창 띄우기

InfoWindow 컴포넌트로 지도 위에 정보 팝업을 표시하는 방법을 설명합니다.

라이브 데모

기본 InfoWindow

tsx
import { NaverMap, InfoWindow, NaverMapProvider } from "react-naver-maps-kit";

function BasicInfoWindow() {
  return (
    <NaverMapProvider ncpKeyId={import.meta.env.VITE_NCP_KEY_ID}>
      <NaverMap
        center={{ lat: 37.5665, lng: 126.978 }}
        zoom={14}
        style={{ width: "100%", height: "500px" }}
      >
        <InfoWindow position={{ lat: 37.5665, lng: 126.978 }} visible>
          <div style={{ padding: "10px" }}>
            <strong>서울시청</strong>
            <p>서울특별시 중구 세종대로 110</p>
          </div>
        </InfoWindow>
      </NaverMap>
    </NaverMapProvider>
  );
}

마커와 연동하기

마커 클릭 시 InfoWindow를 표시하는 일반적인 패턴입니다:

tsx
import { useState } from "react";
import { NaverMap, Marker, InfoWindow, NaverMapProvider } from "react-naver-maps-kit";

const places = [
  { id: 1, name: "서울역", lat: 37.5547, lng: 126.9707 },
  { id: 2, name: "강남역", lat: 37.4981, lng: 127.0276 },
  { id: 3, name: "잠실역", lat: 37.5133, lng: 127.1 }
];

function MarkerWithPopup() {
  const [selectedId, setSelectedId] = useState(null);
  const selectedPlace = places.find((p) => p.id === selectedId);

  return (
    <NaverMapProvider ncpKeyId={import.meta.env.VITE_NCP_KEY_ID}>
      <NaverMap
        center={{ lat: 37.52, lng: 127.0 }}
        zoom={12}
        style={{ width: "100%", height: "500px" }}
      >
        {places.map((place) => (
          <Marker
            key={place.id}
            position={{ lat: place.lat, lng: place.lng }}
            onClick={() => setSelectedId(place.id)}
          />
        ))}

        {selectedPlace && (
          <InfoWindow
            position={{ lat: selectedPlace.lat, lng: selectedPlace.lng }}
            visible
            onCloseClick={() => setSelectedId(null)}
          >
            <div style={{ padding: "12px", minWidth: "150px" }}>
              <strong style={{ fontSize: "16px" }}>{selectedPlace.name}</strong>
              <button onClick={() => setSelectedId(null)} style={{ float: "right" }}>

              </button>
            </div>
          </InfoWindow>
        )}
      </NaverMap>
    </NaverMapProvider>
  );
}

커스텀 스타일 InfoWindow

배경색, 테두리, 크기 등을 커스터마이즈합니다:

tsx
<InfoWindow
  position={{ lat: 37.5665, lng: 126.978 }}
  visible
  backgroundColor="#ffffff"
  borderColor="#03C75A"
  borderWidth={2}
  maxWidth={300}
  disableAutoPan={false}
>
  <div style={{ padding: "16px" }}>
    <h3 style={{ margin: "0 0 8px", color: "#03C75A" }}>커스텀 InfoWindow</h3>
    <p style={{ margin: 0 }}>배경색, 테두리, 최대 너비 등을 조절할 수 있습니다.</p>
  </div>
</InfoWindow>

스타일 Props

Prop설명기본값
backgroundColor배경색흰색
borderColor테두리 색상회색
borderWidth테두리 두께1
maxWidth최대 너비무제한
disableAnchor꼬리 숨기기false
anchorColor꼬리 색상배경색과 동일

앵커 마커 사용

anchor prop으로 특정 마커에 InfoWindow를 고정합니다:

tsx
import { useRef, useState } from "react";
import {
  NaverMap,
  Marker,
  InfoWindow,
  NaverMapProvider,
  type MarkerRef
} from "react-naver-maps-kit";

function InfoWindowWithAnchor() {
  const markerRef = useRef<MarkerRef>(null);
  const [showInfo, setShowInfo] = useState(false);

  return (
    <NaverMapProvider ncpKeyId={import.meta.env.VITE_NCP_KEY_ID}>
      <NaverMap
        center={{ lat: 37.5665, lng: 126.978 }}
        zoom={14}
        style={{ width: "100%", height: "500px" }}
      >
        <Marker
          ref={markerRef}
          position={{ lat: 37.5665, lng: 126.978 }}
          onClick={() => setShowInfo(!showInfo)}
        />

        <InfoWindow
          anchor={markerRef.current?.getInstance()}
          visible={showInfo}
          pixelOffset={{ x: 0, y: -40 }} // 마커 위로 오프셋
        >
          <div style={{ padding: "12px" }}>마커에 고정된 InfoWindow</div>
        </InfoWindow>
      </NaverMap>
    </NaverMapProvider>
  );
}

자동 패닝 제어

InfoWindow가 열릴 때 지도가 자동으로 이동하는 것을 제어합니다:

tsx
<InfoWindow
  position={{ lat: 37.5665, lng: 126.978 }}
  visible
  disableAutoPan={true} // 자동 패닝 비활성화
  autoPanPadding={{ x: 50, y: 50 }} // 패닝 여백
>
  <div>자동 패닝이 비활성화된 InfoWindow</div>
</InfoWindow>

이벤트 처리

InfoWindow 열림/닫힘 이벤트를 처리합니다:

tsx
import { useState } from "react";
import { NaverMap, InfoWindow, NaverMapProvider } from "react-naver-maps-kit";

function InfoWindowEvents() {
  const [visible, setVisible] = useState(true);

  return (
    <div>
      <NaverMapProvider ncpKeyId={import.meta.env.VITE_NCP_KEY_ID}>
        <NaverMap
          center={{ lat: 37.5665, lng: 126.978 }}
          zoom={14}
          style={{ width: "100%", height: "400px" }}
        >
          <InfoWindow
            position={{ lat: 37.5665, lng: 126.978 }}
            visible={visible}
            onOpen={() => console.log("InfoWindow 열림")}
            onClose={() => console.log("InfoWindow 닫힘")}
          >
            <div style={{ padding: "10px" }}>
              <button onClick={() => setVisible(false)}>닫기</button>
            </div>
          </InfoWindow>
        </NaverMap>
      </NaverMapProvider>

      {!visible && <button onClick={() => setVisible(true)}>InfoWindow 열기</button>}
    </div>
  );
}

Ref로 제어하기

Ref를 통해 InfoWindow를 직접 제어합니다:

tsx
import { useRef } from "react";
import { NaverMap, InfoWindow, NaverMapProvider, type InfoWindowRef } from "react-naver-maps-kit";

function InfoWindowWithRef() {
  const infoRef = useRef<InfoWindowRef>(null);

  const updateContent = () => {
    infoRef.current?.setContent("새로운 내용으로 변경!");
  };

  const closeWindow = () => {
    infoRef.current?.close();
  };

  return (
    <div>
      <NaverMapProvider ncpKeyId={import.meta.env.VITE_NCP_KEY_ID}>
        <NaverMap
          center={{ lat: 37.5665, lng: 126.978 }}
          zoom={14}
          style={{ width: "100%", height: "400px" }}
        >
          <InfoWindow ref={infoRef} position={{ lat: 37.5665, lng: 126.978 }} visible>
            <div style={{ padding: "10px" }}>초기 내용</div>
          </InfoWindow>
        </NaverMap>
      </NaverMapProvider>

      <div style={{ marginTop: "1rem" }}>
        <button onClick={updateContent}>내용 변경</button>
        <button onClick={closeWindow}>닫기</button>
      </div>
    </div>
  );
}

꿀팁

1. 여러 InfoWindow 관리

한 번에 하나의 InfoWindow만 표시하려면:

tsx
function SingleInfoWindow() {
  const [openId, setOpenId] = useState(null);

  return (
    <>
      {markers.map((m) => (
        <Marker
          key={m.id}
          position={m.position}
          onClick={() => setOpenId(m.id)} // 다른 InfoWindow는 자동으로 닫힘
        />
      ))}

      {openId && (
        <InfoWindow position={getMarkerPosition(openId)} visible>
          {/* 내용 */}
        </InfoWindow>
      )}
    </>
  );
}

2. React Portal 없이 순수 HTML 사용

content prop으로 HTML 문자열을 직접 전달할 수도 있습니다:

tsx
<InfoWindow
  position={{ lat: 37.5665, lng: 126.978 }}
  content="<div style='padding:10px'><strong>HTML 내용</strong></div>"
  visible
/>

3. 성능 최적화

InfoWindow 내용이 복잡하면 메모이제이션을 사용하세요:

tsx
const infoContent = useMemo(
  () => (
    <div>
      <h3>{place.name}</h3>
      <p>{place.description}</p>
    </div>
  ),
  [place]
);

<InfoWindow visible>{infoContent}</InfoWindow>;

다음 단계

Released under the MIT License.