최정우 최정우 2023-04-18
230417 최정우 1. 대상자, 보호자 등록시, 주소 API(+좌표API) 적용 2. 기관 관리자 메인화면 Leaflet 지도 적용
1. 대상자, 보호자 등록시, 주소 API(+좌표API) 적용
2. 기관 관리자 메인화면 Leaflet 지도 적용
@bcf9ef5d78c7fbeeb64505edb153eeba11e40935
Global.js
--- Global.js
+++ Global.js
@@ -5,8 +5,11 @@
 const SERVICE_STATUS = process.env.NODE_ENV;//development, production
 const PORT = 80;
 const API_SERVER_HOST = 'localhost:8080'
-//const JUSO_API_KEY = 'U01TX0FVVEgyMDIzMDQxNzE2MTgyNzExMzY5MzU=='//실사용 Key
-const JUSO_API_KEY = 'U01TX0FVVEgyMDIyMTEyMTE3NDE1NzExMzI0MjU=';//실사용 Key 사용가능하면, 제거
+//const JUSO_API_KEY = 'U01TX0FVVEgyMDIzMDQxNzIxNDY1NjExMzY5NTg='//실사용 Key u-dolbom.com
+//const JUSO_CORRD_API_KEY = 'U01TX0FVVEgyMDIzMDQxNzE2MTgyNzExMzY5MzE=';//실사용 Key u-dolbom.com
+const JUSO_API_KEY = 'U01TX0FVVEgyMDIyMTEyMTE3NDE1NzExMzI0MjU=';//Test용
+const JUSO_CORRD_API_KEY = 'U01TX0FVVEgyMDIyMTEyMTE4MDIxOTExMzI0MzU=';//Test용
+
 
 module.exports = {
     PROJECT_NAME,
@@ -16,5 +19,6 @@
     SERVICE_STATUS,
     PORT,
     API_SERVER_HOST,
-    JUSO_API_KEY
+    JUSO_API_KEY,
+    JUSO_CORRD_API_KEY
 }
(No newline at end of file)
client/resources/css/main.css
--- client/resources/css/main.css
+++ client/resources/css/main.css
@@ -1327,4 +1327,35 @@
 
 }
 .senior-table span{display: none;}
