@@ -313,22 +504,123 @@
}
requestAnimationFrame(animation);
}
-
+ // --------- 일일 접속량 차트 관련 변수 --------------------
let trafficChart; // 전역 변수
+ // --------- 일일 접속량 차트 관련 변수 --------------------
+
+ // --------- API 호출 통계 기간 입력 관련 변수 --------------------
+ var today = new Date(); // (기준)오늘 날짜
+ var thisYear = today.getFullYear(); // (기준)올해연도
+ var thisMonth = today.getMonth+1; // (기준)이번달
+ var toDate = new Date(today.getFullYear(), today.getMonth(), 1); // (기준)이번달1일
+
+ var weekAgo = new Date(); // 7일 전 날짜
+ weekAgo.setDate(today.getDate() - 7);
+
+ var fvMonthAgo = new Date();
+ fvMonthAgo.setMonth(toDate.getMonth() -5);
+ // --------- API 호출 통계 기간 입력 관련 변수 --------------------
+
+ // --------- API 호출 통계 CSV 내보내기 관련 변수 --------------------
+ let processedStats = {
+ daily: new Map(),
+ monthly: new Map(),
+ yearly: new Map()
+ };
+ // --------- API 호출 통계 CSV 내보내기 관련 변수 --------------------
+
$(document).ready(function(){
+ // ----------------------------------------------------
+ // 1. 날짜 선택기 초기화
+ // ----------------------------------------------------
+
// datepicker
- $("#chartDate").datepicker({
- format: "yyyy-mm-dd",
+ $("#chartDate, #count_from_dt, #count_to_dt").datepicker({
+ format: "yyyy-mm-dd",
language: "ko",
- autoclose: true
+ autoclose: true,
+ todayHighlight: true,
+ container: $("#chartDate").closest(".datepicker-wrapper")
}).datepicker("setDate", new Date());
+
+ $("#count_from_dt").datepicker("setDate", weekAgo); // 국토지반 API 호출 통계 시작일 셋팅(저번주)
- // API 호출 현황
+ // ----------------------------------------------------
+ // 2. 탭 전환 처리
+ // ----------------------------------------------------
+ $('.tab-link').on('click', function() {
+ const targetId = $(this).data('tab');
+
+ $('.tab-link').removeClass('active');
+ $(this).addClass('active');
+
+ $('.tab-content').removeClass('active');
+ $('#' + targetId).addClass('active');
+
+ $("#count_from_dt, #count_to_dt").datepicker('destroy'); // datepicker 재선언(format 변경)
+ // 일별 탭 클릭 시
+ if (targetId === 'daily') {
+ // 월 단위 datepicker 재초기화
+ $("#count_from_dt, #count_to_dt").datepicker({
+ format: "yyyy-mm-dd",
+ language: "ko",
+ autoclose: true,
+ minViewMode: 0, // 월 단위
+ todayHighlight: true,
+ container: $("#count_from_dt").closest(".datepicker-wrapper")
+ });
+ $("#count_to_dt").datepicker("setDate", today); // 국토지반 API 호출 통계 시작일 셋팅(저번주)
+ $("#count_from_dt").datepicker("setDate", weekAgo); // 국토지반 API 호출 통계 시작일 셋팅(저번주)
+ }
+ // 월별 탭 클릭 시
+ if (targetId === 'monthly') {
+ // 월 단위 datepicker 재초기화
+ $("#count_from_dt, #count_to_dt").datepicker({
+ format: "yyyy-mm",
+ language: "ko",
+ autoclose: true,
+ minViewMode: 1, // 월 단위
+ todayHighlight: true,
+ container: $("#count_from_dt").closest(".datepicker-wrapper")
+ });
+
+ $("#count_from_dt").datepicker("setDate", fvMonthAgo);
+ $("#count_to_dt").datepicker("setDate", thisMonth);
+
+ $("#count_from_dt").val($("#count_from_dt").val().substr(0,7));
+ $("#count_to_dt").val($("#count_to_dt").val().substr(0,7));
+ }
+
+ // 연도별 탭 클릭 시
+ if (targetId === 'yearly') {
+ // 월 단위 datepicker 재초기화
+ $("#count_from_dt, #count_to_dt").datepicker({
+ minViewMode: 'years',
+ format: "yyyy",
+ language: "ko",
+ autoclose: true,
+ todayHighlight: true,
+ container: $("#count_from_dt").closest(".datepicker-wrapper")
+ });
+
+ $("#count_to_dt").datepicker("setDate", new Date(thisYear, 0, 1));
+ $("#count_from_dt").datepicker("setDate", new Date((thisYear -5), 0, 1));
+ $("#count_to_dt").val($("#count_to_dt").val().substr(0,4))
+ $("#count_from_dt").val($("#count_from_dt").val().substr(0,4))
+ }
+ });
+
+ // ----------------------------------------------------
+ // 3. API 호출 현황 초기 로드
+ // ----------------------------------------------------
animateValue("successRate", 0, 100, 1500);
animateValue("failRate", 0, 0, 1500);
animateValue("delayRate", 0, 0, 1500);
animateValue("errorRate", 0, 0, 1500);
+ // API 호출 통계 > 일별, 월별, 연도별 통계
+ getApiStat();
+
// API 호출 통제 목록 조회
getMgmtApiList();
@@ -342,6 +634,19 @@
$(document).on('change', '#chartDate', function() {
getMgmtApiChart();
});
+
+ // API 호출 현황 건수 - 날짜 변경 시 차트 다시 로드
+ $(document).on('change', '#count_from_dt, #count_to_dt', function() {
+ if(!validDate()){
+ return;
+ }
+ getApiStat();
+ });
+
+ // API 호출 통계 건수 - CSV 내보내기 클릭
+ $(document).on('click', '#export-csv-btn', function() {
+ exportToCSV();
+ })
});
// API 호출 통제 목록 조회
@@ -432,6 +737,101 @@
return html;
}
+ // API 호출 통계 > 일별, 월별, 연도별 통계
+ function getApiStat(){
+ var statType = $('.stats-container > .tab-nav').find('.active').data("tab");
+ var fromDt = $('#count_from_dt').val();
+ var toDt = $('#count_to_dt').val();
+ // 날짜데이터가 올바르게 셋팅되지 않았을때는 요청하지 않는다.
+ if (statType === "monthly") {
+ if (fromDt.length < 7 || toDt.length < 7 ) return;
+ } else if (statType === "yearly") {
+ if (fromDt.length < 4 || toDt.length < 4 ) return;
+ } else { // statType === "daily"
+ if (fromDt.length < 10 || toDt.length < 10 ) return;
+ }
+ $.ajax({
+ type : "POST",
+ url : "/admins/mgmtApi/i_counts.do" ,
+ data : {
+ statType : statType,
+ countFromDt: $('#count_from_dt').val(),
+ countToDt: $('#count_to_dt').val()
+ },
+ dataType :"json",
+ success : function(res){ // res.code, res.msg, res.data
+ if (res.code == "SUCCESS") {
+ let countList = res.data;
+ let $listContainer;
+ // 활성화 된 탭의 tbody를 컨테이너로 지정
+ if (statType === "monthly") {
+ $listContainer = $("#monthly-tbody");
+ } else if (statType === "yearly") {
+ $listContainer = $("#yearly-tbody");
+ } else {
+ $listContainer = $("#daily-tbody");
+ }
+
+ // 활성화 된 탭의 csv 양식으로 지정
+ if (statType === "monthly") {
+ $listContainer = $("#monthly-tbody");
+ processedStats.monthly = countList;
+ } else if (statType === "yearly") {
+ $listContainer = $("#yearly-tbody");
+ processedStats.yearly = countList;
+ } else {
+ $listContainer = $("#daily-tbody");
+ processedStats.daily = countList;
+ }
+ // DOM에 추가
+ drawApiCountList(res.data, $listContainer);
+ } else {
+ alert("API 호출 로그 목록 조회중 오류가 발생하였습니다. 다시 시도해주세요.");
+ }
+ },
+ error : function(response){
+ alert("API 호출 로그 목록 조회중 내부 오류가 발생하였습니다. 다시 시도해주세요.");
+ }
+ });
+ };
+
+ function drawApiCountList(countList, $target) {
+ $target.empty();
+ if (!countList || countList.length === 0) {
+ $target.append('
| 표시 할 통계데이타가 없습니다. |
');
+ return;
+ }
+
+ const grouped = {};
+ let totalCount = 0; // 합계 초기화
+
+ countList.forEach(item => {
+ if (!grouped[item.period]) grouped[item.period] = [];
+ grouped[item.period].push(item);
+ totalCount += parseInt(item.accessCount);
+ });
+
+ let html = '';
+ Object.keys(grouped).sort((a, b) => b.localeCompare(a)).forEach(period => {
+ const items = grouped[period];
+ items.forEach((item, idx) => {
+ html += `
+ \${idx === 0 ? `| \${period} | ` : ''}
+ \${item.apiName} |
+ \${item.accessCount} 건 |
+
`;
+ });
+ });
+
+ // 합계 row 추가
+ html += `
+ | 합계 |
+ \${totalCount} 건 |
+
`;
+
+ $target.append(html);
+ }
+
// API 호출 로그 목록 조회
function getMgmtApiLogList(){
$.ajax({
@@ -474,12 +874,10 @@
`;
return html;
}
-
// API 호출 로그 목록 조회
function getMgmtApiChart(){
var chartDate = $('#chartDate').val();
- console.log($('#chartDate').val())
$.ajax({
type : "POST",
url : "/admins/mgmtApi/charts.do" ,
@@ -549,80 +947,78 @@
}
});
}
+
+ function exportToCSV() {
+ // 1. 현재 활성화된 탭 찾기
+ const activeTab = document.querySelector('.tab-link.active');
+ if (!activeTab) return;
+
+ const tabId = activeTab.getAttribute('data-tab'); // 'daily', 'monthly', 'yearly'
+ const dataList = processedStats[tabId]; // 전역 변수에서 배열 데이터 가져오기
+ if (!Array.isArray(dataList) || dataList.length === 0) {
+ alert('내보낼 데이터가 없습니다.');
+ return;
+ }
+
+ let headers = [];
+ let filename = '';
+
+ // 2. 탭에 맞는 헤더와 파일명 설정
+ if (tabId === 'daily') {
+ headers = ['날짜', 'API 명', '호출 건수'];
+ filename = 'api_stats_daily.csv';
+ } else if (tabId === 'monthly') {
+ headers = ['월', 'API 명', '총 호출 건수'];
+ filename = 'api_stats_monthly.csv';
+ } else { // 'yearly'
+ headers = ['연도', 'API 명', '총 호출 건수'];
+ filename = 'api_stats_yearly.csv';
+ }
+
+ // 3. 날짜 기준으로 내림차순 정렬
+ const sortedData = [...dataList].sort((a, b) => b.period.localeCompare(a.period));
+
+ // 4. CSV 헤더 + 데이터 행 구성
+ const rows = [headers];
+ sortedData.forEach(item => {
+ const { period, apiName, accessCount } = item;
+ rows.push([period, apiName, accessCount]);
+ });
+
+ // 5. CSV 문자열로 변환
+ const csvContent = rows.map(row => row.join(',')).join('\r\n');
+
+ // 6. UTF-8 BOM 추가 및 파일 다운로드
+ const bom = '\uFEFF';
+ const blob = new Blob([bom + csvContent], { type: 'text/csv;charset=utf-8;' });
+ const url = URL.createObjectURL(blob);
+
+ const a = document.createElement('a');
+ a.style.display = 'none';
+ a.href = url;
+ a.download = filename;
+
+ document.body.appendChild(a);
+ a.click();
+
+ // 6. 리소스 정리
+ document.body.removeChild(a);
+ URL.revokeObjectURL(url);
+ }
/**
- * 날짜형식 YYYYMMDDHHMI -> YYYY-MM-DD HH:MI
+ * API 호출 통계 날짜 유효검사
*/
- function formatDateTime(raw) {
- if (!raw || raw.length !== 12) return "";
- return (
- raw.slice(0, 4) + "-" +
- raw.slice(4, 6) + "-" +
- raw.slice(6, 8) + " " +
- raw.slice(8, 10) + ":" +
- raw.slice(10, 12)
- );
+ function validDate(){
+ var statType = $('.stats-container > .tab-nav').find('.active').data("tab");
+ var from_dt_val = $('#count_from_dt').val();
+ var to_dt_val = $('#count_to_dt').val();
+ if ("daily" === statType && (from_dt_val.length != 10 || to_dt_val.length != 10)) return false; // YYYY-MM-DD
+ if ("monthly" === statType && (from_dt_val.length != 7 || to_dt_val.length != 7)) return false; // YYYY-MM
+ if ("yearly" === statType && (from_dt_val.length != 4 || to_dt_val.length != 4)) return false; // YYYY
+ return true;
}
- // 예시 데이터 (100개 생성)
-// const apiLogs = Array.from({ length: 100 }, (_, i) => ({
-// api: i % 2 === 0 ? "/API_CHA_085/request" : "/API_BH_020/dc332",
-// method: ["GET", "POST", "PUT"][Math.floor(Math.random() * 3)],
-// params: `subCode=test${i}&prvcCode=x${i}`,
-// status: [ "200 OK", "404 NOT FOUND", "500 INTERNAL ERROR" ][Math.floor(Math.random() * 3)],
-// time: Math.floor(Math.random() * 500),
-// result: ["성공", "지연", "실패"][Math.floor(Math.random() * 3)]
-// }));
-
-// const rowsPerPage = 10;
-// let currentPage = 1;
-
-// function renderTable(page) {
-// const tableBody = document.getElementById("apiLogBody");
-// tableBody.innerHTML = "";
-
-// const start = (page - 1) * rowsPerPage;
-// const end = start + rowsPerPage;
-// const pageData = apiLogs.slice(start, end);
-
-// pageData.forEach(row => {
-// const tr = document.createElement("tr");
-// tr.innerHTML += '
' + row.api + ' | ' + row.method + ' | ' + row.params + ' | ' + row.status + ' | ' + row.time + ' | ' + getResultIcon(row.result) + ' ' +row.result + ' | ';
-// tableBody.appendChild(tr);
-// });
-// }
-
-// function renderPagination() {
-// const totalPages = Math.ceil(apiLogs.length / rowsPerPage);
-// const pagination = document.getElementById("pagination");
-// pagination.innerHTML = "";
-
-// for (let i = 1; i <= totalPages; i++) {
-// const btn = document.createElement("button");
-// btn.textContent = i;
-// if (i === currentPage) btn.classList.add("active");
-// btn.addEventListener("click", () => {
-// currentPage = i;
-// renderTable(currentPage);
-// renderPagination();
-// });
-// pagination.appendChild(btn);
-// }
-// }
-
-// function getResultIcon(result) {
-// if (result === "성공")
-// return `
✔`;
-// if (result === "실패")
-// return `
⚠`;
-// if (result === "지연")
-// return `
⏱`;
-// return "";
-// }
-
- // 초기 렌더링
-// renderTable(currentPage);
-// renderPagination();
diff --git a/src/main/webapp/WEB-INF/views/admins/mgmtApi/left.jsp b/src/main/webapp/WEB-INF/views/admins/mgmtApi/left.jsp
index 3d7525b..7cf32c2 100644
--- a/src/main/webapp/WEB-INF/views/admins/mgmtApi/left.jsp
+++ b/src/main/webapp/WEB-INF/views/admins/mgmtApi/left.jsp
@@ -75,7 +75,14 @@ img { border:0; }
|
+ |
+
+
+ |
+
|
diff --git a/src/main/webapp/WEB-INF/views/admins/mgmtApi/mgmt-api-key.jsp b/src/main/webapp/WEB-INF/views/admins/mgmtApi/mgmt-api-key.jsp
index 55ac374..ed4d0b8 100644
--- a/src/main/webapp/WEB-INF/views/admins/mgmtApi/mgmt-api-key.jsp
+++ b/src/main/webapp/WEB-INF/views/admins/mgmtApi/mgmt-api-key.jsp
@@ -8,7 +8,6 @@
-
@@ -16,8 +15,83 @@
body { font-family: 'Pretendard', 'Roboto', sans-serif; background-color: #f5f6fa; margin: 0; padding: 20px; }
.text-center {text-align: center}
+
+ /* 승인 토글스위치 */
+ /* 스위치들을 한 줄에 나란히 */
+ .switch-group {
+ display: flex;
+ align-items: center;
+ gap: 15px; /* 스위치 간 간격 */
+ }
+ .molit-g-switch,.mois-g-switch, .mof-g-switch {
+ display: flex;
+ align-items: center;
+ gap: 5px;
+ margin-right: 10px;
+
+ }
+ /* 스위치 디자인 */
+ .mui-switch {
+ position: relative;
+ flex-shrink: 0; /* 스위치 크기 줄어들지 않게 */
+ display: inline-block;
+ width: 48px;
+ height: 26px;
+ }
+
+ .mui-switch input {
+ opacity: 0;
+ width: 0;
+ height: 0;
+ }
+
+ .mui-slider {
+ position: absolute;
+ cursor: pointer;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background-color: #ccc;
+ transition: 0.4s;
+ border-radius: 34px;
+ display: flex;
+ align-items: center;
+ justify-content: ${''}; /* 가운데 맞춤 */
+ }
+
+ .mui-slider:before {
+ position: absolute;
+ content: "OFF";
+ color: #fff;
+ font-size: 10px;
+ height: 20px;
+ width: 20px;
+ left: 3px;
+ bottom: 3px;
+ background-color: #999;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ border-radius: 50%;
+ transition: 0.4s;
+ }
+
+ input:checked + .mui-slider {
+ background-color: #4A90E2;
+ }
+
+ input:checked + .mui-slider:before {
+ transform: translateX(22px);
+ content: "ON";
+ background-color: #1A73E8;
+ }
+
/* API 신청 내역 테이블 */
.table-container {
+ display: flex;
+ align-items: center;
+ justify-content: space-between; /* 제목 왼쪽, 스위치 오른쪽 */
margin-top: 25px; background: #fff; border-radius: 12px; padding: 20px;
box-shadow: 0 2px 8px rgba(0,0,0,0.05);
}
@@ -56,6 +130,29 @@
+
@@ -121,13 +218,9 @@
});
}
function drawApiKeyList(item) {
- // 승인 상태에 따른 표시
- let approveHtml = "";
- if (item.approveYn == "Y") {
- approveHtml = "승인됨";
- } else {
- approveHtml = ``;
- }
+
+ // approveYn 이 "Y"이면 체크 상태, 아니면 체크 해제
+ const isChecked = item.approveYn === "Y" ? "checked" : "";
// HTML 문자열 생성
const html = `
@@ -137,20 +230,41 @@
| \${item.apiKey} |
\${item.startDt} |
\${item.endDt} |
- \${approveHtml} |
+
+ |
|
`;
return html;
}
-
+
// 승인처리하기
- function approveKey(e) {
- let apiSeq = $(e).data("id")
+ function toggleApproveKey(ele) {
+ let apiSeq = $(ele).data("seq")
+ let seqArr = [];
+ let otype = !$(ele).data("otype") ? '' : $(ele).data("otype"); // 일괄변경 스위치 클릭한 경우
+ let approve = $(ele).is(":checked") ? "Y" : "N";
+
+ if (otype == '') {
+ seqArr.push($(ele).data("seq"));
+ }
+
+
+// if (btnType == 'globalSwitch') {
+// $('.approve-switch').each(function(i, el) {
+// const idx = $(el).data('seq');
+// seqArr.push(idx);
+// });
+// } else {
+// seqArr.push($(ele).data("seq"));
+// }
$.ajax({
type : "POST",
url : "/admins/mgmtApiKey/approve.do" ,
- data : {apiSeq:apiSeq},
+ data : {seqArr:seqArr, approveYn: approve, otype: otype},
traditional: true,
dataType :"json",
success : function(res){ // res.code, res.msg, res.data
diff --git a/src/main/webapp/WEB-INF/views/admins/mgmtApi/mgmt-api-outdata.jsp b/src/main/webapp/WEB-INF/views/admins/mgmtApi/mgmt-api-outdata.jsp
new file mode 100644
index 0000000..ddf8e2d
--- /dev/null
+++ b/src/main/webapp/WEB-INF/views/admins/mgmtApi/mgmt-api-outdata.jsp
@@ -0,0 +1,1025 @@
+<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
+<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
+<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
+<%@ taglib prefix="ui" uri="http://egovframework.gov/ctl/ui"%>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ~
+
+
+
+
일별 API 호출 현황
+
+
+
+ | 날짜 |
+ API 명 |
+ 호출 건수 |
+
+
+
+ | 표시 할 통계데이타가 없습니다. |
+
+
+
+
+
월별 API 호출 현황
+
+
+
+ | 월 |
+ API 명 |
+ 총 호출 건수 |
+
+
+
+ | 표시 할 통계데이타가 없습니다. |
+
+
+
+
+
연도별 API 호출 현황
+
+
+
+ | 연도 |
+ API 명 |
+ 총 호출 건수 |
+
+
+
+ | 표시 할 통계데이타가 없습니다. |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
표시 할 API 목록이 존재하지 않습니다.
+
API를 등록해주세요.
+
+
+
+
+
+
+
+
+
API 호출 로그
+
+
+
+
+
+
+
+
+
+
+
+
+ | No. |
+ API_KEY |
+ ACCESS_ID |
+ ACCESS_TYPE |
+ ACCESS_TABLE |
+ ACCESS_DT |
+ IP_ADDRESS |
+
+
+
+
+
+
+
+
+
+
+
+
+
+