Next.js에서 Script 컴포넌트를 이용해 네이버 지도 API를 동적으로 로드하고, 네이버 지도를 생성하는 방법을 정리해보았습니다.
useRef
로 관리각 단계에서 만난 의문점(❓)과 이슈(🚧)를 함께 기록하면서 문제를 어떻게 해결했는지도 함께 작성해 보았습니다.
Next.js
에서 네이버 지도 API를 동적으로 로드하려면 Script 컴포넌트를 사용해야 합니다. 아래는 네이버 지도 API를 strategy="afterInteractive"
옵션과 함께 로드하는 방법입니다.
import Script from "next/script";
export default function Dashboard() {
return (
<>
<Script
src="https://example.com/script.js"
strategy="afterInteractive"
/>
</>
);
}
strategy
옵션:beforeInteractive
: 초기 렌더링 전에 스크립트를 로드. 모든 페이지에서 전역적으로 적용할 경우에 사용afterInteractive
: 렌더링이 끝난 후 스크립트를 로드. 특정 페이지에서만 필요한 비필수 스크립트에 적합네이버 지도를 렌더링하려면 지도를 삽입할 DOM 요소를 지정해야 합니다. 여기서는 React의 useRef
를 사용해 DOM 요소를 참조합니다.
export default function NaverMap({ coords }: { coords: TCoords }) {
const mapRef = useRef<HTMLDivElement | null>(null);
return (
<>
<Script
src="https://example.com/script.js"
strategy="afterInteractive"
/>
<div className="naver-map" ref={mapRef} />
</>
);
}
useRef
를 사용하는가?id
를 사용할 수도 있지만, React에서는 useRef
가 더 안전하고 권장됩니다.
특성 | useRef | id |
---|---|---|
React와 동기화 | React 렌더링 모델과 자연스럽게 동기화됨. | DOM 렌더링 여부와 React 상태가 불일치할 가능성. |
조건부 렌더링 | 렌더링 후 자동으로 ref 에 설정. | 렌더링 조건에 따라 DOM 요소를 찾지 못할 수 있음. |
안전성 | 컴포넌트 내부적으로 안전하게 관리. | 전역적으로 id 중복 가능성으로 예기치 않은 문제 발생. |
initializeMap
함수는 설정한 DOM(mapRef.current
)에 네이버 지도를 생성하고, 마커와 이벤트 리스너를 추가합니다.
export const initializeMap = (
mapRef: MutableRefObject<HTMLDivElement | null>,
coords: TCoords,
) => {
const { latitude, longitude } = coords;
if (mapRef.current && window.naver) {
// 지도 옵션 설정
const mapOptions = {
center: new window.naver.maps.LatLng(latitude, longitude),
zoom: 15,
minZoom: 12,
};
// 네이버 지도 생성
const map = new window.naver.maps.Map(mapRef.current, mapOptions);
// 마커 설정
const marker = new window.naver.maps.Marker({
position: new window.naver.maps.LatLng(latitude, longitude),
map: map,
icon: {
url: "/images/lawyers/mapMarker.svg",
size: new naver.maps.Size(24, 30),
},
});
// 이벤트 리스너 설정
window.naver.maps.Event.addListener(marker, "click", () => {
const redirectUrl = generateNaverMapLink(coords);
window.open(redirectUrl, "_blank");
});
} else {
console.error(NAVER_MAP_ERRORS.containerNotFound);
}
};
window.naver
가 정의되지 않음initializeMap
이 실행될 때 window.naver
가 아직 로드되지 않아서 에러 발생onLoad
사용onLoad
를 활용하여 스크립트가 로드된 이후에만 initializeMap
을 호출
<Script
src="https://example.com/script.js"
strategy="afterInteractive"
onLoad={() => {
if (mapRef.current) {
initializeMap(mapRef, coords);
}
}}
/>
onLoad
는 스크립트 로드 시 한 번만 실행되므로, 탭 전환 후 다시 렌더링될 때 initializeMap
이 호출되지 않음useEffect
로 상태 변화 처리useEffect
를 사용해 컴포넌트가 다시 렌더링될 때도 initializeMap
이 호출되도록 수정
useEffect(() => {
if (window.naver && mapRef.current) {
initializeMap(mapRef, coords);
}
}, [coords]);
아래는 개선된 최종 코드입니다.
export default function NaverMap({ coords }: { coords: TCoords }) {
const mapRef = useRef<HTMLDivElement | null>(null);
useEffect(() => {
if (window.naver && mapRef.current) {
initializeMap(mapRef, coords);
}
}, [coords]);
return (
<>
<Script
type="text/javascript"
src={NAVER_MAP_SCRIPT}
strategy="afterInteractive"
onLoad={() => {
if (mapRef.current) {
initializeMap(mapRef, coords);
} else {
console.error(NAVER_MAP_ERRORS.apiNotLoaded);
}
}}
onError={() => {
console.error(NAVER_MAP_ERRORS.scriptLoadFailed);
}}
/>
<div className={cx("naver-map")} ref={mapRef} />
</>
);
}
스크립트 동적 로드
성능 최적화 및 필요 페이지에서만 로드
DOM 설정 및 지도 렌더링
스크립트 로드 후 네이버 지도 API 정상 작동
탭 전환 후에도 안정적으로 지도 생성