-.senior-table span img{display: none; }
(No newline at end of file)
+.senior-table span img{display: none; }
+
+
+ul.list-box {
+  width: 100%;
+  max-height: 100px;
+  overflow: auto;
+  border: 1px solid #d3d3d3;
+  border-radius: 5px;
+}
+ul.list-box > li {
+  padding: 0.5rem 1rem;
+  text-align: left;
+  font-size: 1.3rem;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+ul.list-box > li:hover {
+  background: #ff5c00;
+  color: #fff;
+}
+ul.list-box > li.active {
+  background: #f25430;
+  color: #fff;
+}
+
+
+.leaflet-background-radius-icon{
+  background: #f25430;
+  border-radius: 50%;
+}
(No newline at end of file)
 
client/resources/files/images/house.png (Binary) (added)
+++ client/resources/files/images/house.png
Binary file is not shown
client/views/component/Modal_Guardian.jsx
--- client/views/component/Modal_Guardian.jsx
+++ client/views/component/Modal_Guardian.jsx
@@ -1,6 +1,9 @@
 import React from "react";
 import SubTitle from "./SubTitle.jsx";
 import CommonUtil from "../../resources/js/CommonUtil.js";
+import {JUSO_API_KEY, JUSO_CORRD_API_KEY} from "../../../Global.js";
+
+import proj4 from "proj4";
 
 export default function Modal_Guardian({ open, close, guardianManagementCallback, seniorId, guardianBySenior }) {
 
@@ -104,13 +107,18 @@
       alert("연락처를 입력해 주세요.");
       return false;
     }
-    if (isIdCheck == false) {
+    if (CommonUtil.isEmpty(guardian['guardian_id']) && isIdCheck == false) {
       alert("연락처 중복확인을 해주세요.");
       return false;
     }
     if (CommonUtil.isEmpty(guardian['user_address']) == true) {
       guardianRef.current['user_address'].focus();
       alert("주소를 입력해 주세요.");
+      return false;
+    }
+    if (CommonUtil.isEmpty(guardian['zip_no']) == true) {
+      guardianRef.current['user_address'].focus();
+      alert("검색을 통해 주소를 선택해 주세요.");
       return false;
     }
     if (CommonUtil.isEmpty(guardian['senior_relationship']) == true) {
@@ -201,6 +209,80 @@
 
 
 
+  //주소 검색 결과 객체
+  const [jusoList, setJusoList] = React.useState({
+    common: {
+      countPerPage: 10,
+      currentPage: 1,
+      errorCode: '0',
+      errorMessage: '정상',
+      totalCount: 0
+    }, juso: [],
+  });
+  //주소 검색
+  const jusoSearch = (currentPage) => {
+    if (CommonUtil.isEmpty(guardian['user_address']) == true) {
+      guardianRef.current['user_address'].focus();
+      alert("주소를 입력해 주세요.");
+      return false;
+    }
+    // console.log("check done");
+    const vm = this;
+    let url = 'https://business.juso.go.kr/addrlink/addrLinkApi.do'
+      url += `?currentPage=${CommonUtil.isEmpty(currentPage) ? 1 : currentPage}`
+      url += '&countPerPage=10'
+      url += '&resultType=json'
+      url += `&keyword=${guardian['user_address']}`
+      url += `&confmKey=${JUSO_API_KEY}`;
+    fetch(url, {
+      method: "GET",
+    }).then((response) => response.json()).then((data) => {
+      console.log("주소 검색 결과(건수) : ", data);
+      setJusoList(data.results);
+      if (data.results.common.errorCode != '0') {
+        alert(data.results.common.errorMessage);
+      } else {
+        if (CommonUtil.isEmpty(data.results.juso)) {
+          alert('조회된 주소 정보가 없습니다.');
+        }
+      }
+    }).catch((error) => {
+      console.log('jusoSearch() : ', error);
+    });
+  };
+
+  const grs80 = "+proj=tmerc +lat_0=38 +lon_0=127.5 +k=0.9996 +x_0=1000000 +y_0=2000000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs";
+  const wgs84 = "+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs";
+  //주소 정보 활용 좌표 검색
+  const coordSearch = () => {
+    console.log("check guardian : ", guardian);
+    let url = 'https://business.juso.go.kr/addrlink/addrCoordApi.do'
+      url += `?admCd=${guardian['adm_cd']}`
+      url += `&rnMgtSn=${guardian['rn_mgt_sn']}`
+      url += `&udrtYn=${guardian['udrt_yn']}`
+      url += `&buldMnnm=${guardian['buld_mnnm']}`
+      url += `&buldSlno=${guardian['buld_slno']}`
+      url += '&resultType=json'
+      url += `&confmKey=${JUSO_CORRD_API_KEY}`;
+    fetch(url, {
+      method: "GET",
+    }).then((response) => response.json()).then((data) => {
+      console.log("주소 정보 활용 좌표 검색 결과(건수) : ", data);
+      //setJusoList(data.results);
+      if (data.results.common.errorCode == '0' && CommonUtil.isEmpty(data.results.juso) == false) {
+        let grs80Point = [parseFloat(data.results.juso[0].entX), parseFloat(data.results.juso[0].entY)];
+        let wgs84Point = proj4(grs80, wgs84).forward(grs80Point);
+        guardian['x'] = wgs84Point[0];
+        guardian['y'] = wgs84Point[1];
+        setGuardian({...guardian});
+      }
+    }).catch((error) => {
+      console.log('jusoSearch() : ', error);
+    });
+  };
+
+
+
   //Mounted
   React.useEffect(() => {
     console.log('guardianBySenior : ', guardianBySenior);
@@ -267,17 +349,24 @@
                 <tr>
                   <th><span style={{color : "red"}}>*</span>연락처</th>
                   <td colSpan={3}>
-                    <input type="number" maxLength="11" style={{width: 'calc(100% - 160px)'}}
+                    {CommonUtil.isEmpty(guardian['guardian_id']) ?
+                    <>
+                      <input type="number" maxLength="11" style={{width: 'calc(100% - 160px)'}}
+                        value={guardian['user_phonenumber']}
+                        onChange={(e) => {guardianValueChange('user_phonenumber', e.target.value); setIsIdCheck(false);}}
+                        ref={el => guardianRef.current['user_phonenumber'] = el}
+                      />
+                      <button className={"red-btn btn-large"} onClick={userIdCheck}>
+                        중복확인
+                      </button>
+                    </>
+                    : <input type="number" maxLength="11" disabled
                       value={guardian['user_phonenumber']}
-                      onChange={(e) => {guardianValueChange('user_phonenumber', e.target.value); setIsIdCheck(false);}}
-                      ref={el => guardianRef.current['user_phonenumber'] = el}
-                    />
-                    <button className={"red-btn btn-large"} onClick={userIdCheck}>
-                      중복확인
-                    </button>
+                    />}
                   </td>
                 </tr>
-                <tr>
+
+                {/* <tr>
                   <th><span style={{color : "red"}}>*</span>주소</th>
                   <td colSpan={3}>
                     <input type="text"
@@ -286,7 +375,48 @@
                       ref={el => guardianRef.current['user_address'] = el}
                     />
                   </td>
+                </tr> */}
+                <tr>
+                  <th><span style={{color : "red"}}>*</span>주소</th>
+                  <td colSpan={3}>
+                    <div>
+                      <input type="text" style={{width: 'calc(100% - 160px)'}}
+                        value={guardian['user_address']} disabled={CommonUtil.isEmpty(guardian['zip_no']) == false}
+                        onChange={(e) => {guardianValueChange('user_address', e.target.value)}}
+                        onKeyUp={(e) => {e.key == 'Enter' ? jusoSearch() : null}}
+                        ref={el => guardianRef.current['user_address'] = el}
+                      />
+                      {CommonUtil.isEmpty(guardian['zip_no'])
+                        ? <button className={"red-btn btn-large"} onClick={() => {jusoSearch()}}>주소검색</button>
+                        : <button className={"gray-btn btn-large"} onClick={() => {guardianValueChange('zip_no', null)}}>다시검색</button>
+                      }
+                    </div>
+                    {CommonUtil.isEmpty(jusoList.juso) == false && CommonUtil.isEmpty(guardian['zip_no']) ?
+                    <div>
+                      <ul className="list-box" style={{width: '100%'}}>
+                        {CommonUtil.isEmpty(jusoList.juso) == false ? jusoList.juso.map((item, idx) => {return (
+                        <li className={guardian['zip_no'] == item['zipNo'] ? 'active' : null} onClick={() => {
+                          guardian['zip_no'] = item['zipNo']; guardian['adm_cd'] = item['admCd'];
+                          guardian['rn_mgt_sn'] = item['rnMgtSn']; guardian['bd_mgt_sn'] = item['bdMgtSn'];
+                          guardian['siNsi_nmm'] = item['siNm']; guardian['sgg_nm'] = item['sggNm'];
+                          guardian['emd_nm'] = item['emdNm']; guardian['li_nm'] = item['liNm'];
+                          guardian['rn'] = item['rn']; guardian['emd_no'] = item['emdNo'];
+                          guardian['hemd_nm'] = item['hemdNm']; guardian['road_addr'] = item['roadAddr'];
+                          guardian['buld_mnnm'] = item['buldMnnm']; guardian['buld_slno'] = item['buldSlno'];
+                          guardian['user_address'] = item['roadAddr']; guardian['udrt_yn'] = item['udrtYn'];
+                          setGuardian({...guardian}); coordSearch();
+                        }}>
+                          <span style={{fontWeight: 600}}>[지번]</span> {item['jibunAddr']}
+                          <br/>
+                          <span style={{fontWeight: 600}}>[도로명]</span> {item['roadAddr']}
+                        </li>
+                        )}): null}
+                      </ul>
+                    </div>
+                    : null}
+                  </td>
                 </tr>
+
                 <tr>
                   <th><span style={{color : "red"}}>*</span>대상자와의 관계</th>
                   <td colSpan={3}>
client/views/component/Modal_SeniorInsert.jsx
--- client/views/component/Modal_SeniorInsert.jsx
+++ client/views/component/Modal_SeniorInsert.jsx
@@ -3,7 +3,9 @@
 import SubTitle from "./SubTitle.jsx";
 
 import CommonUtil from "../../resources/js/CommonUtil.js";
-import {JUSO_API_KEY} from "../../../Global.js";
+import {JUSO_API_KEY, JUSO_CORRD_API_KEY} from "../../../Global.js";
+
+import proj4 from "proj4";
 
 export default function Modal({ open, close, seniorInsertCallback, defaultAgentId, defaultAgencyId, defaultGovernmentId }) {
 
@@ -81,6 +83,23 @@
     'authority': 'ROLE_SENIOR',
     'agency_id': !defaultAgencyId ? state.loginUser['agency_id'] : defaultAgencyId,
     'government_id': !defaultGovernmentId ? state.loginUser['government_id'] : defaultGovernmentId,
+
+    'zip_no': null,
+    'adm_cd': null,
+    'rn_mgt_sn': null,
+    'bd_mgt_sn': null,
+    'si_nm': null,
+    'sgg_nm': null,
+    'emd_nm': null,
+    'li_nm': null,
+    'rn': null,
+    'emd_no': null,
+    'hemd_nm': null,
+    'road_addr': null,
+    'buld_mnnm': null,
+    'buld_slno': null,
+    'x': null,
+    'y': null,
 
     'senior_id': null,
     'care_grade': null,
@@ -216,6 +235,12 @@
       return false;
     }
 
+    if (CommonUtil.isEmpty(senior['zip_no']) == true) {
+      seniorRef.current['user_address'].focus();
+      alert("검색을 통해 주소를 선택해 주세요.");
+      return false;
+    }
+
     return true;
   }
   //시니어 등록
@@ -244,25 +269,77 @@
   }
 
 
+  //주소 검색 결과 객체
+  const [jusoList, setJusoList] = React.useState({
+    common: {
+      countPerPage: 10,
+      currentPage: 1,
+      errorCode: '0',
+      errorMessage: '정상',
+      totalCount: 0
+    }, juso: [],
+  });
   //주소 검색
   const jusoSearch = (currentPage) => {
+    if (CommonUtil.isEmpty(senior['user_address']) == true) {
+      seniorRef.current['user_address'].focus();
+      alert("주소를 입력해 주세요.");
+      return false;
+    }
     // console.log("check done");
     const vm = this;
-    let url = `https://business.juso.go.kr/addrlink/addrLinkApi.do
-      ?currentPage=${CommonUtil.isEmpty(currentPage) ? 1 : currentPage}
-      &countPerPage=10
-      &resultType=json
-      &keyword=${senior['user_address']}
-      &confmKey=${JUSO_API_KEY}
-    `;
+    let url = 'https://business.juso.go.kr/addrlink/addrLinkApi.do'
+      url += `?currentPage=${CommonUtil.isEmpty(currentPage) ? 1 : currentPage}`
+      url += '&countPerPage=10'
+      url += '&resultType=json'
+      url += `&keyword=${senior['user_address']}`
+      url += `&confmKey=${JUSO_API_KEY}`;
     fetch(url, {
       method: "GET",
     }).then((response) => response.json()).then((data) => {
-      console.log("시니어 등록 결과(건수) : ", data);
+      console.log("주소 검색 결과(건수) : ", data);
+      setJusoList(data.results);
+      if (data.results.common.errorCode != '0') {
+        alert(data.results.common.errorMessage);
+      } else {
+        if (CommonUtil.isEmpty(data.results.juso)) {
+          alert('조회된 주소 정보가 없습니다.');
+        }
+      }
     }).catch((error) => {
       console.log('jusoSearch() : ', error);
     });
-},
+  };
+
+  const grs80 = "+proj=tmerc +lat_0=38 +lon_0=127.5 +k=0.9996 +x_0=1000000 +y_0=2000000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs";
+  const wgs84 = "+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs";
+  //주소 정보 활용 좌표 검색
+  const coordSearch = () => {
+    console.log("check senior : ", senior);
+    let url = 'https://business.juso.go.kr/addrlink/addrCoordApi.do'
+      url += `?admCd=${senior['adm_cd']}`
+      url += `&rnMgtSn=${senior['rn_mgt_sn']}`
+      url += `&udrtYn=${senior['udrt_yn']}`
+      url += `&buldMnnm=${senior['buld_mnnm']}`
+      url += `&buldSlno=${senior['buld_slno']}`
+      url += '&resultType=json'
+      url += `&confmKey=${JUSO_CORRD_API_KEY}`;
+    fetch(url, {
+      method: "GET",
+    }).then((response) => response.json()).then((data) => {
+      console.log("주소 정보 활용 좌표 검색 결과(건수) : ", data);
+      //setJusoList(data.results);
+      if (data.results.common.errorCode == '0' && CommonUtil.isEmpty(data.results.juso) == false) {
+        let grs80Point = [parseFloat(data.results.juso[0].entX), parseFloat(data.results.juso[0].entY)];
+        let wgs84Point = proj4(grs80, wgs84).forward(grs80Point);
+        senior['x'] = wgs84Point[0];
+        senior['y'] = wgs84Point[1];
+        setSenior({...senior});
+      }
+    }).catch((error) => {
+      console.log('jusoSearch() : ', error);
+    });
+  };
 
 
 
@@ -381,11 +458,41 @@
                 <tr>
                   <th><span style={{color : "red"}}>*</span>주소</th>
                   <td colSpan={3}>
-                    <input type="text"
-                      value={senior['user_address']}
-                      onChange={(e) => {seniorValueChange('user_address', e.target.value)}}
-                      ref={el => seniorRef.current['user_address'] = el}
-                    />
+                    <div>
+                      <input type="text" style={{width: 'calc(100% - 160px)'}}
+                        value={senior['user_address']} disabled={CommonUtil.isEmpty(senior['zip_no']) == false}
+                        onChange={(e) => {seniorValueChange('user_address', e.target.value)}}
+                        onKeyUp={(e) => {e.key == 'Enter' ? jusoSearch() : null}}
+                        ref={el => seniorRef.current['user_address'] = el}
+                      />
+                      {CommonUtil.isEmpty(senior['zip_no'])
+                        ? <button className={"red-btn btn-large"} onClick={() => {jusoSearch()}}>주소검색</button>
+                        : <button className={"gray-btn btn-large"} onClick={() => {seniorValueChange('zip_no', null)}}>다시검색</button>
+                      }
+                    </div>
+                    {CommonUtil.isEmpty(jusoList.juso) == false && CommonUtil.isEmpty(senior['zip_no']) ?
+                    <div>
+                      <ul className="list-box" style={{width: '100%'}}>
+                        {CommonUtil.isEmpty(jusoList.juso) == false ? jusoList.juso.map((item, idx) => {return (
+                        <li className={senior['zip_no'] == item['zipNo'] ? 'active' : null} onClick={() => {
+                          senior['zip_no'] = item['zipNo']; senior['adm_cd'] = item['admCd'];
+                          senior['rn_mgt_sn'] = item['rnMgtSn']; senior['bd_mgt_sn'] = item['bdMgtSn'];
+                          senior['siNsi_nmm'] = item['siNm']; senior['sgg_nm'] = item['sggNm'];
+                          senior['emd_nm'] = item['emdNm']; senior['li_nm'] = item['liNm'];
+                          senior['rn'] = item['rn']; senior['emd_no'] = item['emdNo'];
+                          senior['hemd_nm'] = item['hemdNm']; senior['road_addr'] = item['roadAddr'];
+                          senior['buld_mnnm'] = item['buldMnnm']; senior['buld_slno'] = item['buldSlno'];
+                          senior['user_address'] = item['roadAddr']; senior['udrt_yn'] = item['udrtYn'];
+                          setSenior({...senior}); coordSearch();
+                        }}>
+                          <span style={{fontWeight: 600}}>[지번]</span> {item['jibunAddr']}
+                          <br/>
+                          <span style={{fontWeight: 600}}>[도로명]</span> {item['roadAddr']}
+                        </li>
+                        )}): null}
+                      </ul>
+                    </div>
+                    : null}
                   </td>
                 </tr>
                 <tr>
client/views/index.html
--- client/views/index.html
+++ client/views/index.html
@@ -6,7 +6,8 @@
         <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
         <meta name="description" content="Node React Web">
         <link rel="icon" href="" />
-        <script type="text/javascript" src="//dapi.kakao.com/v2/maps/sdk.js?appkey=	557664ec353e27affe3d6c3c513b7183"></script>
+        <!-- <script type="text/javascript" src="//dapi.kakao.com/v2/maps/sdk.js?appkey=	557664ec353e27affe3d6c3c513b7183"></script> -->
+        
         <title>시니어 케어 시스템</title>
     </head>
 
client/views/pages/healthcare/Healthcare.jsx
--- client/views/pages/healthcare/Healthcare.jsx
+++ client/views/pages/healthcare/Healthcare.jsx
@@ -254,7 +254,7 @@
                 </select>
                 <input type="text" className="senior-search" value={senior.search.searchText}
                     onChange={(e) => {senior.search.searchText = e.target.value; setSenior({...senior});}}
-                    onKeyUp={(e) => searchingEnter(e)}/>
+                    onKeyUp={(e) => searchingEnter(e.key)}/>
                 <button className="btn-small gray-btn" onClick={searching}>검색</button>
             </div>
 
client/views/pages/healthcare/Medicalcare.jsx
--- client/views/pages/healthcare/Medicalcare.jsx
+++ client/views/pages/healthcare/Medicalcare.jsx
@@ -988,7 +988,7 @@
                 </select>
                 <input type="text" className="senior-search" value={senior.search.searchText}
                     onChange={(e) => {senior.search.searchText = e.target.value; setSenior({...senior});}}
-                    onKeyUp={(e) => searchingEnter(e)}/>
+                    onKeyUp={(e) => searchingEnter(e.key)}/>
                 <button className="btn-small gray-btn" onClick={searching}>검색</button>
             </div>
 
client/views/pages/main/Main_government.jsx
--- client/views/pages/main/Main_government.jsx
+++ client/views/pages/main/Main_government.jsx
@@ -1,8 +1,14 @@
 import React,{useState} from "react";
 import { useSelector } from "react-redux";
 
+
+
+import { MapContainer, TileLayer, LayerGroup, Marker, Circle, CircleMarker, Tooltip, Popup, useMap } from 'react-leaflet';
+import L, { CRS, latLng, bounds } from 'leaflet';
+
+
 import Title from "../../component/Title.jsx";
-import Map from "../../component/chart/Map.jsx";
+/* import Map from "../../component/chart/Map.jsx"; */
 import Chart5 from "../../component/chart/Chart5.jsx";
 import Chart2_govern from "../../component/chart/Chart2_govern.jsx";
 import Donut1_govern from "../../component/chart/Donut1_govern.jsx";
@@ -13,151 +19,138 @@
 import MedicationIcon from '@mui/icons-material/Medication';
 import ElderlyIcon from '@mui/icons-material/Elderly';
 
+
+import "leaflet/dist/leaflet.css";
+
 export default function Main_government() {
 
-  //전역 변수 저장 객체
-  const state = useSelector((state) => {return state});
+    //전역 변수 저장 객체
+    const state = useSelector((state) => {return state});
 
-  const [cityName, setCityName] = useState("군위군");
+    const [cityName, setCityName] = useState(state.loginUser['government_name']);
 
-  React.useEffect(() => {
-    setCityName(state.loginUser['government_name']);
-  }, []);
+    //대상자(시니어) 목록 조회
+    const [senior, setSenior] = React.useState({userList: [], userListCount: 0});
+    const seniorSelectList = () => {
+        fetch("/user/userSelectList.json", {
+            method: "POST",
+            headers: {
+            'Content-Type': 'application/json; charset=UTF-8'
+            },
+            body: JSON.stringify({
+                'government_id': state.loginUser['government_id'],
+                'agency_id': state.loginUser['agency_id'],
+                'authority': 'ROLE_SENIOR',
+            }),
+        }).then((response) => response.json()).then((data) => {
+            console.log("대상자(시니어) 목록 조회 : ", data);
+            setSenior(data);
+        }).catch((error) => {
+            console.log('seniorSelectList() /user/userSelectList.json error : ', error);
+        });
+    }
 
-  // const tableHead = ["방문날짜", "방문사유", "방문 상세 사유"];
-  // const Key = ["date", "reason", "detail_reason"];
-  // const content = [
-  //   {
-  //     date: "2022.12.12",
-  //     reason: "어르신케어",
-  //     detail_reason: "하루동안 미복약으로 방문. 방문시 두통을 호소하셔 병원 동행",
-  //   },
-  // ];
+    const iconHouse = new L.Icon({
+        iconUrl: '/client/resources/files/images/house.png',
+        iconRetinaUrl: '/client/resources/files/images/house.png',
+        iconSize: [20, 20],
+        className: 'leaflet-background-radius-icon'//leaflet-div-icon
+    });
 
-  // //노인리스트
-  // const tableHead2 = [
-  //   "이름",
-  //   "요양등급",
-  //   "생년월일",
-  //   "연락처",
-  //   "주소",
-  //   "기저질환",
-  // ];
-  // const Key2 = [
-  //   "name",
-  //   "level_of_care",
-  //   "birth",
-  //   "phone",
-  //   "address",
-  //   "management_number",
-  // ];
-  // const content2 = [
-  //   {
-  //     name: "김복남",
-  //     level_of_care: "1등급",
-  //     birth: "1948.11.15",
-  //     phone: "010-1234-5678",
-  //     address: "경상북도 군위군 삼국유사면",
-  //     management_number: "혈압",
-  //   },
-  //   {
-  //     name: "홍길동",
-  //     level_of_care: "2등급",
-  //     birth: "1948.05.18",
-  //     phone: "010-3333-3333",
-  //     address: "경상북도 군위군 삼국유사면",
-  //     management_number: "당뇨",
-  //   },
-  //   {
-  //     name: "김말순",
-  //     level_of_care: "3등급",
-  //     birth: "1939.03.19",
-  //     phone: "010-3333-4444",
-  //     address: "경상북도 군위군 삼국유사면",
-  //     management_number: "천식",
-  //   },
-  //   {
-  //     name: "신정길",
-  //     level_of_care: "1등급",
-  //     birth: "1945.05.19",
-  //     phone: "010-3333-5555",
-  //     address: "경상북도 군위군 삼국유사면",
-  //     management_number: "폐렴",
-  //   },
-  //   {
-  //     name: "김정남",
-  //     level_of_care: "1등급",
-  //     birth: "1945.05.19",
-  //     phone: "010-3333-6666",
-  //     address: "경상북도 군위군 삼국유사면",
-  //     management_number: "인지장애",
-  //   },
-  // ];
+    React.useEffect(() => {
+        seniorSelectList();
+    }, []);
 
-  return (
-    <main>
-      <div className="main-grid-government">
-        <div className="sub-grid-government">
-          <ul className="content-box statistics-govern" background="#f7acba">
-            <li> 
-              <p><ElderlyIcon sx={{ width: "50px", height: "50px", color: "#ffffff", background:"#bf0629", borderRadius:"50px" }}/></p>           
-              <p>{cityName} 전체 대상자</p>
-              <p>30</p>
-            </li>
+
+    return (
+        <main>
+            <div className="main-grid-government">
+                <div className="sub-grid-government">
+                    <ul className="content-box statistics-govern" background="#f7acba">
+                    <li> 
+                        <p><ElderlyIcon sx={{ width: "50px", height: "50px", color: "#ffffff", background:"#bf0629", borderRadius:"50px" }}/></p>           
+                        <p>{cityName} 전체 대상자</p>
+                        <p>30</p>
+                    </li>
+                            </ul>
+                    <ul className="content-box statistics-govern" background="#8ef3d1">
+                    <li>
+                        <p><MedicationIcon sx={{ width: "50px", height: "50px", color: "#ffffff", background:"#076143", borderRadius:"50px" }}/></p>
+                        <p>{cityName} 미복약 위험 대상자</p>
+                        <p>11</p>
+                    </li>
                     </ul>
-          <ul className="content-box statistics-govern" background="#8ef3d1">
-            <li>
-             <p><MedicationIcon sx={{ width: "50px", height: "50px", color: "#ffffff", background:"#076143", borderRadius:"50px" }}/></p>
-              <p>{cityName} 미복약 위험 대상자</p>
-              <p>11</p>
-            </li>
-          </ul>
-          <ul className="content-box statistics-govern" background="#ebe7b9" >
-            <li>
-               <p><DeviceThermostatIcon sx={{ width: "50px", height: "50px", color: "#ffffff", background:"#f1de05", borderRadius:"50px" }}/></p>
-              <p>{cityName} 댁내 온도 위험 대상자</p>
-              <p>7</p>
-            </li>
-          </ul>
-          <ul className="content-box statistics-govern" background="#5f9af3">
-            <li>
-              <p><BatteryCharging20Icon sx={{ width: "50px", height: "50px", color: "#ffffff", background:"#5f9af3", borderRadius:"50px" }}/></p>
-              <p>{cityName} 배터리 부족 대상자 </p>
-              <p>13</p>
-            </li>
-          </ul>
-        </div>
-        <div className="content-box combine-left-government combine-bottom-government2 main-main">
-          <div className="flex">
-            <Title title={"지역별 케어 대상자 분포 현황"} explanation={"지역 선택 시 해당 지역의 대상자리스트가 보여집니다."} />
-          </div>
-          <Map setCityName={setCityName} />
-        </div>
-        <div className="content-box combine-all-government combine-bottom-government2">
-        <div className="flex">
-            <Title title={`${cityName} 월별 방문 횟수`} explanation={"최근 6개월간 방문 횟수의 변화를 확인할 수 있습니다."} />
-          </div>
-          <RowChart_govern />
-        </div>        
-        <div className="content-box combine-left-government2">
-          <div className="flex">
-            <Title title={`${cityName} 복용률 평균`} explanation={"해당 지역의 대상자 복용률이 그래프로 보여집니다."} />
-          </div>
-          <Chart2_govern />
-        </div>
-        <div className="content-box combine-right-government2">
-        <div className="flex">
-            <Title title={`기관별 대상자 등록 현황`} explanation={"약상자 사용자의 데이터 차트가 보여집니다."} />
-          </div>
-          <Chart5 />         
-        </div>
-        <div className="content-box combine-right-government">
-          <div className="flex">
-            <Title title={`기관별 약상자 사용 현황`} explanation={""} />
-          </div>
-          <Donut1_govern />
-        </div>        
-      </div>
-    </main>
-  );
+                    <ul className="content-box statistics-govern" background="#ebe7b9" >
+                    <li>
+                        <p><DeviceThermostatIcon sx={{ width: "50px", height: "50px", color: "#ffffff", background:"#f1de05", borderRadius:"50px" }}/></p>
+                        <p>{cityName} 댁내 온도 위험 대상자</p>
+                        <p>7</p>
+                    </li>
+                    </ul>
+                    <ul className="content-box statistics-govern" background="#5f9af3">
+                    <li>
+                        <p><BatteryCharging20Icon sx={{ width: "50px", height: "50px", color: "#ffffff", background:"#5f9af3", borderRadius:"50px" }}/></p>
+                        <p>{cityName} 배터리 부족 대상자 </p>
+                        <p>13</p>
+                    </li>
+                    </ul>
+                </div>
+                <div className="content-box combine-left-government combine-bottom-government2 main-main">
+                    <div className="flex">
+                        <Title title={"지역별 케어 대상자 분포 현황"} explanation={"지역 선택 시 해당 지역의 대상자리스트가 보여집니다."} />
+                    </div>
+                    <div style={{height: 'calc(100% - 60px)'}}>
+                    <MapContainer center={latLng(35.8713802646197, 128.601805491072)} zoom={13} scrollWheelZoom={true} style={{height: '100%'}}>
+                        <TileLayer
+                            attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
+                            url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
+                        />
+                        {/* <Marker position={[128.601405491072, 35.8913802646197]}>
+                        <Popup>
+                            A pretty CSS3 popup. <br /> Easily customizable.
+                        </Popup>
+                        </Marker> */}
+
+                        <LayerGroup>
+                        {senior.userList.map((item, idx) => {return (
+                            <Marker position={[item['y'], item['x']]} icon={iconHouse}>
+                                <Popup>
+                                    <div>
+                                        {item['user_name']}({item['user_birth']} - {item['user_gender']})
+                                    </div>
+                                </Popup>
+                            </Marker>
+                        )})}
+                        </LayerGroup>
+                    </MapContainer>
+                    </div>
+                    {/* <Map setCityName={setCityName} /> */}
+                </div>
+                <div className="content-box combine-all-government combine-bottom-government2">
+                    <div className="flex">
+                        <Title title={`${cityName} 월별 방문 횟수`} explanation={"최근 6개월간 방문 횟수의 변화를 확인할 수 있습니다."} />
+                    </div>
+                    <RowChart_govern />
+                </div>        
+                <div className="content-box combine-left-government2">
+                    <div className="flex">
+                        <Title title={`${cityName} 복용률 평균`} explanation={"해당 지역의 대상자 복용률이 그래프로 보여집니다."} />
+                    </div>
+                    <Chart2_govern />
+                </div>
+                <div className="content-box combine-right-government2">
+                    <div className="flex">
+                        <Title title={`기관별 대상자 등록 현황`} explanation={"약상자 사용자의 데이터 차트가 보여집니다."} />
+                    </div>
+                    <Chart5 />         
+                </div>
+                <div className="content-box combine-right-government">
+                    <div className="flex">
+                        <Title title={`기관별 약상자 사용 현황`} explanation={""} />
+                    </div>
+                    <Donut1_govern />
+                </div>        
+            </div>
+        </main>
+    );
 }
client/views/pages/senior_management/SeniorEdit.jsx
--- client/views/pages/senior_management/SeniorEdit.jsx
+++ client/views/pages/senior_management/SeniorEdit.jsx
@@ -4,6 +4,9 @@
 import SubTitle from "../../component/SubTitle.jsx";
 
 import CommonUtil from "../../../resources/js/CommonUtil.js";
+import {JUSO_API_KEY, JUSO_CORRD_API_KEY} from "../../../../Global.js";
+
+import proj4 from "proj4";
 
 export default function SeniorEdit() {
   const navigate = useNavigate();
@@ -190,6 +193,12 @@
       return false;
     }
 
+    if (CommonUtil.isEmpty(senior['zip_no']) == true) {
+      seniorRef.current['user_address'].focus();
+      alert("검색을 통해 주소를 선택해 주세요.");
+      return false;
+    }
+
     return true;
   }
   //시니어 등록
@@ -216,6 +225,79 @@
       console.log('seniorUpdate() /user/seniorUpdate.json error : ', error);
     });
   }
