sn: header에 site-id 추가

This commit is contained in:
2026-04-15 10:18:33 +09:00
parent 812293b4fa
commit 4dacfdaadc
16 changed files with 310 additions and 204 deletions
@@ -18,7 +18,7 @@ import { Button, TableHead, TableRow } from '@common/components/ui';
import React, { useState, useEffect } from 'react';
import { untrtSwgRainCol3, untrtSwgRainCol4, untrtSwgRainCol5 } from '../common/table';
import { VerticalTable } from '@/components/ui/verticalTable';
import { getData } from "@/components/lib/api";
import { getRawJson } from "@/components/lib/api";
import { formatDisplayData } from '@/components/lib/utils/formatDisplayValue';
import { Label } from '@radix-ui/react-label';
@@ -65,22 +65,7 @@ const Ssc = ({ featureData, isMainSsc, chmbrXcnts, chmbrYdnts, onSaveClick }: {
size: '3',
});
let result;
const res = await fetch(`/sewerage-service/api/v1/onsemiro/getSwgRainsByGeomety?${queryParams.toString()}`, {
method: "GET",
});
if (res.ok) {
// 응답이 비어있는지 확인
const contentLength = res.headers.get('content-length');
if (contentLength === '0') result = null;
// 응답 텍스트를 먼저 가져와서 빈 문자열인지 확인
const text = await res.text();
if (!text || text.trim() === '') result = null;
// JSON 파싱 시도
result = JSON.parse(text);
}
const result = await getRawJson(`/sewerage-service/api/v1/onsemiro/getSwgRainsByGeomety?${queryParams.toString()}`);
// helper: "YYYY-MM-DD HH:MM:SS" -> "MM-DD HH:MM"
const formatNoYearNoSec = (val: any) => {
@@ -138,22 +123,7 @@ const Ssc = ({ featureData, isMainSsc, chmbrXcnts, chmbrYdnts, onSaveClick }: {
size: '3',
});
let result;
const res = await fetch(`/sewerage-service/api/v1/onsemiro/swgRain/${adstDstrCd}/${manageNo}/mntrngs?${queryParams.toString()}`, {
method: "GET",
});
if (res.ok) {
// 응답이 비어있는지 확인
const contentLength = res.headers.get('content-length');
if (contentLength === '0') result = null;
// 응답 텍스트를 먼저 가져와서 빈 문자열인지 확인
const text = await res.text();
if (!text || text.trim() === '') result = null;
// JSON 파싱 시도
result = JSON.parse(text);
}
const result = await getRawJson(`/sewerage-service/api/v1/onsemiro/swgRain/${adstDstrCd}/${manageNo}/mntrngs?${queryParams.toString()}`);
// 각 아이템 전처리
const processedDataArray = result.content.map((data: any) => {
@@ -355,4 +325,4 @@ const Ssc = ({ featureData, isMainSsc, chmbrXcnts, chmbrYdnts, onSaveClick }: {
)
}
export default Ssc
export default Ssc
@@ -15,40 +15,20 @@
import { VerticalTable } from '@/components/ui/verticalTable';
import { useEffect, useState } from 'react';
import GisApp from "@common/business_components/ui/map/lib/gis.core";
import { getData } from '@/components/lib/api';
// 상류관로 보기 함수 (FeatureClickEventPopup에서 사용)
export async function searchUpperPpls(gisMap: any, fcltMngNo: any) {
const BASE_URL = "/sewerage-service/api";
const res = await fetch(`${BASE_URL}/v1/fcltmhl/upperPpls/${fcltMngNo}`, {
method: "GET",
});
let data: any = null;
if (res.ok) {
try {
// 응답이 비어있는지 확인
const contentLength = res.headers.get("content-length");
if (contentLength === "0") {
alert(`해당 맨홀의 상류관로를 찾을 수 없습니다.`);
return;
}
try {
data = await getData(`v1/fcltmhl/upperPpls/${fcltMngNo}`);
} catch (error) {
console.error("JSON parsing error:", error);
alert(`해당 맨홀의 상류관로를 찾을 수 없습니다.`);
return;
}
// 응답 텍스트를 먼저 가져와서 빈 문자열인지 확인
const text = await res.text();
if (!text || text.trim() === "") {
alert(`해당 맨홀의 상류관로를 찾을 수 없습니다.`);
return;
}
// JSON 파싱 시도
const response = JSON.parse(text);
data = response.data;
} catch (error) {
console.error("JSON parsing error:", error);
alert(`해당 맨홀의 상류관로를 찾을 수 없습니다.`);
return;
}
} else {
if (!data) {
alert(`해당 맨홀의 상류관로를 찾을 수 없습니다.`);
return;
}
@@ -143,4 +123,4 @@ const Smh = ({ featureData, onSaveClick }: { featureData: any, onSaveClick?: ()
)
}
export default Smh;
export default Smh;
+2 -17
View File
@@ -18,7 +18,7 @@
"use client";
import Gis from "@common/business_components/ui/map/Gis";
import { getData } from "../../components/lib/api";
import { getRawJson } from "../../components/lib/api";
import { useCallback, useEffect, useRef, useState } from "react";
import GisApp from "@common/business_components/ui/map/lib/gis.core";
import SearchModal from "./searchModal/searchModal";
@@ -43,22 +43,7 @@ const Map = () => {
const fetchData = async () => {
try {
let data;
const res = await fetch("/sewerage-service/api/v1/map/lyrStngs/SN_NETWORK", {
method: "GET",
});
if (res.ok) {
// 응답이 비어있는지 확인
const contentLength = res.headers.get('content-length');
if (contentLength === '0') data = null;
// 응답 텍스트를 먼저 가져와서 빈 문자열인지 확인
const text = await res.text();
if (!text || text.trim() === '') data = null;
// JSON 파싱 시도
data = JSON.parse(text);
}
const data = await getRawJson("/sewerage-service/api/v1/map/lyrStngs/SN_NETWORK");
setTreeData(data);
} catch (error) {
console.error('JSON parsing error:', error);
@@ -21,6 +21,7 @@
// FileUpload.js
import React, { useState } from 'react';
import axios from 'axios';
import { SITE_ID } from '@/shared/config';
const SEWERAGE_URL = "/sewerage-service/api";
function FileUpload() {
@@ -49,6 +50,7 @@ function FileUpload() {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Site-Id": SITE_ID,
},
body: JSON.stringify({authToken: auth})
}).then((data) => {
@@ -58,7 +60,11 @@ function FileUpload() {
const handleFindUserOna = () => {
const auth = getCookie("Authorization");
if (auth) console.log("쿠키 auth : " + auth);
fetch("/server/user-service/api/v1/users/65a00f65-8460-49af-98ec-042977e56f4b").then((data) => {
fetch("/server/user-service/api/v1/users/65a00f65-8460-49af-98ec-042977e56f4b", {
headers: {
"X-Site-Id": SITE_ID,
},
}).then((data) => {
console.log(data);
});
};
@@ -86,7 +92,11 @@ function FileUpload() {
formData.append('inpFile', file);
try {
const response = await axios.post('http://localhost:8883/sewerage-service/api/v1/rdiiAnls/savewithfile', formData);
const response = await axios.post('http://localhost:8883/sewerage-service/api/v1/rdiiAnls/savewithfile', formData, {
headers: {
"X-Site-Id": SITE_ID,
},
});
alert('업로드 성공: ' + response.data.message);
} catch (err) {
@@ -17,6 +17,7 @@ import { useState, useEffect } from 'react'
import LoadingOverlay from '@/components/ui/LoadingBar'
import { UITab, UiTable } from '@common/business_components/ui'
import * as XLSX from 'xlsx';
import { getBlobData } from '@/components/lib/api';
// import { Table, TableBody, TableHeader } from "@common/components/ui";
// import { HeadMakeFloodPre } from './headMakeFloodPre';
// import { InputTableBody } from './inputTableBody';
@@ -54,12 +55,7 @@ const TableModal: React.FC<TableModalProps> = ({ fileNm, atflId }) => {
return false
}
const response = await fetch(`/attach-service/api/v1/download/${atflId}`);
if (!response.ok) {
throw new Error('파일을 가져오는 중 오류가 발생했습니다.');
}
const blob = await response.blob();
const blob = await getBlobData(`/attach-service/api/v1/download/${atflId}`);
const file = new File([blob], fileNm, {
type: blob.type || 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
@@ -267,4 +263,4 @@ const TableModal: React.FC<TableModalProps> = ({ fileNm, atflId }) => {
)
};
export default TableModal;
export default TableModal;
@@ -19,6 +19,7 @@ import { UITab, UiTable } from '@common/business_components/ui'
import * as XLSX from 'xlsx';
import { HeadMakeColSpan } from '@common/business_components/ui/table/HeadMakeColSpan';
import { oldPpTable2HeadList, oldPpTable2NodeList, oldPpTable6HeadList, oldPpTable6NodeList } from './headList';
import { getBlobData } from '@/components/lib/api';
const TABS = [
{ val: "1", text: "raw data" },
@@ -63,12 +64,7 @@ const TableModal: React.FC<TableModalProps> = ({ fileNm, atflId }) => {
return false;
}
const response = await fetch(`/attach-service/api/v1/download/${atflId}`);
if (!response.ok) {
throw new Error('파일을 가져오는 중 오류가 발생했습니다.');
}
const blob = await response.blob();
const blob = await getBlobData(`/attach-service/api/v1/download/${atflId}`);
const file = new File([blob], fileNm, {
type: blob.type || 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
@@ -476,4 +472,4 @@ const TableModal: React.FC<TableModalProps> = ({ fileNm, atflId }) => {
)
};
export default TableModal;
export default TableModal;
+67 -9
View File
@@ -11,12 +11,25 @@
*/
import { useEffect, useState } from "react";
import axios from 'axios';
import { SITE_ID } from "@/shared/config";
const BASE_URL = '/sewerage-service/api';
const DEFAULT_HEADERS = {
"X-Site-Id": SITE_ID,
};
const withSiteIdHeaders = (headers: HeadersInit = {}) => ({
...headers,
...DEFAULT_HEADERS,
});
const resolveApiUrl = (url: string) =>
url.startsWith('/') ? `${url}` : `${BASE_URL}/${url}`;
export const getData = async (url: string): Promise<any> => {
const res = await fetch(url.startsWith('/') ? `${url}` : `${BASE_URL}/${url}`, {
const res = await fetch(resolveApiUrl(url), {
method: "GET",
headers: withSiteIdHeaders(),
});
if (res.ok) {
try {
@@ -47,9 +60,9 @@ export const getData = async (url: string): Promise<any> => {
export const postData = async (url: string, data: object): Promise<any> => {
const options: RequestInit = {
method: "POST",
headers: {
headers: withSiteIdHeaders({
"Content-Type": "application/json",
},
}),
body: JSON.stringify(data),
};
const res = await fetch(`${BASE_URL}/${url}`, options);
@@ -62,6 +75,7 @@ export const postData = async (url: string, data: object): Promise<any> => {
export const postMutiPart = async (url: string, data: FormData): Promise<any> => {
const res = await axios.post(`${BASE_URL}/${url}`, data, {
headers: DEFAULT_HEADERS,
timeout: 600000, // 10분
});
console.log('postMultiPart response:', res);
@@ -71,9 +85,9 @@ export const postMutiPart = async (url: string, data: FormData): Promise<any> =>
export const putData = async (url: string, data: object): Promise<any> => {
const options: RequestInit = {
method: "PUT",
headers: {
headers: withSiteIdHeaders({
"Content-Type": "application/json",
},
}),
body: JSON.stringify(data),
};
const res = await fetch(`${BASE_URL}/${url}`, options);
@@ -85,20 +99,62 @@ export const putData = async (url: string, data: object): Promise<any> => {
export const deleteData = async (url: string, data?: object): Promise<any> => {
const options: RequestInit = {
method: "DELETE",
headers: {
headers: withSiteIdHeaders({
"Content-Type": "application/json",
},
}),
body: data ? JSON.stringify(data) : undefined,
};
const res = await fetch(url.startsWith('/') ? `${url}` : `${BASE_URL}/${url}`, options);
const res = await fetch(resolveApiUrl(url), options);
if (res.status === 204) return true
return res;
};
export const getRawJson = async (url: string): Promise<any> => {
const res = await fetch(resolveApiUrl(url), {
method: "GET",
headers: withSiteIdHeaders(),
});
if (!res.ok) {
throw new Error('Failed to fetch data');
}
const contentLength = res.headers.get('content-length');
if (contentLength === '0') {
return null;
}
const text = await res.text();
if (!text || text.trim() === '') {
return null;
}
try {
return JSON.parse(text);
} catch (error) {
console.error('JSON parsing error:', error);
throw new Error('Invalid JSON response from server');
}
};
export const getBlobData = async (url: string): Promise<Blob> => {
const res = await fetch(resolveApiUrl(url), {
method: "GET",
headers: withSiteIdHeaders(),
});
if (!res.ok) {
throw new Error('Failed to fetch data');
}
return res.blob();
};
// DC API 호출
export const getDcData = async (url: string, resType: string = "json"): Promise<any> => {
const res = await fetch('/dc-service' + url, {
method: "GET",
headers: withSiteIdHeaders(),
});
if (res.ok) {
let data;
@@ -114,6 +170,7 @@ export const getDcData = async (url: string, resType: string = "json"): Promise<
export const getCmsData = async (url: string, data?: object): Promise<any> => {
const res = await fetch('/cms-service' + url, {
method: "GET",
headers: withSiteIdHeaders(),
});
if (res.ok) {
const data = await res.json();
@@ -126,6 +183,7 @@ export const getCmsData = async (url: string, data?: object): Promise<any> => {
export const getGeoData = async (url: string, data?: object): Promise<any> => {
const res = await fetch('/geoserver' + url, {
method: "GET",
headers: withSiteIdHeaders(),
});
if (res.ok) {
const data = await res.json();
@@ -157,4 +215,4 @@ export const useCookieAuth = () => {
}, []);
return data;
};
};
@@ -13,6 +13,7 @@
* - 첨부파일 삭제및 Soft Delete 삭제(첨부파일코드로 삭제): DELETE /api/v1/atfl/attachments/atflCd/{atflCd} (attach-service)
*/
import { getCookie } from "@/shared/lib";
import { SITE_ID } from "@/shared/config";
import { ENDPOINT } from "../api/attachment/attachment-service";
const AUTH_USER_ID = process.env.NEXT_PUBLIC_AUTH_USER_ID || "token-id";
@@ -43,6 +44,7 @@ export const handleDelete = async (
method: "DELETE",
headers: {
"Content-Type": "application/json",
"X-Site-Id": SITE_ID,
[CLAIM_NAME]: getCookie(CLAIM_NAME) ?? "",
[AUTH_USER_ID]: getCookie(AUTH_USER_ID) ?? "",
},
@@ -118,4 +120,4 @@ export const handleDelete = async (
// options?.handleDeleteError?.(deleteError);
// throw deleteError;
// }
// };
// };
@@ -10,10 +10,15 @@
* - Fetch 기반 HTTP 요청 처리
*/
import { SITE_ID } from "@/shared/config";
export const handleDownload = async (api: string, fileNm: string) => {
try {
const response = await fetch(api, {
method: 'GET',
headers: {
"X-Site-Id": SITE_ID,
},
});
if (!response.ok) throw new Error('파일 다운로드 실패');
@@ -34,4 +39,4 @@ export const fileDownload = (data: Blob, fileName: string) => { // Blob데이터
// 메모리 해제
link.remove();
window.URL.revokeObjectURL(url);
}
}
@@ -7,7 +7,7 @@ import { downloadExcelWithKoreanHeaders } from "@common/business_components/ui";
import Stroke from "ol/style/Stroke.js";
import Style from "ol/style/Style.js";
import useStore from "@/store/useStore";
import { getData } from "@/components/lib/api";
import { getRawJson } from "@/components/lib/api";
import AgedPipeFilterPanel from "./AgedPipeFilterPanel";
import AgedPipeDetailPanel from "./AgedPipeDetailPanel";
import AgedPipeListModal from "./AgedPipeListModal";
@@ -102,19 +102,7 @@ const Map = () => {
useEffect(() => {
const fetchData = async () => {
try {
let data;
const response = await fetch("/sewerage-service/api/v1/map/lyrStngs/SN_NETWORK", {
method: "GET",
});
if (response.ok) {
const contentLength = response.headers.get("content-length");
if (contentLength === "0") data = null;
const text = await response.text();
if (!text || text.trim() === "") data = null;
else data = JSON.parse(text);
}
const data = await getRawJson("/sewerage-service/api/v1/map/lyrStngs/SN_NETWORK");
setTreeData(data);
} catch (error) {
console.error("노후관로 레이어 설정 조회 중 오류:", error);
@@ -18,7 +18,7 @@ import { Button, TableHead, TableRow } from '@common/components/ui';
import React, { useState, useEffect } from 'react';
import { untrtSwgRainCol3, untrtSwgRainCol4, untrtSwgRainCol5 } from '../common/table';
import { VerticalTable } from '@/components/ui/verticalTable';
import { getData } from "@/components/lib/api";
import { getRawJson } from "@/components/lib/api";
import { formatDisplayData } from '@/components/lib/utils/formatDisplayValue';
import { Label } from '@radix-ui/react-label';
@@ -65,22 +65,7 @@ const Ssc = ({ featureData, isMainSsc, chmbrXcnts, chmbrYdnts, onSaveClick }: {
size: '3',
});
let result;
const res = await fetch(`/sewerage-service/api/v1/onsemiro/getSwgRainsByGeomety?${queryParams.toString()}`, {
method: "GET",
});
if (res.ok) {
// 응답이 비어있는지 확인
const contentLength = res.headers.get('content-length');
if (contentLength === '0') result = null;
// 응답 텍스트를 먼저 가져와서 빈 문자열인지 확인
const text = await res.text();
if (!text || text.trim() === '') result = null;
// JSON 파싱 시도
result = JSON.parse(text);
}
const result = await getRawJson(`/sewerage-service/api/v1/onsemiro/getSwgRainsByGeomety?${queryParams.toString()}`);
// helper: "YYYY-MM-DD HH:MM:SS" -> "MM-DD HH:MM"
const formatNoYearNoSec = (val: any) => {
@@ -138,22 +123,7 @@ const Ssc = ({ featureData, isMainSsc, chmbrXcnts, chmbrYdnts, onSaveClick }: {
size: '3',
});
let result;
const res = await fetch(`/sewerage-service/api/v1/onsemiro/swgRain/${adstDstrCd}/${manageNo}/mntrngs?${queryParams.toString()}`, {
method: "GET",
});
if (res.ok) {
// 응답이 비어있는지 확인
const contentLength = res.headers.get('content-length');
if (contentLength === '0') result = null;
// 응답 텍스트를 먼저 가져와서 빈 문자열인지 확인
const text = await res.text();
if (!text || text.trim() === '') result = null;
// JSON 파싱 시도
result = JSON.parse(text);
}
const result = await getRawJson(`/sewerage-service/api/v1/onsemiro/swgRain/${adstDstrCd}/${manageNo}/mntrngs?${queryParams.toString()}`);
// 각 아이템 전처리
const processedDataArray = result.content.map((data: any) => {
@@ -355,4 +325,4 @@ const Ssc = ({ featureData, isMainSsc, chmbrXcnts, chmbrYdnts, onSaveClick }: {
)
}
export default Ssc
export default Ssc
@@ -15,40 +15,20 @@
import { VerticalTable } from '@/components/ui/verticalTable';
import { useEffect, useState } from 'react';
import GisApp from "@common/business_components/ui/map/lib/gis.core";
import { getData } from '@/components/lib/api';
// 상류관로 보기 함수 (FeatureClickEventPopup에서 사용)
export async function searchUpperPpls(gisMap: any, fcltMngNo: any) {
const BASE_URL = "/sewerage-service/api";
const res = await fetch(`${BASE_URL}/v1/fcltmhl/upperPpls/${fcltMngNo}`, {
method: "GET",
});
let data: any = null;
if (res.ok) {
try {
// 응답이 비어있는지 확인
const contentLength = res.headers.get("content-length");
if (contentLength === "0") {
alert(`해당 맨홀의 상류관로를 찾을 수 없습니다.`);
return;
}
try {
data = await getData(`v1/fcltmhl/upperPpls/${fcltMngNo}`);
} catch (error) {
console.error("JSON parsing error:", error);
alert(`해당 맨홀의 상류관로를 찾을 수 없습니다.`);
return;
}
// 응답 텍스트를 먼저 가져와서 빈 문자열인지 확인
const text = await res.text();
if (!text || text.trim() === "") {
alert(`해당 맨홀의 상류관로를 찾을 수 없습니다.`);
return;
}
// JSON 파싱 시도
const response = JSON.parse(text);
data = response.data;
} catch (error) {
console.error("JSON parsing error:", error);
alert(`해당 맨홀의 상류관로를 찾을 수 없습니다.`);
return;
}
} else {
if (!data) {
alert(`해당 맨홀의 상류관로를 찾을 수 없습니다.`);
return;
}
@@ -143,4 +123,4 @@ const Smh = ({ featureData, onSaveClick }: { featureData: any, onSaveClick?: ()
)
}
export default Smh;
export default Smh;
+2 -17
View File
@@ -18,7 +18,7 @@
"use client";
import Gis from "@common/business_components/ui/map/Gis";
import { getData } from "../../../components/lib/api";
import { getRawJson } from "../../../components/lib/api";
import { useCallback, useEffect, useRef, useState } from "react";
import GisApp from "@common/business_components/ui/map/lib/gis.core";
import SearchModal from "./searchModal/searchModal";
@@ -43,22 +43,7 @@ const Map = () => {
const fetchData = async () => {
try {
let data;
const res = await fetch("/sewerage-service/api/v1/map/lyrStngs/SN_NETWORK", {
method: "GET",
});
if (res.ok) {
// 응답이 비어있는지 확인
const contentLength = res.headers.get('content-length');
if (contentLength === '0') data = null;
// 응답 텍스트를 먼저 가져와서 빈 문자열인지 확인
const text = await res.text();
if (!text || text.trim() === '') data = null;
// JSON 파싱 시도
data = JSON.parse(text);
}
const data = await getRawJson("/sewerage-service/api/v1/map/lyrStngs/SN_NETWORK");
setTreeData(data);
} catch (error) {
console.error('JSON parsing error:', error);
+169
View File
@@ -0,0 +1,169 @@
/**
* ➡️ 경로 : 하수관망도 메인 페이지
*
* 📄 Map Page
* 지도 페이지 컴포넌트
*
* - OpenLayers 기반 GIS 지도 렌더링 및 상호작용
* - 레이어 트리 데이터 관리 및 동적 레이어 표시/숨김
* - 피처 검색 및 선택 기능 (SearchModal, ProcSearchBar)
* - 피처 클릭 이벤트 처리 및 상세 정보 팝업 표시
* - 수심(WaterDepth) 레이어 애니메이션 재생 기능
* - Zustand를 통한 검색 모달 상태 관리
*
* 🔗 사용 API
* - 레이어 설정 조회: GET /api/v1/map/lyrStngs/{lyrGrp} (sewerage-service)
*/
"use client";
import Gis from "@common/business_components/ui/map/Gis";
import { getRawJson } from "../../components/lib/api";
import { useCallback, useEffect, useRef, useState } from "react";
import GisApp from "@common/business_components/ui/map/lib/gis.core";
import SearchModal from "./searchModal/searchModal";
import FeatureClickEventPopup from "./components/popupComponent/featureClickEventPopup";
import { WaterDepth, WATER_DEPTH_LAYER_TYPES } from "./waterDepth/waterDepth";
import { ProcSearchBar } from "@common/business_components/ui/map/searchBar";
import useSearchModalStore from "@/store/useSearchModalStore";
const Map = () => {
const { isSearchModalOpen, setIsSearchModalOpen } = useSearchModalStore();
const [treeData, setTreeData] = useState([]); // 초기값을 객체 또는 null로 설정
const gisMaps = useRef<any | null>(null);
const [isOpen, setIsOpen] = useState(false); // 팝업 열기/닫기 상태
const [featureData, setFeatureData] = useState(null); // 선택된 피쳐 데이터
const [showWaterDepth, setShowWaterDepth] = useState(false); // WaterDepth 표시 여부
const [isPlaying, setIsPlaying] = useState(false);
useEffect(() => {
fetchData();
}, []);
const fetchData = async () => {
try {
const data = await getRawJson("/sewerage-service/api/v1/map/lyrStngs/SN_NETWORK");
setTreeData(data);
} catch (error) {
console.error('JSON parsing error:', error);
// JSON 파싱에 실패한 경우, 원본 텍스트 또는 null 반환
throw new Error('Invalid JSON response from server');
}
};
// SearchModal, GisContent 등에서 호출
const onFeatureSelect = (data: any) => {
let map = gisMaps.current;
if (!map) {
console.warn("지도가 초기화되지 않았습니다.");
return;
}
// 항상 같은 select 객체 사용
const select = GisApp.Module.select as any;
if (!select) return;
// 1. 기존 선택 초기화
select.resetSelectedFeature();
// 2. data가 OpenLayers Feature 인스턴스인지 확인
let foundFeature = null;
if (
data &&
typeof data.getGeometry === "function" &&
typeof data.setStyle === "function"
) {
// 이미 Feature 인스턴스라면 그대로 사용
foundFeature = data;
} else {
// 아니면 featureId로 맵에서 Feature 인스턴스를 찾아서 사용
const featureId = data.fid || data.ftrIdn || data.id;
map
.getLayers()
.getArray()
.forEach((layer: any) => {
const source = layer.getSource?.();
if (source?.getFeatureById) {
const feature = source.getFeatureById(String(featureId).trim());
if (feature) foundFeature = feature;
}
});
}
// 3. 피처 선택
if (foundFeature) {
let geometry = foundFeature.getGeometry();
if (geometry) {
let extent = geometry.getExtent();
map.getView().fit(extent, {
duration: 300,
padding: [50, 50, 50, 50],
maxZoom: 16,
constrainResolution: true,
nearest: true,
});
}
select.selectFeature(foundFeature, true);
} else {
console.warn("OpenLayers Feature를 찾을 수 없습니다:", data);
}
setFeatureData(data);
setIsOpen(true);
setIsSearchModalOpen(false);
};
const facilityTypes = [{ text: "하수처리시설", val: "tb_il_swage_prcs_fclt" }];
// WaterDepth 표시 여부를 결정하는 함수
const handleWaterDepthVisibility = useCallback((checkedIds: string[], checkedNodesData: any[]) => {
const waterDepthLayers = checkedNodesData.filter(node =>
WATER_DEPTH_LAYER_TYPES.includes(node.data?.lyrTyNm)
);
const hasWaterDepthLayer = waterDepthLayers.length > 0;
setShowWaterDepth(hasWaterDepthLayer);
}, []);
// 체크된 레이어 변경 처리 함수
const handleCheckedLayersChange = useCallback((checkedIds: string[], checkedNodesData: any[]) => {
handleWaterDepthVisibility(checkedIds, checkedNodesData);
}, [handleWaterDepthVisibility]);
return (
<>
<div className="bg-container">
<Gis
treeData={treeData}
isSearch={true}
gisMaps={gisMaps}
isMiniMap={false}
searchView={SearchModal}
detailView={FeatureClickEventPopup}
onFeatureSelect={onFeatureSelect}
featureData={featureData}
setFeatureData={setFeatureData}
isOpen={isOpen}
setIsOpen={setIsOpen}
isSearchModalOpen={isSearchModalOpen}
setIsSearchModalOpen={setIsSearchModalOpen}
onCheckedLayersChange={handleCheckedLayersChange}
searchBar={<ProcSearchBar gisMaps={gisMaps} facilityTypes={facilityTypes} />}
waterDepth={showWaterDepth ? WaterDepth : undefined}
isPlaying={isPlaying}
setIsPlaying={setIsPlaying as any}
targetLayerTypes={WATER_DEPTH_LAYER_TYPES as any}
checkInterval={2500}
showZoomLevel={true}
defaultZoom={7.5}
defaultLonLat={[127.75, 36]}
/>
</div>
</>
);
};
export default Map;
+11 -1
View File
@@ -5,6 +5,7 @@ import {
ACCESS_TOKEN,
AUTH_USER_ID,
CLAIM_NAME,
SITE_ID,
} from "@/shared/config";
import type { LoginService, ILogin } from "../model/login-interface";
import { tokenManager } from "../lib/token-manager";
@@ -26,6 +27,10 @@ export class LoginApiService implements LoginService {
tokenManager.clearAuthToken();
const response = await fetch(`/api/proxy${ENDPOINT.USER_SERVICE.LOGIN}`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Site-Id": SITE_ID,
},
body: JSON.stringify({
userId: payload.userId,
password: payload.password,
@@ -58,7 +63,12 @@ export class LoginApiService implements LoginService {
try {
const response = await fetch(
`/api/proxy${ENDPOINT.USER_SERVICE.SILENT_REFRESH}`,
{ method: "PUT" },
{
method: "PUT",
headers: {
"X-Site-Id": SITE_ID,
},
},
);
const data = await response.json();
@@ -8,6 +8,7 @@ import { useUserStore } from "@/shared/lib";
import { ENDPOINT, AUTH_USER_ID } from "@/shared/config";
import { isAtLoginRedirectUrl } from "@common/network";
import { getCookie } from "@/shared/lib";
import { tokenManager } from "./token-manager";
const LOGIN_PAGE_URL =
process.env.NEXT_PUBLIC_CM_LOGIN_PAGE_URL || "/auth/login";
@@ -38,6 +39,7 @@ const AuthWrapper = ({ children }: { children: React.ReactNode }) => {
await authCheck();
} catch (error) {
console.error(error);
tokenManager.clearAuthToken();
setUserInfo(null);
if (isAtLoginRedirectUrl(LOGIN_PAGE_URL)) return;