From 3e9c5668343398cd9ed1482fff12c297137da342 Mon Sep 17 00:00:00 2001 From: dohyeons Date: Mon, 17 Nov 2025 12:10:29 +0900 Subject: [PATCH] =?UTF-8?q?=EC=A7=80=EB=8F=84=20=EC=9C=84=EC=A0=AF=20?= =?UTF-8?q?=ED=8C=9D=EC=97=85=20=EC=88=98=EC=A0=95=20=EB=B0=8F=20=ED=8F=B4?= =?UTF-8?q?=EB=A6=AC=EA=B3=A4=20=EB=A7=A4=ED=95=91=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dashboard/data-sources/MultiApiConfig.tsx | 26 +- .../dashboard/widgets/MapTestWidgetV2.tsx | 337 +++++++++++++----- 2 files changed, 250 insertions(+), 113 deletions(-) diff --git a/frontend/components/admin/dashboard/data-sources/MultiApiConfig.tsx b/frontend/components/admin/dashboard/data-sources/MultiApiConfig.tsx index 2aba31f8..feb983cd 100644 --- a/frontend/components/admin/dashboard/data-sources/MultiApiConfig.tsx +++ b/frontend/components/admin/dashboard/data-sources/MultiApiConfig.tsx @@ -26,8 +26,6 @@ export default function MultiApiConfig({ dataSource, onChange, onTestResult }: M const [sampleData, setSampleData] = useState([]); // 샘플 데이터 (최대 3개) const [columnSearchTerm, setColumnSearchTerm] = useState(""); // 컬럼 검색어 - console.log("🔧 MultiApiConfig - dataSource:", dataSource); - // 외부 API 커넥션 목록 로드 useEffect(() => { const loadApiConnections = async () => { @@ -51,14 +49,12 @@ export default function MultiApiConfig({ dataSource, onChange, onTestResult }: M return; } - console.log("불러온 커넥션:", connection); // base_url과 endpoint_path를 조합하여 전체 URL 생성 const fullEndpoint = connection.endpoint_path ? `${connection.base_url}${connection.endpoint_path}` : connection.base_url; - console.log("전체 엔드포인트:", fullEndpoint); const updates: Partial = { endpoint: fullEndpoint, @@ -76,7 +72,6 @@ export default function MultiApiConfig({ dataSource, onChange, onTestResult }: M value, }); }); - console.log("기본 헤더 적용:", headers); } // 인증 설정이 있으면 헤더 또는 쿼리 파라미터에 추가 @@ -91,7 +86,6 @@ export default function MultiApiConfig({ dataSource, onChange, onTestResult }: M key: authConfig.keyName, value: authConfig.keyValue, }); - console.log("API Key 헤더 추가:", authConfig.keyName); } else if (authConfig.keyLocation === "query" && authConfig.keyName && authConfig.keyValue) { // UTIC API는 'key'를 사용하므로, 'apiKey'를 'key'로 변환 const actualKeyName = authConfig.keyName === "apiKey" ? "key" : authConfig.keyName; @@ -100,7 +94,6 @@ export default function MultiApiConfig({ dataSource, onChange, onTestResult }: M key: actualKeyName, value: authConfig.keyValue, }); - console.log("API Key 쿼리 파라미터 추가:", actualKeyName, "(원본:", authConfig.keyName, ")"); } break; @@ -111,7 +104,6 @@ export default function MultiApiConfig({ dataSource, onChange, onTestResult }: M key: "Authorization", value: `Bearer ${authConfig.token}`, }); - console.log("Bearer Token 헤더 추가"); } break; @@ -123,7 +115,6 @@ export default function MultiApiConfig({ dataSource, onChange, onTestResult }: M key: "Authorization", value: `Basic ${credentials}`, }); - console.log("Basic Auth 헤더 추가"); } break; @@ -134,7 +125,6 @@ export default function MultiApiConfig({ dataSource, onChange, onTestResult }: M key: "Authorization", value: `Bearer ${authConfig.accessToken}`, }); - console.log("OAuth2 Token 헤더 추가"); } break; } @@ -148,7 +138,6 @@ export default function MultiApiConfig({ dataSource, onChange, onTestResult }: M updates.queryParams = queryParams; } - console.log("최종 업데이트:", updates); onChange(updates); }; @@ -235,12 +224,12 @@ export default function MultiApiConfig({ dataSource, onChange, onTestResult }: M const result = await response.json(); + console.log("🌐 [API 테스트 결과]", result.data); + if (result.success) { // 텍스트 데이터 파싱 함수 (MapTestWidgetV2와 동일) const parseTextData = (text: string): any[] => { try { - console.log("🔍 텍스트 파싱 시작 (처음 500자):", text.substring(0, 500)); - const lines = text.split('\n').filter(line => { const trimmed = line.trim(); return trimmed && @@ -249,8 +238,6 @@ export default function MultiApiConfig({ dataSource, onChange, onTestResult }: M !trimmed.startsWith('---'); }); - console.log(`📝 유효한 라인: ${lines.length}개`); - if (lines.length === 0) return []; const result: any[] = []; @@ -278,7 +265,6 @@ export default function MultiApiConfig({ dataSource, onChange, onTestResult }: M } } - console.log("📊 파싱 결과:", result.length, "개"); return result; } catch (error) { console.error("❌ 텍스트 파싱 오류:", error); @@ -291,10 +277,8 @@ export default function MultiApiConfig({ dataSource, onChange, onTestResult }: M // 텍스트 데이터 체크 (기상청 API 등) if (data && typeof data === 'object' && data.text && typeof data.text === 'string') { - console.log("📄 텍스트 형식 데이터 감지, CSV 파싱 시도"); const parsedData = parseTextData(data.text); if (parsedData.length > 0) { - console.log(`✅ CSV 파싱 성공: ${parsedData.length}개 행`); data = parsedData; } } else if (dataSource.jsonPath) { @@ -306,6 +290,8 @@ export default function MultiApiConfig({ dataSource, onChange, onTestResult }: M const rows = Array.isArray(data) ? data : [data]; + console.log("📊 [최종 파싱된 데이터]", rows); + // 컬럼 목록 및 타입 추출 if (rows.length > 0) { const columns = Object.keys(rows[0]); @@ -336,9 +322,6 @@ export default function MultiApiConfig({ dataSource, onChange, onTestResult }: M // 샘플 데이터 저장 (최대 3개) setSampleData(rows.slice(0, 3)); - - console.log("📊 발견된 컬럼:", columns); - console.log("📊 컬럼 타입:", types); } // 위도/경도 또는 coordinates 필드 또는 지역 코드 체크 @@ -422,7 +405,6 @@ export default function MultiApiConfig({ dataSource, onChange, onTestResult }: M id={`endpoint-${dataSource.id}`} value={dataSource.endpoint || ""} onChange={(e) => { - console.log("📝 API URL 변경:", e.target.value); onChange({ endpoint: e.target.value }); }} placeholder="https://api.example.com/data" diff --git a/frontend/components/dashboard/widgets/MapTestWidgetV2.tsx b/frontend/components/dashboard/widgets/MapTestWidgetV2.tsx index 5df1663a..56ef2089 100644 --- a/frontend/components/dashboard/widgets/MapTestWidgetV2.tsx +++ b/frontend/components/dashboard/widgets/MapTestWidgetV2.tsx @@ -96,11 +96,8 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) { // 다중 데이터 소스 로딩 const loadMultipleDataSources = useCallback(async () => { if (!dataSources || dataSources.length === 0) { - // // console.log("⚠️ 데이터 소스가 없습니다."); return; } - - // // console.log(`🔄 ${dataSources.length}개의 데이터 소스 로딩 시작...`); setLoading(true); setError(null); @@ -109,8 +106,6 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) { const results = await Promise.allSettled( dataSources.map(async (source) => { try { - // // console.log(`📡 데이터 소스 "${source.name || source.id}" 로딩 중...`); - if (source.type === "api") { return await loadRestApiData(source); } else if (source.type === "database") { @@ -130,21 +125,16 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) { const allPolygons: PolygonData[] = []; results.forEach((result, index) => { - // // console.log(`🔍 결과 ${index}:`, result); - if (result.status === "fulfilled" && result.value) { const value = result.value as { markers: MarkerData[]; polygons: PolygonData[] }; - // // console.log(`✅ 데이터 소스 ${index} 성공:`, value); // 마커 병합 if (value.markers && Array.isArray(value.markers)) { - // // console.log(` → 마커 ${value.markers.length}개 추가`); allMarkers.push(...value.markers); } // 폴리곤 병합 if (value.polygons && Array.isArray(value.polygons)) { - // // console.log(` → 폴리곤 ${value.polygons.length}개 추가`); allPolygons.push(...value.polygons); } } else if (result.status === "rejected") { @@ -152,10 +142,6 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) { } }); - // // console.log(`✅ 총 ${allMarkers.length}개의 마커, ${allPolygons.length}개의 폴리곤 로딩 완료`); - // // console.log("📍 최종 마커 데이터:", allMarkers); - // // console.log("🔷 최종 폴리곤 데이터:", allPolygons); - // 이전 마커 위치와 비교하여 진행 방향 계산 const markersWithHeading = allMarkers.map((marker) => { const prevMarker = prevMarkers.find((pm) => pm.id === marker.id); @@ -192,7 +178,6 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) { // 수동 새로고침 핸들러 const handleManualRefresh = useCallback(() => { - // // console.log("🔄 수동 새로고침 버튼 클릭"); loadMultipleDataSources(); }, [loadMultipleDataSources]); @@ -200,8 +185,6 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) { const loadRestApiData = async ( source: ChartDataSource, ): Promise<{ markers: MarkerData[]; polygons: PolygonData[] }> => { - // // console.log(`🌐 REST API 데이터 로딩 시작:`, source.name, `mapDisplayType:`, source.mapDisplayType); - if (!source.endpoint) { throw new Error("API endpoint가 없습니다."); } @@ -256,13 +239,12 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) { // 텍스트 형식 데이터 체크 (기상청 API 등) if (data && typeof data === "object" && data.text && typeof data.text === "string") { - // // console.log("📄 텍스트 형식 데이터 감지, CSV 파싱 시도"); const parsedData = parseTextData(data.text); if (parsedData.length > 0) { - // // console.log(`✅ CSV 파싱 성공: ${parsedData.length}개 행`); // 컬럼 매핑 적용 const mappedData = applyColumnMapping(parsedData, source.columnMapping); - return convertToMapData(mappedData, source.name || source.id || "API", source.mapDisplayType, source); + const result = convertToMapData(mappedData, source.name || source.id || "API", source.mapDisplayType, source); + return result; } } @@ -280,15 +262,14 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) { const mappedRows = applyColumnMapping(rows, source.columnMapping); // 마커와 폴리곤으로 변환 (mapDisplayType + dataSource 전달) - return convertToMapData(mappedRows, source.name || source.id || "API", source.mapDisplayType, source); + const finalResult = convertToMapData(mappedRows, source.name || source.id || "API", source.mapDisplayType, source); + return finalResult; }; // Database 데이터 로딩 const loadDatabaseData = async ( source: ChartDataSource, ): Promise<{ markers: MarkerData[]; polygons: PolygonData[] }> => { - // // console.log(`💾 Database 데이터 로딩 시작:`, source.name, `mapDisplayType:`, source.mapDisplayType); - if (!source.query) { throw new Error("SQL 쿼리가 없습니다."); } @@ -330,7 +311,6 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) { // XML 데이터 파싱 (UTIC API 등) const parseXmlData = (xmlText: string): any[] => { try { - // // console.log(" 📄 XML 파싱 시작"); const parser = new DOMParser(); const xmlDoc = parser.parseFromString(xmlText, "text/xml"); @@ -349,7 +329,6 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) { results.push(obj); } - // // console.log(` ✅ XML 파싱 완료: ${results.length}개 레코드`); return results; } catch (error) { console.error(" ❌ XML 파싱 실패:", error); @@ -360,11 +339,8 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) { // 텍스트 데이터 파싱 (CSV, 기상청 형식 등) const parseTextData = (text: string): any[] => { try { - // // console.log(" 🔍 원본 텍스트 (처음 500자):", text.substring(0, 500)); - // XML 형식 감지 if (text.trim().startsWith("")) { - // // console.log(" 📄 XML 형식 데이터 감지"); return parseXmlData(text); } @@ -373,8 +349,6 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) { return trimmed && !trimmed.startsWith("#") && !trimmed.startsWith("=") && !trimmed.startsWith("---"); }); - // // console.log(` 📝 유효한 라인: ${lines.length}개`); - if (lines.length === 0) return []; // CSV 형식으로 파싱 @@ -384,8 +358,6 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) { const line = lines[i]; const values = line.split(",").map((v) => v.trim().replace(/,=$/g, "")); - // // console.log(` 라인 ${i}:`, values); - // 기상특보 형식: 지역코드, 지역명, 하위코드, 하위지역명, 발표시각, 특보종류, 등급, 발표상태, 설명 if (values.length >= 4) { const obj: any = { @@ -408,7 +380,6 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) { } } - // // console.log(" 📊 최종 파싱 결과:", result.length, "개"); return result; } catch (error) { console.error(" ❌ 텍스트 파싱 오류:", error); @@ -423,23 +394,15 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) { mapDisplayType?: "auto" | "marker" | "polygon", dataSource?: ChartDataSource, ): { markers: MarkerData[]; polygons: PolygonData[] } => { - // // console.log(`🔄 ${sourceName} 데이터 변환 시작:`, rows.length, "개 행"); - // // console.log(` 📌 mapDisplayType:`, mapDisplayType, `(타입: ${typeof mapDisplayType})`); - // // console.log(` 🎨 마커 색상:`, dataSource?.markerColor, `폴리곤 색상:`, dataSource?.polygonColor); - if (rows.length === 0) return { markers: [], polygons: [] }; const markers: MarkerData[] = []; const polygons: PolygonData[] = []; rows.forEach((row, index) => { - // // console.log(` 행 ${index}:`, row); - // 텍스트 데이터 체크 (기상청 API 등) if (row && typeof row === "object" && row.text && typeof row.text === "string") { - // // console.log(" 📄 텍스트 형식 데이터 감지, CSV 파싱 시도"); const parsedData = parseTextData(row.text); - // // console.log(` ✅ CSV 파싱 결과: ${parsedData.length}개 행`); // 파싱된 데이터를 재귀적으로 변환 (색상 정보 전달) const result = convertToMapData(parsedData, sourceName, mapDisplayType, dataSource); @@ -450,7 +413,6 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) { // 폴리곤 데이터 체크 (coordinates 필드가 배열인 경우 또는 강제 polygon 모드) if (row.coordinates && Array.isArray(row.coordinates) && row.coordinates.length > 0) { - // // console.log(` → coordinates 발견:`, row.coordinates.length, "개"); // coordinates가 [lat, lng] 배열의 배열인지 확인 const firstCoord = row.coordinates[0]; if (Array.isArray(firstCoord) && firstCoord.length === 2) { @@ -460,7 +422,7 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) { name: row.name || row.title || `영역 ${index + 1}`, coordinates: row.coordinates as [number, number][], status: row.status || row.level, - description: row.description || JSON.stringify(row, null, 2), + description: JSON.stringify(row, null, 2), // 항상 전체 row 객체를 JSON으로 저장 source: sourceName, color: dataSource?.polygonColor || getColorByStatus(row.status || row.level), }); @@ -471,13 +433,12 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) { // 지역명으로 해상 구역 확인 (auto 또는 polygon 모드일 때만) const regionName = row.name || row.area || row.region || row.location || row.subRegion; if (regionName && MARITIME_ZONES[regionName] && mapDisplayType !== "marker") { - // // console.log(` → 해상 구역 발견: ${regionName}, 폴리곤으로 처리`); polygons.push({ id: `${sourceName}-polygon-${index}-${row.code || row.id || Date.now()}`, // 고유 ID 생성 name: regionName, coordinates: MARITIME_ZONES[regionName] as [number, number][], status: row.status || row.level, - description: row.description || `${row.type || ""} ${row.level || ""}`.trim() || JSON.stringify(row, null, 2), + description: JSON.stringify(row, null, 2), // 항상 전체 row 객체를 JSON으로 저장 source: sourceName, color: dataSource?.polygonColor || getColorByStatus(row.status || row.level), }); @@ -494,7 +455,6 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) { (row.code || row.areaCode || row.regionCode || row.tmFc || row.stnId) ) { const regionCode = row.code || row.areaCode || row.regionCode || row.tmFc || row.stnId; - // // console.log(` → 지역 코드 발견: ${regionCode}, 위도/경도 변환 시도`); const coords = getCoordinatesByRegionCode(regionCode); if (coords) { lat = coords.lat; @@ -506,7 +466,6 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) { // 지역명으로도 시도 if ((lat === undefined || lng === undefined) && (row.name || row.area || row.region || row.location)) { const regionName = row.name || row.area || row.region || row.location; - // // console.log(` → 지역명 발견: ${regionName}, 위도/경도 변환 시도`); const coords = getCoordinatesByRegionName(regionName); if (coords) { lat = coords.lat; @@ -525,7 +484,7 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) { name: regionName, coordinates: [], // GeoJSON에서 좌표를 가져올 것 status: row.status || row.level, - description: row.description || JSON.stringify(row, null, 2), + description: JSON.stringify(row, null, 2), // 항상 전체 row 객체를 JSON으로 저장 source: sourceName, color: dataSource?.polygonColor || getColorByStatus(row.status || row.level), }); @@ -537,7 +496,6 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) { // 위도/경도가 있고 polygon 모드가 아니면 마커로 처리 if (lat !== undefined && lng !== undefined && (mapDisplayType as string) !== "polygon") { - // // console.log(` → 마커로 처리: (${lat}, ${lng})`); markers.push({ id: `${sourceName}-marker-${index}-${row.code || row.id || Date.now()}`, // 고유 ID 생성 lat: Number(lat), @@ -546,7 +504,7 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) { longitude: Number(lng), name: row.name || row.title || row.area || row.region || `위치 ${index + 1}`, status: row.status || row.level, - description: row.description || JSON.stringify(row, null, 2), + description: JSON.stringify(row, null, 2), // 항상 전체 row 객체를 JSON으로 저장 source: sourceName, color: dataSource?.markerColor || "#3b82f6", // 사용자 지정 색상 또는 기본 파랑 }); @@ -560,7 +518,7 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) { name: regionName, coordinates: [], // GeoJSON에서 좌표를 가져올 것 status: row.status || row.level, - description: row.description || JSON.stringify(row, null, 2), + description: JSON.stringify(row, null, 2), // 항상 전체 row 객체를 JSON으로 저장 source: sourceName, color: dataSource?.polygonColor || getColorByStatus(row.status || row.level), }); @@ -571,7 +529,6 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) { } }); - // // console.log(`✅ ${sourceName}: 마커 ${markers.length}개, 폴리곤 ${polygons.length}개 변환 완료`); return { markers, polygons }; }; @@ -627,6 +584,97 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) { // 해상 구역 폴리곤 좌표 (기상청 특보 구역 기준) const MARITIME_ZONES: Record> = { + // 서해 해역 + "인천·경기북부앞바다": [ + [37.8, 125.8], + [37.8, 126.5], + [37.3, 126.5], + [37.3, 125.8], + ], + "인천·경기남부앞바다": [ + [37.3, 125.7], + [37.3, 126.4], + [36.8, 126.4], + [36.8, 125.7], + ], + 충남북부앞바다: [ + [36.8, 125.6], + [36.8, 126.3], + [36.3, 126.3], + [36.3, 125.6], + ], + 충남남부앞바다: [ + [36.3, 125.5], + [36.3, 126.2], + [35.8, 126.2], + [35.8, 125.5], + ], + 전북북부앞바다: [ + [35.8, 125.4], + [35.8, 126.1], + [35.3, 126.1], + [35.3, 125.4], + ], + 전북남부앞바다: [ + [35.3, 125.3], + [35.3, 126.0], + [34.8, 126.0], + [34.8, 125.3], + ], + 전남북부서해앞바다: [ + [35.5, 125.2], + [35.5, 125.9], + [35.0, 125.9], + [35.0, 125.2], + ], + 전남중부서해앞바다: [ + [35.0, 125.1], + [35.0, 125.8], + [34.5, 125.8], + [34.5, 125.1], + ], + 전남남부서해앞바다: [ + [34.5, 125.0], + [34.5, 125.7], + [34.0, 125.7], + [34.0, 125.0], + ], + 서해중부안쪽먼바다: [ + [37.5, 124.5], + [37.5, 126.0], + [36.0, 126.0], + [36.0, 124.5], + ], + 서해중부바깥먼바다: [ + [37.5, 123.5], + [37.5, 125.0], + [36.0, 125.0], + [36.0, 123.5], + ], + 서해남부북쪽안쪽먼바다: [ + [36.0, 124.5], + [36.0, 126.0], + [35.0, 126.0], + [35.0, 124.5], + ], + 서해남부북쪽바깥먼바다: [ + [36.0, 123.5], + [36.0, 125.0], + [35.0, 125.0], + [35.0, 123.5], + ], + 서해남부남쪽안쪽먼바다: [ + [35.0, 124.0], + [35.0, 125.5], + [34.0, 125.5], + [34.0, 124.0], + ], + 서해남부남쪽바깥먼바다: [ + [35.0, 123.0], + [35.0, 124.5], + [33.5, 124.5], + [33.5, 123.0], + ], // 제주도 해역 제주도남부앞바다: [ [33.25, 126.0], @@ -896,7 +944,6 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) { try { const response = await fetch("/geojson/korea-municipalities.json"); const data = await response.json(); - // // console.log("🗺️ GeoJSON 로드 완료:", data.features?.length, "개 시/군/구"); setGeoJsonData(data); } catch (err) { console.error("❌ GeoJSON 로드 실패:", err); @@ -982,7 +1029,13 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) {

{error}

) : ( - + {/* 폴리곤 렌더링 */} @@ -1069,14 +1122,57 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) { }); if (matchingPolygon) { - layer.bindPopup(` -
-
${matchingPolygon.name}
- ${matchingPolygon.source ? `
출처: ${matchingPolygon.source}
` : ""} - ${matchingPolygon.status ? `
상태: ${matchingPolygon.status}
` : ""} - ${matchingPolygon.description ? `
${matchingPolygon.description}
` : ""} -
- `); + // 폴리곤의 데이터 소스 찾기 + const polygonDataSource = dataSources?.find((ds) => ds.name === matchingPolygon.source); + const popupFields = polygonDataSource?.popupFields; + + let popupContent = ""; + + // popupFields가 설정되어 있으면 설정된 필드만 표시 + if (popupFields && popupFields.length > 0 && matchingPolygon.description) { + try { + const parsed = JSON.parse(matchingPolygon.description); + popupContent = ` +
+ ${matchingPolygon.source ? `
📡 ${matchingPolygon.source}
` : ""} +
+
상세 정보
+
+ ${popupFields + .map((field) => { + const value = parsed[field.fieldName]; + if (value === undefined || value === null) return ""; + return `
${field.label}: ${value}
`; + }) + .join("")} +
+
+
+ `; + } catch (error) { + // JSON 파싱 실패 시 기본 표시 + popupContent = ` +
+
${matchingPolygon.name}
+ ${matchingPolygon.source ? `
출처: ${matchingPolygon.source}
` : ""} + ${matchingPolygon.status ? `
상태: ${matchingPolygon.status}
` : ""} + ${matchingPolygon.description ? `
${matchingPolygon.description}
` : ""} +
+ `; + } + } else { + // popupFields가 없으면 전체 데이터 표시 + popupContent = ` +
+
${matchingPolygon.name}
+ ${matchingPolygon.source ? `
출처: ${matchingPolygon.source}
` : ""} + ${matchingPolygon.status ? `
상태: ${matchingPolygon.status}
` : ""} + ${matchingPolygon.description ? `
${matchingPolygon.description}
` : ""} +
+ `; + } + + layer.bindPopup(popupContent); } }} /> @@ -1089,33 +1185,91 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) { {/* 폴리곤 렌더링 (해상 구역만) */} {polygons .filter((p) => MARITIME_ZONES[p.name]) - .map((polygon) => ( - - -
-
{polygon.name}
- {polygon.source && ( -
출처: {polygon.source}
- )} - {polygon.status &&
상태: {polygon.status}
} - {polygon.description && ( -
-
{polygon.description}
-
- )} -
-
-
- ))} + .map((polygon) => { + // 폴리곤의 데이터 소스 찾기 + const polygonDataSource = dataSources?.find((ds) => ds.name === polygon.source); + const popupFields = polygonDataSource?.popupFields; + + return ( + + +
+ {/* popupFields가 설정되어 있으면 설정된 필드만 표시 */} + {popupFields && popupFields.length > 0 && polygon.description ? ( + (() => { + try { + const parsed = JSON.parse(polygon.description); + return ( + <> + {polygon.source && ( +
+
📡 {polygon.source}
+
+ )} +
+
상세 정보
+
+ {popupFields.map((field, idx) => { + const value = parsed[field.fieldName]; + if (value === undefined || value === null) return null; + return ( +
+ {field.label}:{" "} + {String(value)} +
+ ); + })} +
+
+ + ); + } catch (error) { + // JSON 파싱 실패 시 기본 표시 + return ( + <> +
{polygon.name}
+ {polygon.source && ( +
출처: {polygon.source}
+ )} + {polygon.status &&
상태: {polygon.status}
} + {polygon.description && ( +
+
{polygon.description}
+
+ )} + + ); + } + })() + ) : ( + // popupFields가 없으면 전체 데이터 표시 + <> +
{polygon.name}
+ {polygon.source && ( +
출처: {polygon.source}
+ )} + {polygon.status &&
상태: {polygon.status}
} + {polygon.description && ( +
+
{polygon.description}
+
+ )} + + )} +
+
+
+ ); + })} {/* 마커 렌더링 */} {markers.map((marker) => { @@ -1227,8 +1381,9 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) {
{marker.description && (() => { - const firstDataSource = dataSources?.[0]; - const popupFields = firstDataSource?.popupFields; + // 마커의 소스에 해당하는 데이터 소스 찾기 + const sourceDataSource = dataSources?.find((ds) => ds.name === marker.source); + const popupFields = sourceDataSource?.popupFields; // popupFields가 설정되어 있으면 설정된 필드만 표시 if (popupFields && popupFields.length > 0) {