+
+
+  //주소 검색 결과 객체
+  const [jusoList, setJusoList] = React.useState({
+    common: {
+      countPerPage: 10,
+      currentPage: 1,
+      errorCode: '0',
+      errorMessage: '정상',
+      totalCount: 0
+    }, juso: [],
+  });
+  //주소 검색
+  const jusoSearch = (currentPage) => {
+    if (CommonUtil.isEmpty(senior['user_address']) == true) {
+      seniorRef.current['user_address'].focus();
+      alert("주소를 입력해 주세요.");
+      return false;
+    }
+    // console.log("check done");
+    let url = 'https://business.juso.go.kr/addrlink/addrLinkApi.do'
+      url += `?currentPage=${CommonUtil.isEmpty(currentPage) ? 1 : currentPage}`
+      url += '&countPerPage=10'
+      url += '&resultType=json'
+      url += `&keyword=${senior['user_address']}`
+      url += `&confmKey=${JUSO_API_KEY}`;
+    fetch(url, {
+      method: "GET",
+    }).then((response) => response.json()).then((data) => {
+      console.log("주소 검색 결과(건수) : ", data);
+      setJusoList(data.results);
+      if (data.results.common.errorCode != '0') {
+        alert(data.results.common.errorMessage);
+      } else {
+        if (CommonUtil.isEmpty(data.results.juso)) {
+          alert('조회된 주소 정보가 없습니다.');
+        }
+      }
+    }).catch((error) => {
+      console.log('jusoSearch() : ', error);
+    });
+  };
+
+  const grs80 = "+proj=tmerc +lat_0=38 +lon_0=127.5 +k=0.9996 +x_0=1000000 +y_0=2000000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs";
+  const wgs84 = "+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs";
+  //주소 정보 활용 좌표 검색
+  const coordSearch = () => {
+    console.log("check senior : ", senior);
+    const vm = this;
+    let url = 'https://business.juso.go.kr/addrlink/addrCoordApi.do'
+      url += `?admCd=${senior['adm_cd']}`
+      url += `&rnMgtSn=${senior['rn_mgt_sn']}`
+      url += `&udrtYn=${senior['udrt_yn']}`
+      url += `&buldMnnm=${senior['buld_mnnm']}`
+      url += `&buldSlno=${senior['buld_slno']}`
+      url += '&resultType=json'
+      url += `&confmKey=${JUSO_CORRD_API_KEY}`;
+    fetch(url, {
+      method: "GET",
+    }).then((response) => response.json()).then((data) => {
+      console.log("주소 정보 활용 좌표 검색 결과(건수) : ", data);
+      //setJusoList(data.results);
+      if (data.results.common.errorCode == '0' && CommonUtil.isEmpty(data.results.juso) == false) {
+        let grs80Point = [parseFloat(data.results.juso[0].entX), parseFloat(data.results.juso[0].entY)];
+        let wgs84Point = proj4(grs80, wgs84).forward(grs80Point);
+        senior['x'] = wgs84Point[0];
+        senior['y'] = wgs84Point[1];
+        setSenior({...senior});
+      }
+    }).catch((error) => {
+      console.log('jusoSearch() : ', error);
+    });
+  };
 
 
   //Mounted
