|
|
|
@@ -44,6 +44,30 @@ type NaverInfoWindow = {
|
|
|
|
|
|
|
|
|
|
|
|
const NAVER_CLIENT_ID = process.env.NEXT_PUBLIC_NAVER_MAP_CLIENT_ID || "";
|
|
|
|
const NAVER_CLIENT_ID = process.env.NEXT_PUBLIC_NAVER_MAP_CLIENT_ID || "";
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Channel color palette — GoogleMapView와 동일
|
|
|
|
|
|
|
|
const CHANNEL_COLORS = [
|
|
|
|
|
|
|
|
{ border: "#f59e0b" }, // amber (default)
|
|
|
|
|
|
|
|
{ border: "#3b82f6" }, // blue
|
|
|
|
|
|
|
|
{ border: "#22c55e" }, // green
|
|
|
|
|
|
|
|
{ border: "#ec4899" }, // pink
|
|
|
|
|
|
|
|
{ border: "#a855f7" }, // purple
|
|
|
|
|
|
|
|
{ border: "#ef4444" }, // red
|
|
|
|
|
|
|
|
{ border: "#14b8a6" }, // teal
|
|
|
|
|
|
|
|
{ border: "#eab308" }, // yellow
|
|
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function getChannelColorMap(restaurants: Restaurant[]) {
|
|
|
|
|
|
|
|
const channels = new Set<string>();
|
|
|
|
|
|
|
|
restaurants.forEach((r) => r.channels?.forEach((ch) => channels.add(ch)));
|
|
|
|
|
|
|
|
const map: Record<string, typeof CHANNEL_COLORS[0]> = {};
|
|
|
|
|
|
|
|
let i = 0;
|
|
|
|
|
|
|
|
for (const ch of channels) {
|
|
|
|
|
|
|
|
map[ch] = CHANNEL_COLORS[i % CHANNEL_COLORS.length];
|
|
|
|
|
|
|
|
i++;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return map;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function useNaverMaps(): { ready: boolean; error: string | null } {
|
|
|
|
function useNaverMaps(): { ready: boolean; error: string | null } {
|
|
|
|
const [ready, setReady] = useState(typeof window !== "undefined" && !!window.naver?.maps);
|
|
|
|
const [ready, setReady] = useState(typeof window !== "undefined" && !!window.naver?.maps);
|
|
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
@@ -101,9 +125,9 @@ function getClusterSize(count: number): number {
|
|
|
|
return 54;
|
|
|
|
return 54;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// SVG data URL — 단일 마커(주황 핀)
|
|
|
|
// 단일 마커 — 채널 색상별
|
|
|
|
function markerIconHtml(): string {
|
|
|
|
function markerIconHtml(color: string): string {
|
|
|
|
return `<div style="width:28px;height:28px;border-radius:9999px;background:#f59e0b;border:2px solid #fff;box-shadow:0 2px 6px rgba(0,0,0,.25);"></div>`;
|
|
|
|
return `<div style="width:28px;height:28px;border-radius:9999px;background:${color};border:2px solid #fff;box-shadow:0 2px 6px rgba(0,0,0,.25);"></div>`;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// SVG data URL — 클러스터(숫자)
|
|
|
|
// SVG data URL — 클러스터(숫자)
|
|
|
|
function clusterIconHtml(count: number, size: number): string {
|
|
|
|
function clusterIconHtml(count: number, size: number): string {
|
|
|
|
@@ -117,7 +141,9 @@ export default function NaverMapView({
|
|
|
|
onBoundsChanged,
|
|
|
|
onBoundsChanged,
|
|
|
|
flyTo,
|
|
|
|
flyTo,
|
|
|
|
onMyLocation,
|
|
|
|
onMyLocation,
|
|
|
|
|
|
|
|
activeChannel,
|
|
|
|
}: MapViewProps) {
|
|
|
|
}: MapViewProps) {
|
|
|
|
|
|
|
|
const channelColors = useMemo(() => getChannelColorMap(restaurants), [restaurants]);
|
|
|
|
const { ready, error } = useNaverMaps();
|
|
|
|
const { ready, error } = useNaverMaps();
|
|
|
|
const divRef = useRef<HTMLDivElement | null>(null);
|
|
|
|
const divRef = useRef<HTMLDivElement | null>(null);
|
|
|
|
const mapRef = useRef<NaverMapInstance | null>(null);
|
|
|
|
const mapRef = useRef<NaverMapInstance | null>(null);
|
|
|
|
@@ -230,12 +256,14 @@ export default function NaverMapView({
|
|
|
|
markersRef.current.push(marker);
|
|
|
|
markersRef.current.push(marker);
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
const r = (feature.properties as RestaurantProps).restaurant;
|
|
|
|
const r = (feature.properties as RestaurantProps).restaurant;
|
|
|
|
|
|
|
|
const chKey = activeChannel && r.channels?.includes(activeChannel) ? activeChannel : r.channels?.[0];
|
|
|
|
|
|
|
|
const chColor = chKey ? channelColors[chKey] : CHANNEL_COLORS[0];
|
|
|
|
const marker = new naver.Marker({
|
|
|
|
const marker = new naver.Marker({
|
|
|
|
position: new naver.LatLng(lat, lng),
|
|
|
|
position: new naver.LatLng(lat, lng),
|
|
|
|
map: m,
|
|
|
|
map: m,
|
|
|
|
title: r.name,
|
|
|
|
title: r.name,
|
|
|
|
icon: {
|
|
|
|
icon: {
|
|
|
|
content: markerIconHtml(),
|
|
|
|
content: markerIconHtml(chColor?.border ?? "#f59e0b"),
|
|
|
|
anchor: new naver.Point(14, 14),
|
|
|
|
anchor: new naver.Point(14, 14),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
});
|
|
|
|
});
|
|
|
|
@@ -252,7 +280,7 @@ export default function NaverMapView({
|
|
|
|
markersRef.current.push(marker);
|
|
|
|
markersRef.current.push(marker);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}, [clusters, getExpansionZoom, onSelectRestaurant]);
|
|
|
|
}, [clusters, getExpansionZoom, onSelectRestaurant, channelColors, activeChannel]);
|
|
|
|
|
|
|
|
|
|
|
|
// 컴포넌트 unmount 시 마커 정리
|
|
|
|
// 컴포넌트 unmount 시 마커 정리
|
|
|
|
useEffect(() => {
|
|
|
|
useEffect(() => {
|
|
|
|
|