@@ -303,14 +385,15 @@
             <tr>
               <th><span style={{color : "red"}}>*</span>연락처</th>
               <td colSpan={3}>
-                <input type="number" maxLength="11"
+                <input type="number" maxLength="11" disabled
                   value={senior['user_phonenumber']}
                   onChange={(e) => {seniorValueChange('user_phonenumber', e.target.value)}}
                   ref={el => seniorRef.current['user_phonenumber'] = el}
                 />
               </td>
             </tr>
-            <tr>
+
+            {/* <tr>
               <th><span style={{color : "red"}}>*</span>주소</th>
               <td colSpan={3}>
                 <input type="text"
@@ -319,7 +402,48 @@
                   ref={el => seniorRef.current['user_address'] = el}
                 />
               </td>
+            </tr> */}
+            <tr>
+              <th><span style={{color : "red"}}>*</span>주소</th>
+              <td colSpan={3}>
+                <div>
+                  <input type="text" style={{width: 'calc(100% - 160px)'}}
+                    value={senior['user_address']} disabled={CommonUtil.isEmpty(senior['zip_no']) == false}
+                    onChange={(e) => {seniorValueChange('user_address', e.target.value)}}
+                    onKeyUp={(e) => {e.key == 'Enter' ? jusoSearch() : null}}
+                    ref={el => seniorRef.current['user_address'] = el}
+                  />
+                  {CommonUtil.isEmpty(senior['zip_no'])
+                    ? <button className={"red-btn btn-large"} onClick={() => {jusoSearch()}}>주소검색</button>
+                    : <button className={"gray-btn btn-large"} onClick={() => {seniorValueChange('zip_no', null)}}>다시검색</button>
+                  }
+                </div>
+                {CommonUtil.isEmpty(jusoList.juso) == false && CommonUtil.isEmpty(senior['zip_no']) ?
+                <div>
+                  <ul className="list-box" style={{width: '100%'}}>
+                    {CommonUtil.isEmpty(jusoList.juso) == false ? jusoList.juso.map((item, idx) => {return (
+                    <li className={senior['zip_no'] == item['zipNo'] ? 'active' : null} onClick={() => {
+                      senior['zip_no'] = item['zipNo']; senior['adm_cd'] = item['admCd'];
+                      senior['rn_mgt_sn'] = item['rnMgtSn']; senior['bd_mgt_sn'] = item['bdMgtSn'];
+                      senior['siNsi_nmm'] = item['siNm']; senior['sgg_nm'] = item['sggNm'];
+                      senior['emd_nm'] = item['emdNm']; senior['li_nm'] = item['liNm'];
+                      senior['rn'] = item['rn']; senior['emd_no'] = item['emdNo'];
+                      senior['hemd_nm'] = item['hemdNm']; senior['road_addr'] = item['roadAddr'];
+                      senior['buld_mnnm'] = item['buldMnnm']; senior['buld_slno'] = item['buldSlno'];
+                      senior['user_address'] = item['roadAddr']; senior['udrt_yn'] = item['udrtYn'];
+                      setSenior({...senior}); coordSearch();
+                    }}>
+                      <span style={{fontWeight: 600}}>[지번]</span> {item['jibunAddr']}
+                      <br/>
+                      <span style={{fontWeight: 600}}>[도로명]</span> {item['roadAddr']}
+                    </li>
+                    )}): null}
+                  </ul>
+                </div>
+                : null}
+              </td>
             </tr>
+            
             <tr>
               <th>필요 복약</th>
               <td className="medicationTime-td">
client/views/pages/user_management/AgencySeniorSelect.jsx
--- client/views/pages/user_management/AgencySeniorSelect.jsx
+++ client/views/pages/user_management/AgencySeniorSelect.jsx
@@ -410,7 +410,7 @@
 						</select>
 						<input type="text" className="senior-search" value={search.searchText}
 							onChange={(e) => {search.searchText = e.target.value; setSearch({...search});}}
-							onKeyUp={(e) => searchingEnter(e)}/>
+							onKeyUp={(e) => searchingEnter(e.key)}/>
 						<button className="btn-small gray-btn" onClick={searching}>검색</button>
 					</div>
 
node_modules/.package-lock.json
--- node_modules/.package-lock.json
+++ node_modules/.package-lock.json
@@ -1753,6 +1753,16 @@
       "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
       "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw=="
     },
+    "node_modules/@react-leaflet/core": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/@react-leaflet/core/-/core-2.1.0.tgz",
+      "integrity": "sha512-Qk7Pfu8BSarKGqILj4x7bCSZ1pjuAPZ+qmRwH5S7mDS91VSbVVsJSrW4qA+GPrro8t69gFYVMWb1Zc4yFmPiVg==",
+      "peerDependencies": {
+        "leaflet": "^1.9.0",
+        "react": "^18.0.0",
+        "react-dom": "^18.0.0"
+      }
+    },
     "node_modules/@reduxjs/toolkit": {
       "version": "1.9.3",
       "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.9.3.tgz",
@@ -6011,6 +6021,11 @@
         "graceful-fs": "^4.1.9"
       }
     },
+    "node_modules/leaflet": {
+      "version": "1.9.3",
+      "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.3.tgz",
+      "integrity": "sha512-iB2cR9vAkDOu5l3HAay2obcUHZ7xwUBBjph8+PGtmW/2lYhbLizWtG7nTeYht36WfOslixQF9D/uSIzhZgGMfQ=="
+    },
     "node_modules/levn": {
       "version": "0.3.0",
       "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
@@ -6197,6 +6212,11 @@
       "engines": {
         "node": ">= 0.6"
       }
+    },
+    "node_modules/mgrs": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/mgrs/-/mgrs-1.0.0.tgz",
+      "integrity": "sha512-awNbTOqCxK1DBGjalK3xqWIstBZgN6fxsMSiXLs9/spqWkF2pAhb2rrYCFSsr1/tT7PhcDGjZndG8SWYn0byYA=="
     },
     "node_modules/micromatch": {
       "version": "2.3.11",
@@ -7136,6 +7156,15 @@
       "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
       "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
     },
+    "node_modules/proj4": {
+      "version": "2.9.0",
+      "resolved": "https://registry.npmjs.org/proj4/-/proj4-2.9.0.tgz",
+      "integrity": "sha512-BoDXEzCVnRJVZoOKA0QHTFtYoE8lUxtX1jST38DJ8U+v1ixY70Kpwi0Llu6YqSWEH2xqu4XMEBNGcgeRIEywoA==",
+      "dependencies": {
+        "mgrs": "1.0.0",
+        "wkt-parser": "^1.3.1"
+      }
+    },
     "node_modules/prop-types": {
       "version": "15.8.1",
       "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
@@ -7347,6 +7376,19 @@
       "peerDependencies": {
         "react": ">=16.9.0",
         "react-dom": ">=16.9.0"
+      }
+    },
+    "node_modules/react-leaflet": {
+      "version": "4.2.1",
+      "resolved": "https://registry.npmjs.org/react-leaflet/-/react-leaflet-4.2.1.tgz",
+      "integrity": "sha512-p9chkvhcKrWn/H/1FFeVSqLdReGwn2qmiobOQGO3BifX+/vV/39qhY8dGqbdcPh1e6jxh/QHriLXr7a4eLFK4Q==",
+      "dependencies": {
+        "@react-leaflet/core": "^2.1.0"
+      },
+      "peerDependencies": {
+        "leaflet": "^1.9.0",
+        "react": "^18.0.0",
+        "react-dom": "^18.0.0"
       }
     },
     "node_modules/react-lifecycles-compat": {
@@ -9307,6 +9349,11 @@
       "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz",
       "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw=="
     },
+    "node_modules/wkt-parser": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/wkt-parser/-/wkt-parser-1.3.2.tgz",
+      "integrity": "sha512-A26BOOo7sHAagyxG7iuRhnKMO7Q3mEOiOT4oGUmohtN/Li5wameeU4S6f8vWw6NADTVKljBs8bzA8JPQgSEMVQ=="
+    },
     "node_modules/wmf": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/wmf/-/wmf-1.0.2.tgz",
package-lock.json
--- package-lock.json
+++ package-lock.json
@@ -31,16 +31,19 @@
         "g3": "^0.2.37",
         "http": "^0.0.1-security",
         "https": "^1.0.0",
+        "leaflet": "^1.9.3",
         "moment": "^2.29.4",
         "mysql": "2.18.1",
         "oracledb": "5.5.0",
         "pg": "8.8.0",
-        "react": "18.2.0",
+        "proj4": "^2.9.0",
+        "react": "^18.2.0",
         "react-calendar": "^4.0.0",
         "react-chartjs-2": "^5.2.0",
-        "react-dom": "18.2.0",
+        "react-dom": "^18.2.0",
         "react-is": "18.2.0",
         "react-kakao-maps-sdk": "^1.1.5",
+        "react-leaflet": "^4.2.1",
         "react-redux": "^8.0.5",
         "react-router": "6.3.0",
         "react-router-dom": "6.3.0",
@@ -1806,6 +1809,16 @@
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
       "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw=="
+    },
+    "node_modules/@react-leaflet/core": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/@react-leaflet/core/-/core-2.1.0.tgz",
+      "integrity": "sha512-Qk7Pfu8BSarKGqILj4x7bCSZ1pjuAPZ+qmRwH5S7mDS91VSbVVsJSrW4qA+GPrro8t69gFYVMWb1Zc4yFmPiVg==",
+      "peerDependencies": {
+        "leaflet": "^1.9.0",
+        "react": "^18.0.0",
+        "react-dom": "^18.0.0"
+      }
     },
     "node_modules/@reduxjs/toolkit": {
       "version": "1.9.3",
@@ -6111,6 +6124,11 @@
         "graceful-fs": "^4.1.9"
       }
     },
+    "node_modules/leaflet": {
+      "version": "1.9.3",
+      "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.3.tgz",
+      "integrity": "sha512-iB2cR9vAkDOu5l3HAay2obcUHZ7xwUBBjph8+PGtmW/2lYhbLizWtG7nTeYht36WfOslixQF9D/uSIzhZgGMfQ=="
+    },
     "node_modules/levn": {
       "version": "0.3.0",
       "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
@@ -6297,6 +6315,11 @@
       "engines": {
         "node": ">= 0.6"
       }
+    },
+    "node_modules/mgrs": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/mgrs/-/mgrs-1.0.0.tgz",
+      "integrity": "sha512-awNbTOqCxK1DBGjalK3xqWIstBZgN6fxsMSiXLs9/spqWkF2pAhb2rrYCFSsr1/tT7PhcDGjZndG8SWYn0byYA=="
     },
     "node_modules/micromatch": {
       "version": "2.3.11",
@@ -7242,6 +7265,15 @@
       "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
       "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
     },
+    "node_modules/proj4": {
+      "version": "2.9.0",
+      "resolved": "https://registry.npmjs.org/proj4/-/proj4-2.9.0.tgz",
+      "integrity": "sha512-BoDXEzCVnRJVZoOKA0QHTFtYoE8lUxtX1jST38DJ8U+v1ixY70Kpwi0Llu6YqSWEH2xqu4XMEBNGcgeRIEywoA==",
+      "dependencies": {
+        "mgrs": "1.0.0",
+        "wkt-parser": "^1.3.1"
+      }
+    },
     "node_modules/prop-types": {
       "version": "15.8.1",
       "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
@@ -7453,6 +7485,19 @@
       "peerDependencies": {
         "react": ">=16.9.0",
         "react-dom": ">=16.9.0"
+      }
+    },
+    "node_modules/react-leaflet": {
+      "version": "4.2.1",
+      "resolved": "https://registry.npmjs.org/react-leaflet/-/react-leaflet-4.2.1.tgz",
+      "integrity": "sha512-p9chkvhcKrWn/H/1FFeVSqLdReGwn2qmiobOQGO3BifX+/vV/39qhY8dGqbdcPh1e6jxh/QHriLXr7a4eLFK4Q==",
+      "dependencies": {
+        "@react-leaflet/core": "^2.1.0"
+      },
+      "peerDependencies": {
+        "leaflet": "^1.9.0",
+        "react": "^18.0.0",
+        "react-dom": "^18.0.0"
       }
     },
     "node_modules/react-lifecycles-compat": {
@@ -9413,6 +9458,11 @@
       "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz",
       "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw=="
     },
+    "node_modules/wkt-parser": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/wkt-parser/-/wkt-parser-1.3.2.tgz",
+      "integrity": "sha512-A26BOOo7sHAagyxG7iuRhnKMO7Q3mEOiOT4oGUmohtN/Li5wameeU4S6f8vWw6NADTVKljBs8bzA8JPQgSEMVQ=="
+    },
     "node_modules/wmf": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/wmf/-/wmf-1.0.2.tgz",
package.json
--- package.json
+++ package.json
@@ -26,16 +26,19 @@
     "g3": "^0.2.37",
     "http": "^0.0.1-security",
     "https": "^1.0.0",
+    "leaflet": "^1.9.3",
     "moment": "^2.29.4",
     "mysql": "2.18.1",
     "oracledb": "5.5.0",
     "pg": "8.8.0",
-    "react": "18.2.0",
+    "proj4": "^2.9.0",
+    "react": "^18.2.0",
     "react-calendar": "^4.0.0",
     "react-chartjs-2": "^5.2.0",
-    "react-dom": "18.2.0",
+    "react-dom": "^18.2.0",
     "react-is": "18.2.0",
     "react-kakao-maps-sdk": "^1.1.5",
+    "react-leaflet": "^4.2.1",
     "react-redux": "^8.0.5",
     "react-router": "6.3.0",
     "react-router-dom": "6.3.0",
Add a comment
List