한글뷰어 기능수정 Ver.01
This commit is contained in:
2480
PM관리자화면개발산출물_20260611/관리자화면_통합대시보드_UI.html
Normal file
2480
PM관리자화면개발산출물_20260611/관리자화면_통합대시보드_UI.html
Normal file
File diff suppressed because it is too large
Load Diff
118
PM관리자화면개발산출물_20260611/관리자화면기획안.html
Normal file
118
PM관리자화면개발산출물_20260611/관리자화면기획안.html
Normal file
@@ -0,0 +1,118 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ko">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>관리자 화면 기능 설계 명세표</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Malgun Gothic', '맑은 고딕', sans-serif;
|
||||
background-color: #f1f5f9;
|
||||
padding: 20px;
|
||||
}
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
background-color: #ffffff;
|
||||
box-shadow: 0 4px 6px rgba(0,0,0,0.05);
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
}
|
||||
th {
|
||||
background-color: #0f172a;
|
||||
color: #ffffff;
|
||||
font-weight: bold;
|
||||
padding: 12px;
|
||||
text-align: left;
|
||||
border: 1px solid #cbd5e1;
|
||||
}
|
||||
td {
|
||||
padding: 12px;
|
||||
border: 1px solid #cbd5e1;
|
||||
color: #334155;
|
||||
}
|
||||
tr:nth-child(even) {
|
||||
background-color: #f8fafc;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h2>🖥️ 관리자 화면(Admin Panel) 필수 기능 설계 명세표</h2>
|
||||
<p>※ 이 파일을 Excel에서 <strong>[파일] -> [열기]</strong>로 실행하거나, 표 전체를 복사하여 Excel 시트에 붙여넣으시면 서식과 셀 구조가 그대로 유지되어 열립니다.</p>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 15%;">대분류</th>
|
||||
<th style="width: 20%;">메뉴명</th>
|
||||
<th style="width: 35%;">상세 기능 명세</th>
|
||||
<th style="width: 30%;">추가 고려사항 (고급 기능)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><strong>프로젝트 관리</strong></td>
|
||||
<td>프로젝트 등록 및 설정</td>
|
||||
<td>신규 현장/공구의 생성 및 수정, 프로젝트별 용량 제한(storage_byte) 설정 및 잠금 토글(is_active) 관리</td>
|
||||
<td>비공개/보안(Secret) 폴더에 대한 일반 사용자의 접근 신청 결재 및 임시 승인 처리</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>프로젝트 관리</strong></td>
|
||||
<td>실시간 배너 공지</td>
|
||||
<td>대시보드 상단 마르퀴 띠배너 공지사항(banner_notice) 등록 및 소켓 기반 실시간 송출 제어</td>
|
||||
<td>배너 공지 예약 노출 설정 및 노출/송출 이력 관리</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>사용자 권한 관리</strong></td>
|
||||
<td>사용자 계정 제어</td>
|
||||
<td>사용자 계정 등록, 정보(부서/직급) 수정, 비밀번호 재설정 및 퇴사자 계정 잠금(is_resigned) 처리</td>
|
||||
<td>외부 SSO(Sentinel) 계정 연동 및 중복 로그인 차단 옵션 관리</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>사용자 권한 관리</strong></td>
|
||||
<td>프로젝트 권한 배정</td>
|
||||
<td>현장별 참여 유저 목록 배정 및 권한 등급(Master/Sub-Master/Worker/Viewer) 마우스 클릭 지정</td>
|
||||
<td>폴더 수준의 세부 접근 제어 리스트(ACL) 추가 연동</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>서버 및 리소스 모니터링</strong></td>
|
||||
<td>용량 분석 현황</td>
|
||||
<td>현장별 누적 스토리지 사용량, 남은 용량 및 파일/폴더 개수 대시보드 시각화</td>
|
||||
<td>스토리지 임계값(예: 90%) 도달 시 관리자 자동 경고 알림</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>서버 및 리소스 모니터링</strong></td>
|
||||
<td>압축 다운로드 관리</td>
|
||||
<td>비동기 폴더 압축 다운로드(BullMQ) 작업의 진행 현황 모니터링 및 대기열 제어</td>
|
||||
<td>임시 보존 기간이 지난 압축 임시파일 자동/일괄 영구 삭제를 통한 디스크 확보</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>시스템 감사 및 통계</strong></td>
|
||||
<td>감사 로그 조회</td>
|
||||
<td>파일 삭제, 이동, 다운로드 등 민감한 조작 행위(tb_log)의 날짜/유저/활동별 필터 검색</td>
|
||||
<td>감사 로그 이력 보고서 인쇄 및 엑셀 백업 다운로드</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>시스템 감사 및 통계</strong></td>
|
||||
<td>행동 분석 및 통계</td>
|
||||
<td>사용자 UI 클릭 로그(tb_click_log) 기반 최다 접근 메뉴 및 최다 조회 도면/문서 사용 현황 분석</td>
|
||||
<td>AI 요약 서비스(Gemini API) 호출 제한량(Quota) 관리 및 비용 통제</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>실시간 접속자</strong></td>
|
||||
<td>실시간 접속 현황</td>
|
||||
<td>현재 소켓 연결된 동시 접속 유저 목록 및 접속 IP와 현재 탐색 중인 아카이브 경로 실시간 표출</td>
|
||||
<td>특정 유저 강제 소켓 연결 끊기(Kick) 기능</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>시스템 환경 및 정책 관리</strong></td>
|
||||
<td>보관 및 삭제 정책 설정</td>
|
||||
<td>기존 pageRenderer.js에 하드코딩된 '최소 유지 파일 개수(3개)' 및 '보존 기간(15일)' 임계치를 DB화하여, 관리자 화면에서 프로젝트별/글로벌 동적 설정 기능 제공</td>
|
||||
<td>tb_project 테이블에 limit_file_count 및 limit_days 컬럼을 연동하여 프론트엔드가 변경된 기준값으로 실시간 바인딩되도록 API 구조 개선</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
343
PM관리자화면개발산출물_20260611/기능명세서.html
Normal file
343
PM관리자화면개발산출물_20260611/기능명세서.html
Normal file
@@ -0,0 +1,343 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ko">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>PROJECT MASTER ver 4.0 기능명세서</title>
|
||||
<!-- Google Fonts - Inter & Noto Sans KR -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600;700&family=Noto+Sans+KR:wght@300;400;500;700&display=swap" rel="stylesheet">
|
||||
|
||||
<style>
|
||||
:root {
|
||||
--primary: #10b981;
|
||||
--primary-dark: #047857;
|
||||
--primary-light: #ecfdf5;
|
||||
--bg: #f8fafc;
|
||||
--card-bg: #ffffff;
|
||||
--text-main: #0f172a;
|
||||
--text-muted: #475569;
|
||||
--border: #e2e8f0;
|
||||
--shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
|
||||
--shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Inter', 'Noto Sans KR', sans-serif;
|
||||
background-color: var(--bg);
|
||||
color: var(--text-main);
|
||||
line-height: 1.7;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1000px;
|
||||
margin: 40px auto;
|
||||
padding: 40px;
|
||||
background-color: var(--card-bg);
|
||||
border-radius: 16px;
|
||||
box-shadow: var(--shadow-lg);
|
||||
border: 1px solid var(--border);
|
||||
}
|
||||
|
||||
/* Header Styling */
|
||||
header {
|
||||
border-bottom: 2px solid var(--border);
|
||||
padding-bottom: 24px;
|
||||
margin-bottom: 40px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.2rem;
|
||||
font-weight: 700;
|
||||
margin: 0 0 8px 0;
|
||||
background: linear-gradient(135deg, var(--primary-dark) 0%, var(--primary) 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 1.1rem;
|
||||
color: var(--text-muted);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Typography & Structure */
|
||||
h2 {
|
||||
font-size: 1.6rem;
|
||||
font-weight: 700;
|
||||
color: var(--primary-dark);
|
||||
margin-top: 40px;
|
||||
margin-bottom: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-left: 5px solid var(--primary);
|
||||
padding-left: 12px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-main);
|
||||
margin-top: 30px;
|
||||
margin-bottom: 12px;
|
||||
border-bottom: 1px dashed var(--border);
|
||||
padding-bottom: 6px;
|
||||
}
|
||||
|
||||
p, li {
|
||||
font-size: 1rem;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
ul {
|
||||
padding-left: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
li {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
/* Premium Table Styles */
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin: 24px 0;
|
||||
font-size: 0.95rem;
|
||||
box-shadow: var(--shadow);
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
border: 1px solid var(--border);
|
||||
}
|
||||
|
||||
th {
|
||||
background-color: var(--primary-dark);
|
||||
color: #ffffff;
|
||||
font-weight: 600;
|
||||
text-align: left;
|
||||
padding: 14px 16px;
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 14px 16px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
background-color: #ffffff;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
tr:last-child td {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
tr:nth-child(even) td {
|
||||
background-color: #f8fafc;
|
||||
}
|
||||
|
||||
tr:hover td {
|
||||
background-color: var(--primary-light);
|
||||
color: var(--primary-dark);
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
/* Mermaid Wrapper */
|
||||
.diagram-container {
|
||||
background-color: #f8fafc;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 12px;
|
||||
padding: 24px;
|
||||
margin: 24px 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
box-shadow: inset 0 2px 4px 0 rgb(0 0 0 / 0.05);
|
||||
}
|
||||
|
||||
.mermaid {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* Highlight Boxes (Alerts) */
|
||||
.alert {
|
||||
background-color: var(--primary-light);
|
||||
border-left: 4px solid var(--primary);
|
||||
border-radius: 4px 8px 8px 4px;
|
||||
padding: 16px 20px;
|
||||
margin: 24px 0;
|
||||
color: var(--primary-dark);
|
||||
}
|
||||
|
||||
.alert-title {
|
||||
font-weight: 700;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
/* Responsive Design */
|
||||
@media (max-width: 768px) {
|
||||
.container {
|
||||
margin: 0;
|
||||
padding: 20px;
|
||||
border-radius: 0;
|
||||
box-shadow: none;
|
||||
border: none;
|
||||
}
|
||||
h1 {
|
||||
font-size: 1.8rem;
|
||||
}
|
||||
table {
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- Mermaid.js CDN for dynamic rendering of charts -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js"></script>
|
||||
<script>
|
||||
mermaid.initialize({
|
||||
startOnLoad: true,
|
||||
theme: 'neutral',
|
||||
securityLevel: 'loose'
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="container">
|
||||
<header>
|
||||
<h1>PROJECT MASTER ver 4.0 기능명세서</h1>
|
||||
<p class="subtitle">시스템 아키텍처, 기능 명세 및 운영 가이드라인</p>
|
||||
</header>
|
||||
|
||||
<h2>1. 시스템 아키텍처 및 연동 구조</h2>
|
||||
<p>본 시스템은 실시간 협업과 대용량 건설 산출물 관리를 위해 웹소켓, 비동기 작업 큐 및 분산 저장소를 포함하는 모던 웹 아키텍처로 구성되어 있습니다.</p>
|
||||
|
||||
<div class="diagram-container">
|
||||
<div class="mermaid">
|
||||
graph TD
|
||||
Client[Browser / Client] <-->|HTTP / Axios| NodeServer[Node.js Express Server]
|
||||
Client <-->|Websocket / Socket.io| NodeServer
|
||||
NodeServer <-->|SQL Queries| Postgres[(PostgreSQL DB)]
|
||||
NodeServer <-->|PubSub / Queue| Redis[(Redis Server)]
|
||||
NodeServer <-->|Presigned URL / PUT| MinIO[(MinIO Object Storage)]
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3>기술 스택 (Tech Stack)</h3>
|
||||
<ul>
|
||||
<li><strong>프론트엔드 (Frontend)</strong>: HTML5, Vanilla JavaScript, Vanilla CSS, Axios, Socket.io-client, OpenLayers (GIS), Cesium (3D 모델)</li>
|
||||
<li><strong>백엔드 (Backend)</strong>: Node.js (Express), Socket.io, BullMQ (비동기 작업 큐), Winston (로깅)</li>
|
||||
<li><strong>데이터베이스 (Database)</strong>: PostgreSQL (ver4 스키마), Redis (소켓 및 작업 큐 세션 관리)</li>
|
||||
<li><strong>오브젝트 스토리지 (Storage)</strong>: MinIO / AWS S3 (Presigned URL 및 원격 정적 파일 보관)</li>
|
||||
</ul>
|
||||
|
||||
<h2>2. 사용자 권한 체계 (User Permission System)</h2>
|
||||
<p>사용자 정보 및 소속 프로젝트별로 세분화된 권한 관리를 지원하며, UI 및 API 호출 수준에서 차단 및 필터링이 적용됩니다.</p>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>권한 레벨</th>
|
||||
<th>그룹/명칭</th>
|
||||
<th>주요 권한 범위 및 설명</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><strong>Super / Dev</strong></td>
|
||||
<td>super / dev</td>
|
||||
<td>시스템 전체 관리 및 개발자 전용 모달 접근 권한. 비활성화된 프로젝트 우회 접근 지원.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Master</strong></td>
|
||||
<td>관리자</td>
|
||||
<td>프로젝트 전반의 산출물 수정/삭제/관리, 개별 폴더/파일별 권한 제어 및 대량 압축 다운로드 권한.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Sub-Master</strong></td>
|
||||
<td>부관리자</td>
|
||||
<td>신규 폴더 생성 및 삭제, 과업개요 정보 수정, 사용자 권한 배정 및 변경 처리.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Security Worker</strong></td>
|
||||
<td>보안참여자</td>
|
||||
<td>보안 설정이 적용된 비밀 폴더 및 파일에 대한 읽기/쓰기 권한 부여 및 관리.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Worker</strong></td>
|
||||
<td>일반참여자</td>
|
||||
<td>일반 산출물 업로드/다운로드, 개인 메모 작성 및 수정, AI 연동 문서 요약(Gemini) 기능 활용.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Viewer</strong></td>
|
||||
<td>참관자</td>
|
||||
<td>스토리지 조회 및 개별 파일 다운로드만 가능 (활동 로그 노출 방지 및 파일 추가/수정 버튼 숨김).</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h2>3. 핵심 모듈 명세 (Core Module Specifications)</h2>
|
||||
|
||||
<h3>3.1 아카이브 모듈 (Archive Module)</h3>
|
||||
<p>프로젝트별 모든 산출물(도면, 공문, 과업 문서 등)을 관리하는 핵심 가상 스토리지 시스템입니다.</p>
|
||||
<ul>
|
||||
<li><strong>폴더 구조 제어</strong>: 폴더 생성(createFolder), 이름 변경(renameTarget), 다른 폴더로의 계층식 위치 이동(relocateTarget)을 실시간 지원합니다.</li>
|
||||
<li><strong>S3 Presigned URL 업로드</strong>: 대용량 업로드 안전성을 위해 서버의 자원을 거치지 않고, 스토리지로부터 Presigned URL을 직접 발급받아 브라우저가 직접 Put Object를 수행합니다.</li>
|
||||
<li><strong>백그라운드 ZIP 압축 다운로드</strong>: 대량의 파일이나 폴더 통째 다운로드 시, BullMQ 큐에 작업을 위임하여 서버 부하를 최소화하고 완료 시 다운로드 링크를 팝업으로 제공합니다.</li>
|
||||
<li><strong>가상 휴지통</strong>: 삭제된 파일은 즉시 지워지지 않고 휴지통으로 이동하며, 소유자 및 관리자의 승인을 통해 복원하거나 영구 삭제할 수 있습니다.</li>
|
||||
<li><strong>뷰어 및 메모 연동</strong>: HWP/DWG 등 다양한 확장자 파일을 PDF로 자동 변환하여 브라우저에서 즉시 열람하는 통합 뷰어를 제공하며, 파일별 텍스트 메모 및 AI 요약 서비스를 연동합니다.</li>
|
||||
</ul>
|
||||
|
||||
<h3>3.2 과업개요 모듈 (Overview Module)</h3>
|
||||
<p>계약 사항 및 상세 마일스톤 정보, 지도 개요도를 하나의 통합 카드뷰 대시보드로 제공합니다.</p>
|
||||
<ul>
|
||||
<li><strong>계약 및 사업 정보</strong>: 계약 금액(외화 금액 포함), Commencement Date, 준공 예정일, 공동 수급사 구성 지분율 및 대표인 정보를 보관합니다.</li>
|
||||
<li><strong>개요도 드래그앤드롭 업로드</strong>: 현장 위치도 및 종합 개요도를 별도 파일 선택창 없이 드래그하여 직관적으로 변경 및 등록할 수 있습니다.</li>
|
||||
<li><strong>과업 일정 캘린더</strong>: 연월 단위의 마일스톤 일정을 생성/관리하고 주요 진척도를 실시간 모니터링합니다.</li>
|
||||
<li><strong>시차 연동 실시간 시계</strong>: 해외 프로젝트의 경우 해당 현장 국가의 표준시 시차 데이터를 바탕으로 실시간 현지 시각을 헤더에 동적으로 출력합니다.</li>
|
||||
</ul>
|
||||
|
||||
<h3>3.3 공문 모듈 (Official Document Module)</h3>
|
||||
<p>프로젝트 관계 기관 간 송수신된 공문 번호, 날짜, 수신처 목록과 문서를 체계적으로 매핑 및 보관합니다.</p>
|
||||
<ul>
|
||||
<li>공문 정보 관리 (발송일, 제목, 수발신처, 문서 번호).</li>
|
||||
<li>공문 매핑 첨부파일 연계 보관 및 1-클릭 다운로드 서비스.</li>
|
||||
</ul>
|
||||
|
||||
<h3>3.4 GSIM (GIS 기반 위치 모델 모듈)</h3>
|
||||
<p>건설 현장의 실제 위경도 좌표 및 고도(Height) 값과 연계하여 모델 및 데이터를 공간 정보와 매핑합니다.</p>
|
||||
<ul>
|
||||
<li>2D(OpenLayers) 지도 및 3D(Cesium) 입체 모델 뷰어 탑재.</li>
|
||||
<li>줌 레벨에 최적화된 마커 클러스터링(Clustering) 기법으로 대량의 포인트를 성능 저하 없이 표시.</li>
|
||||
</ul>
|
||||
|
||||
<h2>4. 실시간 동기화 및 로그 모듈</h2>
|
||||
|
||||
<h3>4.1 Websocket 다중 접속자 커서 (Socket.io)</h3>
|
||||
<p>프로젝트 협업 효율성을 극대화하기 위해 다중 유저 동시 접속 시 각 유저의 Client 마우스 움직임을 추적하여 화면상에 실시간으로 커서 위치와 소속/이름표를 그려줍니다.</p>
|
||||
|
||||
<h3>4.2 실시간 로그 및 클릭 통계</h3>
|
||||
<ul>
|
||||
<li><strong>푸터 실시간 한 줄 로그</strong>: 파일 생성, 삭제, 변환 등 유저가 수행한 핵심 행위가 푸터의 띠 배너 형태로 전 유저에게 실시간 브로드캐스트됩니다.</li>
|
||||
<li><strong>클릭 활동 추적</strong>: 향후 화면 분석 및 건설 관리 통계용으로 유저가 누르는 주요 탭, 버튼 등의 액션을 수집하여 <code>tb_click_log</code> 테이블에 실시간으로 기록합니다.</li>
|
||||
</ul>
|
||||
|
||||
<h2>5. 안정성 및 장애 예방 설계 (Reliability & Robustness)</h2>
|
||||
|
||||
<div class="alert">
|
||||
<div class="alert-title">💡 최신 패치 반영 사항 (System Resilience)</div>
|
||||
<ul>
|
||||
<li><strong>캐시 잠김 해제 미들웨어</strong>: GET 요청 중 정적 자원을 제외한 비동기 동적 API 요청에 대하여 브라우저 304 캐시를 완전히 강제 무효화함으로써, 이전 에러 데이터에 의한 화면 중단 현상을 원천 방지하였습니다.</li>
|
||||
<li><strong>서버 Crash 방어 널가드 (Null-Guard)</strong>: DB 조회 쿼리 등에서 일시적인 커넥션 유실이나 스키마 에러가 나더라도 백엔드 프로세스가 통째로 죽지 않도록, 주요 API 핸들러 전반에 예외 위임(try-catch) 및 Null 체크 조건문을 완비하였습니다.</li>
|
||||
<li><strong>클라이언트 실시간 디버거</strong>: 사용자 브라우저 상에서 유발된 자바스크립트 크래시 및 미처리 거부(Unhandled Rejection) 오류를 백엔드 콘솔로 자동 수집/출력해 주는 에러 추적 스크립트를 내장하였습니다.</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
377
PM관리자화면개발산출물_20260611/메뉴정의서.html
Normal file
377
PM관리자화면개발산출물_20260611/메뉴정의서.html
Normal file
@@ -0,0 +1,377 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ko">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>PM_ver4 통합 관리자 메뉴정의서</title>
|
||||
<!-- Google Fonts - Inter & Noto Sans KR -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Noto+Sans+KR:wght@300;400;500;700&display=swap"
|
||||
rel="stylesheet">
|
||||
|
||||
<style>
|
||||
:root {
|
||||
--primary: #1e5149;
|
||||
/* Forest Green */
|
||||
--primary-dark: #142e29;
|
||||
--primary-light: #e9eeed;
|
||||
--border: #d2dcdb;
|
||||
--bg: #f4f7f6;
|
||||
--card-bg: #ffffff;
|
||||
--text-main: #1f2937;
|
||||
--text-muted: #4b746d;
|
||||
--shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.05), 0 2px 4px -2px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Inter', 'Noto Sans KR', sans-serif;
|
||||
background-color: var(--bg);
|
||||
color: var(--text-main);
|
||||
line-height: 1.6;
|
||||
margin: 0;
|
||||
padding: 40px 20px;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
background-color: var(--card-bg);
|
||||
border-radius: 12px;
|
||||
padding: 40px;
|
||||
box-shadow: var(--shadow);
|
||||
border: 1px solid var(--border);
|
||||
}
|
||||
|
||||
header {
|
||||
border-bottom: 2px solid var(--border);
|
||||
padding-bottom: 20px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2rem;
|
||||
font-weight: 700;
|
||||
color: var(--primary-dark);
|
||||
margin: 0 0 8px 0;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 1.05rem;
|
||||
color: var(--text-muted);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Filter Controls */
|
||||
.filter-controls {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
margin-bottom: 24px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.filter-btn {
|
||||
background-color: #ffffff;
|
||||
border: 1px solid var(--border);
|
||||
color: var(--text-main);
|
||||
padding: 8px 16px;
|
||||
border-radius: 20px;
|
||||
font-size: 0.85rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.filter-btn:hover,
|
||||
.filter-btn.active {
|
||||
background-color: var(--primary);
|
||||
color: #ffffff;
|
||||
border-color: var(--primary);
|
||||
}
|
||||
|
||||
/* Menu Definition Table */
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 0.88rem;
|
||||
margin-top: 10px;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
border: 1px solid var(--border);
|
||||
}
|
||||
|
||||
th {
|
||||
background-color: var(--primary-dark);
|
||||
color: #ffffff;
|
||||
font-weight: 600;
|
||||
text-align: left;
|
||||
padding: 12px 16px;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 14px 16px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
vertical-align: top;
|
||||
color: var(--text-main);
|
||||
}
|
||||
|
||||
tr:last-child td {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
tr:nth-child(even) td {
|
||||
background-color: #f9fafb;
|
||||
}
|
||||
|
||||
tr.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Badges */
|
||||
.badge-cat {
|
||||
display: inline-block;
|
||||
background-color: var(--primary-light);
|
||||
color: var(--primary);
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.badge-table {
|
||||
font-family: monospace;
|
||||
background-color: #f1f5f9;
|
||||
color: #334155;
|
||||
padding: 2px 6px;
|
||||
border-radius: 4px;
|
||||
font-size: 0.8rem;
|
||||
border: 1px solid #e2e8f0;
|
||||
display: inline-block;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.menu-id {
|
||||
font-family: monospace;
|
||||
font-weight: 600;
|
||||
color: #0369a1;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
/* Rules list style */
|
||||
.rules-list {
|
||||
margin: 0;
|
||||
padding-left: 16px;
|
||||
font-size: 0.82rem;
|
||||
color: #4b5563;
|
||||
}
|
||||
|
||||
.rules-list li {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div class="container">
|
||||
<header>
|
||||
<h1>PM_ver4 통합 관리자 메뉴정의서</h1>
|
||||
<p class="subtitle">관리자 화면(Admin Panel) 1단계 및 2단계 메뉴 기능 명세 및 테이블 매핑 테이블</p>
|
||||
</header>
|
||||
|
||||
<!-- Filter Buttons -->
|
||||
<div class="filter-controls">
|
||||
<button class="filter-btn active" onclick="filterMenu('all')">전체보기</button>
|
||||
<button class="filter-btn" onclick="filterMenu('dashboard')">1. Dashboards</button>
|
||||
<button class="filter-btn" onclick="filterMenu('project')">2. 프로젝트 관리</button>
|
||||
<button class="filter-btn" onclick="filterMenu('user')">3. 사용자 관리</button>
|
||||
<button class="filter-btn" onclick="filterMenu('system')">4. 시스템 감사 및 환경</button>
|
||||
</div>
|
||||
|
||||
<!-- Menu Table -->
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 15%;">1단계 메뉴</th>
|
||||
<th style="width: 15%;">2단계 메뉴</th>
|
||||
<th style="width: 15%;">메뉴 ID / Hash</th>
|
||||
<th style="width: 25%;">주요 기능 명세 (Functional Specifications)</th>
|
||||
<th style="width: 15%;">관련 DB 테이블</th>
|
||||
<th style="width: 15%;">비고 및 비즈니스 규칙</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="menu-tbody">
|
||||
<!-- Row 1 -->
|
||||
<tr data-category="dashboard">
|
||||
<td><strong>1. Dashboards</strong></td>
|
||||
<td><span class="badge-cat">종합 용량 및 접속자</span></td>
|
||||
<td class="menu-id">menu-dashboard<br>#dashboard</td>
|
||||
<td>
|
||||
• 전체 디스크 용량, 실시간 소켓 접속자 수, Redis 대기 작업 수 요약 노출.<br>
|
||||
• 현장별 사용 용량, 백분율, 파일 수량 프로그레스 바 표시.<br>
|
||||
• 실시간 소켓 연결 정보 그리드 및 사용자 강제퇴장(Kick) 기능.
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge-table">tb_project</span><br>
|
||||
<span class="badge-table">tb_data</span><br>
|
||||
<span class="badge-table">Redis (Queue)</span>
|
||||
</td>
|
||||
<td>소켓 세션 맵과 실시간 동기화하여 퇴장 처리 즉시 실행.</td>
|
||||
</tr>
|
||||
|
||||
<!-- Row 2 -->
|
||||
<tr data-category="project">
|
||||
<td><strong>2. 프로젝트 관리</strong></td>
|
||||
<td><span class="badge-cat">프로젝트 관리</span></td>
|
||||
<td class="menu-id">menu-project-mgmt<br>#project-mgmt</td>
|
||||
<td>
|
||||
• 프로젝트 목록 조회 및 신규 등록/수정/삭제 모달 팝업.<br>
|
||||
• 프로젝트 구분 카테고리 지정.<br>
|
||||
• 행 클릭 시 우측에 참여 사용자 등급 조회 및 즉각 수정/배정제외.<br>
|
||||
• 미배정 사용자 다중 선택 일괄 추가 배정 팝업.
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge-table">tb_project</span><br>
|
||||
<span class="badge-table">code_detail</span><br>
|
||||
<span class="badge-table">tb_permission</span><br>
|
||||
<span class="badge-table">tb_user</span>
|
||||
</td>
|
||||
<td>
|
||||
<ul class="rules-list">
|
||||
<li>등록 시 ID 직접 입력, 수정 시 ID 비활성.</li>
|
||||
<li><strong>삭제 제한</strong>: 관련 테이블(tb_data, tb_official_doc_file, tb_banner_notice 등)에 현장 ID
|
||||
사용 이력이 있으면 삭제 불가능.</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Row 3 -->
|
||||
<tr data-category="project">
|
||||
<td><strong>2. 프로젝트 관리</strong></td>
|
||||
<td><span class="badge-cat">실시간 배너 공지</span></td>
|
||||
<td class="menu-id">menu-banner-notice<br>#banner-notice</td>
|
||||
<td>
|
||||
• 배너 공지사항 작성 및 등록 (대상 현장, 송출 기간, 편집용 등록일 지정).<br>
|
||||
• 송출 상태(송출중, 예약됨, 만료) 및 등록일 범위 필터 검색 기능.<br>
|
||||
• 진행 중인 공지 개별 즉시 송출중지 처리.
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge-table">tb_banner_notice</span><br>
|
||||
<span class="badge-table">code_detail</span><br>
|
||||
<span class="badge-table">tb_project</span>
|
||||
</td>
|
||||
<td>시작일/종료일 경과 여부에 따라 송출 상태 실시간 계산 렌더링.</td>
|
||||
</tr>
|
||||
|
||||
<!-- Row 4 -->
|
||||
<tr data-category="user">
|
||||
<td><strong>3. 사용자 관리</strong></td>
|
||||
<td><span class="badge-cat">사용자 관리</span></td>
|
||||
<td class="menu-id">menu-user-mgmt<br>#user-mgmt</td>
|
||||
<td>
|
||||
• 전체 사용자 정보 조회 및 신규 계정 등록/정보 수정/삭제 모달.<br>
|
||||
• 권한 그룹 및 재직/퇴직잠금 상태 배지 설정.<br>
|
||||
• 행 클릭 시 해당 유저의 프로젝트 참여 목록(권한 등급 포함) 우측 연동 리스트업.
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge-table">tb_user</span><br>
|
||||
<span class="badge-table">code_detail</span><br>
|
||||
<span class="badge-table">tb_permission</span><br>
|
||||
<span class="badge-table">tb_project</span>
|
||||
</td>
|
||||
<td><strong>삭제 제한</strong>: 권한 테이블(tb_permission)에 프로젝트 참여 권한 정보가 존재하면 계정 삭제 불가능.</td>
|
||||
</tr>
|
||||
|
||||
<!-- Row 5 -->
|
||||
<tr data-category="system">
|
||||
<td><strong>4. 시스템 감사 및 환경</strong></td>
|
||||
<td><span class="badge-cat">감사 로그 조회</span></td>
|
||||
<td class="menu-id">menu-audit-logs<br>#audit-logs</td>
|
||||
<td>
|
||||
• 파일 삭제, 이동, 다운로드 등 보안 감사 대상 활동 조회.<br>
|
||||
• 유저 ID 검색 및 액션 타입(activity) 필터 검색 기능.
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge-table">tb_log</span>
|
||||
</td>
|
||||
<td>감사 추적용 조회 전용 화면.</td>
|
||||
</tr>
|
||||
|
||||
<!-- Row 6 -->
|
||||
<tr data-category="system">
|
||||
<td><strong>4. 시스템 감사 및 환경</strong></td>
|
||||
<td><span class="badge-cat">보관 및 삭제 정책 설정</span></td>
|
||||
<td class="menu-id">menu-delete-policy<br>#delete-policy</td>
|
||||
<td>
|
||||
• 시스템 공통 자동 보존 및 파일 삭제 임계 기준 설정 폼.<br>
|
||||
• 폼 데이터 변경 시 작동 시나리오 문구 동적 요약 안내.<br>
|
||||
• 정기 자동 삭제 스케줄러 배치 구동 이력 로그 리스트업.
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge-table">tb_system_policy</span><br>
|
||||
<span class="badge-table">tb_auto_clean_log</span>
|
||||
</td>
|
||||
<td>
|
||||
<ul class="rules-list">
|
||||
<li>설정값 변경 저장 시 로그 이력의 대상에는 'SYSTEM' 기입.</li>
|
||||
<li>배치는 수동 변경 값을 즉각 바인딩하여 다음 기동 시 적용.</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Row 7 -->
|
||||
<tr data-category="system">
|
||||
<td><strong>4. 시스템 감사 및 환경</strong></td>
|
||||
<td><span class="badge-cat">공통 코드 관리</span></td>
|
||||
<td class="menu-id">menu-code-mgmt<br>#code-mgmt</td>
|
||||
<td>
|
||||
• 대분류 코드 마스터 등록/수정/삭제 모달.<br>
|
||||
• 대분류 선택 시 하단에 소속 소분류 세부 코드 실시간 필터 로드.<br>
|
||||
• 소분류 코드 등록/수정/삭제 모달.<br>
|
||||
• 소분류 base_code (대분류_소분류 결합 코드) 자동 완성 저장.
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge-table">code_master</span><br>
|
||||
<span class="badge-table">code_detail</span>
|
||||
</td>
|
||||
<td>
|
||||
<ul class="rules-list">
|
||||
<li>대분류 미선택 시 하단 세부코드 등록 차단 및 경고.</li>
|
||||
<li><strong>삭제 제한</strong>: 대분류 코드에 속한 하위 소분류 세부 코드(code_detail)가 존재하면 대분류 삭제 불가능.</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Interactive Filtering Script -->
|
||||
<script>
|
||||
function filterMenu(category) {
|
||||
// Update active filter button styling
|
||||
const buttons = document.querySelectorAll('.filter-btn');
|
||||
buttons.forEach(btn => btn.classList.remove('active'));
|
||||
|
||||
// Highlight clicked button
|
||||
const clickedBtn = event.currentTarget || event.target;
|
||||
if (clickedBtn && clickedBtn.classList) {
|
||||
clickedBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// Show/Hide rows
|
||||
const rows = document.querySelectorAll('#menu-tbody tr');
|
||||
rows.forEach(row => {
|
||||
if (category === 'all' || row.getAttribute('data-category') === category) {
|
||||
row.classList.remove('hidden');
|
||||
} else {
|
||||
row.classList.add('hidden');
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
424
PM관리자화면개발산출물_20260611/워크플로우_데이터흐름도.html
Normal file
424
PM관리자화면개발산출물_20260611/워크플로우_데이터흐름도.html
Normal file
@@ -0,0 +1,424 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ko">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>PM 프로젝트 워크플로우 및 데이터 흐름도</title>
|
||||
<!-- Google Fonts - Inter & Noto Sans KR -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600;700&family=Noto+Sans+KR:wght@300;400;500;700&display=swap"
|
||||
rel="stylesheet">
|
||||
|
||||
<!-- Mermaid.js 라이브러리 (CDN) -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js"></script>
|
||||
<script>
|
||||
mermaid.initialize({
|
||||
startOnLoad: true,
|
||||
theme: 'default',
|
||||
securityLevel: 'loose',
|
||||
themeVariables: {
|
||||
fontFamily: 'Inter, Noto Sans KR, sans-serif',
|
||||
primaryColor: '#f43f5e',
|
||||
primaryTextColor: '#fff',
|
||||
lineColor: '#cbd5e1'
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
:root {
|
||||
--primary: #f43f5e;
|
||||
--primary-dark: #be123c;
|
||||
--primary-light: #fff1f2;
|
||||
--bg: #f8fafc;
|
||||
--card-bg: #ffffff;
|
||||
--text-main: #0f172a;
|
||||
--text-muted: #475569;
|
||||
--border: #e2e8f0;
|
||||
--shadow: 0 4px 6px -1px rgb(0 0 0 / 0.05), 0 2px 4px -2px rgb(0 0 0 / 0.05);
|
||||
--shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.05), 0 4px 6px -4px rgb(0 0 0 / 0.05);
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Inter', 'Noto Sans KR', sans-serif;
|
||||
background-color: var(--bg);
|
||||
color: var(--text-main);
|
||||
line-height: 1.7;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 40px auto;
|
||||
padding: 40px;
|
||||
background-color: var(--card-bg);
|
||||
border-radius: 16px;
|
||||
box-shadow: var(--shadow-lg);
|
||||
border: 1px solid var(--border);
|
||||
}
|
||||
|
||||
/* Header Styling */
|
||||
header {
|
||||
border-bottom: 2px solid var(--border);
|
||||
padding-bottom: 24px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.2rem;
|
||||
font-weight: 700;
|
||||
margin: 0 0 8px 0;
|
||||
background: linear-gradient(135deg, var(--primary-dark) 0%, var(--primary) 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 1.1rem;
|
||||
color: var(--text-muted);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.5rem;
|
||||
color: #1e293b;
|
||||
border-left: 5px solid var(--primary);
|
||||
padding-left: 12px;
|
||||
margin-top: 40px;
|
||||
margin-bottom: 20px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.2rem;
|
||||
color: #334155;
|
||||
margin-top: 30px;
|
||||
margin-bottom: 15px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
p,
|
||||
li {
|
||||
color: var(--text-muted);
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
ul {
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
li {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
/* Card / Diagram Area Style */
|
||||
.diagram-card {
|
||||
background: #ffffff;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 12px;
|
||||
padding: 24px;
|
||||
margin: 20px 0 35px 0;
|
||||
box-shadow: var(--shadow);
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.mermaid {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
/* Highlight text */
|
||||
code {
|
||||
font-family: monospace;
|
||||
background-color: #f1f5f9;
|
||||
color: #e11d48;
|
||||
padding: 2px 6px;
|
||||
border-radius: 4px;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.badge {
|
||||
display: inline-block;
|
||||
background-color: var(--primary-light);
|
||||
color: var(--primary-dark);
|
||||
padding: 4px 10px;
|
||||
border-radius: 20px;
|
||||
font-size: 0.85rem;
|
||||
font-weight: 600;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
@media (max-width: 768px) {
|
||||
.container {
|
||||
margin: 0;
|
||||
padding: 20px;
|
||||
border-radius: 0;
|
||||
box-shadow: none;
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div class="container">
|
||||
<header>
|
||||
<h1>PM 프로젝트 워크플로우 및 데이터 흐름도</h1>
|
||||
<p class="subtitle">로컬 개발 인프라 및 핵심 기능(업로드/압축)에 대한 시각화 명세서</p>
|
||||
</header>
|
||||
|
||||
<p>본 문서는 <code>PM_ver4</code> 프로젝트의 시스템 아키텍처, 기능별 워크플로우 및 엔티티 간 데이터 흐름을 시각적으로 구현하여 이해를 돕기 위한 HTML 문서입니다.</p>
|
||||
|
||||
<!-- 1. 전체 아키텍처 -->
|
||||
<h2>1. 전체 시스템 아키텍처 및 데이터 흐름도</h2>
|
||||
<p>사용자 브라우저에서 출발하여 웹 서버(Express), PostgreSQL, Redis 및 로컬 MinIO(S3) 스토리지까지 연결되는 데이터 흐름도입니다.</p>
|
||||
|
||||
<div class="diagram-card">
|
||||
<div class="mermaid">
|
||||
graph LR
|
||||
%% 1. 사용자 영역 (가장 좌측 배치)
|
||||
subgraph Client [사용자 브라우저]
|
||||
UI["HTML CSS JS views"]
|
||||
SocketClient["Socket.io Client"]
|
||||
end
|
||||
|
||||
%% 2. 백엔드 웹서버 영역 (중앙 배치)
|
||||
subgraph WebServer [백엔드 서버 Port 6565]
|
||||
Express["Express App"]
|
||||
Router["Router / Controller"]
|
||||
AuthMid["SSO / Login Bypass"]
|
||||
SocketServer["Socket.io Server"]
|
||||
BullQueue["BullMQ Producer"]
|
||||
S3SDK["aws-sdk s3 - MinIO 연동"]
|
||||
PGDriver["pg - PostgreSQL 연동"]
|
||||
end
|
||||
|
||||
%% 3. 백그라운드 워커 영역 (중앙 하단 배치)
|
||||
subgraph BackgroundWorker [비동기 워커 프로세스]
|
||||
Worker["BullMQ Worker - 압축 수행"]
|
||||
ExternalCLI["programs - pdf_thumb / encryp"]
|
||||
end
|
||||
|
||||
%% 4. 인프라 영역 (가장 우측 배치)
|
||||
subgraph Infrastructure [Docker 가상 환경]
|
||||
PostgresDB["PostgreSQL DB"]
|
||||
RedisDB["Redis Broker"]
|
||||
MinIOStorage["MinIO S3"]
|
||||
end
|
||||
|
||||
%% 좌측 (Client) -> 중앙 (WebServer) 흐름
|
||||
UI -->|HTTP Request| Express
|
||||
Express --> UI
|
||||
SocketClient ---|Socket Event| SocketServer
|
||||
|
||||
%% 웹서버 내부 로직 흐름
|
||||
Express --> AuthMid
|
||||
AuthMid --> Router
|
||||
|
||||
%% 중앙 (WebServer) -> 우측 (Infrastructure) 데이터 흐름
|
||||
Router -->|Query| PGDriver
|
||||
PGDriver --> PostgresDB
|
||||
|
||||
Router -->|Push Job| BullQueue
|
||||
BullQueue --> RedisDB
|
||||
|
||||
Router -->|Generate URL| S3SDK
|
||||
S3SDK --> MinIOStorage
|
||||
|
||||
%% 좌측 (Client) -> 우측 (Infrastructure) 다이렉트 업로드 흐름
|
||||
UI -->|Direct Upload| MinIOStorage
|
||||
|
||||
%% 워커 동작 흐름 (중앙 하단 -> 우측 및 외부)
|
||||
Worker -->|Pop Job| RedisDB
|
||||
Worker -->|Execute CLI| ExternalCLI
|
||||
Worker -->|Read Write Files| MinIOStorage
|
||||
Worker -->|Update Status| PGDriver
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 2. 주요 기능별 핵심 워크플로우 -->
|
||||
<h2>2. 주요 기능별 핵심 워크플로우</h2>
|
||||
|
||||
<h3>2.1 파일 업로드 워크플로우 <span class="badge">Presigned URL 방식</span></h3>
|
||||
<p>서버 리소스를 보존하기 위해 브라우저에서 스토리지(MinIO)로 파일을 직접 올릴 수 있게 구현된 프로세스입니다.</p>
|
||||
|
||||
<div class="diagram-card">
|
||||
<div class="mermaid">
|
||||
sequenceDiagram
|
||||
autonumber
|
||||
actor User as 사용자 (브라우저)
|
||||
participant Server as 백엔드 서버 (Node.js)
|
||||
participant MinIO as 로컬 MinIO 스토리지
|
||||
participant DB as 데이터베이스 (PostgreSQL)
|
||||
|
||||
User->>Server: 1. 업로드 링크 요청 (POST /:projectId/archive/generateUploadUrl)
|
||||
Note over Server: Bucket명 변환 미들웨어 작동<br>(PM_TEST_01 -> pm-test-01)
|
||||
Server->>MinIO: 2. S3 SDK를 통한 Presigned PUT URL 요청
|
||||
MinIO-->>Server: 3. 제한 시간 설정된 Presigned URL 반환
|
||||
Server-->>User: 4. Presigned URL 전달
|
||||
|
||||
User->>MinIO: 5. 해당 Presigned URL로 직접 파일 업로드 (HTTP PUT)
|
||||
Note over User,MinIO: 브라우저에서 스토리지로 다이렉트 전송
|
||||
MinIO-->>User: 6. 업로드 완료 응답 (HTTP 200 OK)
|
||||
|
||||
User->>Server: 7. 업로드 정보 DB 반영 요청
|
||||
Server->>DB: 8. tb_data / _test_tb_data 테이블에 파일 정보 INSERT
|
||||
DB-->>Server: 9. 삽입 성공
|
||||
Server-->>User: 10. 업로드 최종 완료 처리
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3>2.2 폴더 비동기 압축 및 다운로드 워크플로우 <span class="badge">BullMQ + Redis 방식</span></h3>
|
||||
<p>오래 걸리는 압축 다운로드 작업을 백그라운드에서 비동기로 수행하고, 소켓으로 클라이언트에 완료 알림을 제공하는 워크플로우입니다.</p>
|
||||
|
||||
<div class="diagram-card">
|
||||
<div class="mermaid">
|
||||
sequenceDiagram
|
||||
autonumber
|
||||
actor User as 사용자 (브라우저)
|
||||
participant Server as 백엔드 서버 (Node.js)
|
||||
participant Redis as Redis (6379)
|
||||
participant Worker as 백그라운드 Worker
|
||||
participant MinIO as 로컬 MinIO 스토리지
|
||||
participant DB as 데이터베이스 (PostgreSQL)
|
||||
|
||||
User->>Server: 1. 폴더 압축 다운로드 요청
|
||||
Server->>DB: 2. tb_download_folder에 'PENDING' 상태로 이력 기록
|
||||
Server->>Redis: 3. BullMQ를 통해 압축 Job 발행 (Push)
|
||||
Server-->>User: 4. 압축 작업 시작 안내 응답 (즉시 반환, 화면 안멈춤)
|
||||
|
||||
loop Worker 감시
|
||||
Worker->>Redis: 5. 대기 중인 Job 가져오기 (Pop)
|
||||
end
|
||||
|
||||
Note over Worker: 6. 폴더 내 모든 파일 탐색 및<br>zip 압축 파일 로컬 생성
|
||||
Worker->>MinIO: 7. 생성된 zip 파일을 S3 스토리지에 업로드
|
||||
Worker->>DB: 8. tb_download_folder 상태를 'COMPLETED'로 갱신,<br>zip_key 및 만료일(expire_date) 기록
|
||||
|
||||
Note over Server,User: 소켓(Socket.io) 또는 폴링 감시
|
||||
Server->>User: 9. 압축 완료 알림 전송 (Socket.io)
|
||||
User->>Server: 10. 내 다운로드 리스트 요청 (GET /getMyDownloadList)
|
||||
Server->>DB: 11. 완료된 zip 다운로드 정보 조회
|
||||
DB-->>Server: 12. zip 키값 반환
|
||||
Server-->>User: 13. MinIO zip 다운로드 다운로드 링크 반환
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 3. ERD 맵핑 관계 -->
|
||||
<h2>3. 주요 데이터 스키마 간 맵핑 관계 (Core ERD)</h2>
|
||||
<p>시스템 비즈니스 로직의 핵심이 되는 6개 주요 테이블 간의 참조 관계와 주요 필드 중심의 ERD 다이어그램입니다.</p>
|
||||
|
||||
<div class="diagram-card">
|
||||
<div class="mermaid">
|
||||
erDiagram
|
||||
tb_user {
|
||||
varchar user_id PK
|
||||
varchar user_nm
|
||||
varchar company
|
||||
varchar dept
|
||||
varchar group
|
||||
}
|
||||
|
||||
tb_project {
|
||||
varchar project_id PK
|
||||
varchar user_id FK
|
||||
varchar project_nm
|
||||
varchar short_nm
|
||||
boolean overview
|
||||
boolean official_doc
|
||||
boolean gsim
|
||||
}
|
||||
|
||||
tb_data {
|
||||
integer data_id PK
|
||||
varchar project_id FK
|
||||
varchar user_id FK
|
||||
boolean is_folder
|
||||
bigint data_size
|
||||
text object_key
|
||||
bigint popup_size
|
||||
bigint preview_size
|
||||
text ai_summary
|
||||
timestamp create_date
|
||||
}
|
||||
|
||||
tb_download_folder {
|
||||
integer download_id PK
|
||||
varchar project_id FK
|
||||
varchar user_id FK
|
||||
varchar status
|
||||
text zip_key
|
||||
timestamp expire_date
|
||||
boolean made
|
||||
}
|
||||
|
||||
tb_official_doc_file {
|
||||
integer doc_id PK
|
||||
varchar project_id FK
|
||||
varchar uploader FK
|
||||
varchar doc_number
|
||||
varchar doc_date
|
||||
text doc_title
|
||||
text doc_title_summary
|
||||
text doc_content_summary
|
||||
bigint popup_size
|
||||
bigint preview_size
|
||||
}
|
||||
|
||||
tb_click_log {
|
||||
integer click_log_id PK
|
||||
varchar project_id FK
|
||||
varchar user_id FK
|
||||
varchar activity
|
||||
varchar user_ip
|
||||
text_array path_arr
|
||||
int_array data_id_arr
|
||||
}
|
||||
|
||||
%% Relationships
|
||||
tb_user ||--o{ tb_project : "manages"
|
||||
tb_project ||--o{ tb_data : "contains"
|
||||
tb_user ||--o{ tb_data : "creates"
|
||||
tb_project ||--o{ tb_download_folder : "contains"
|
||||
tb_user ||--o{ tb_download_folder : "requests"
|
||||
tb_project ||--o{ tb_official_doc_file : "contains"
|
||||
tb_user ||--o{ tb_official_doc_file : "uploads"
|
||||
tb_project ||--o{ tb_click_log : "records"
|
||||
tb_user ||--o{ tb_click_log : "performs"
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 4. 사용자 관리 업무 흐름 -->
|
||||
<h2>4. 사용자 관리 업무 흐름</h2>
|
||||
<p>신규 사용자 계정을 생성한 뒤, 프로젝트 현장을 등록하고, 해당 현장에 사용자를 배정하여 시스템 기능을 가동하는 일련의 흐름입니다.</p>
|
||||
|
||||
<div class="diagram-card">
|
||||
<div class="mermaid">
|
||||
graph TD
|
||||
A["👤 1. 사용자 등록 (tb_user)<br>사용자 아이디, 패스워드, 이름, 회사/소속부서 등록"] --> B["🏗️ 2. 프로젝트 등록 (tb_project)<br>현장 고유
|
||||
ID, 공식 명칭, 카테고리 구분, 용량제한 설정"]
|
||||
B --> C["🔗 3. 프로젝트 사용자 등록 (tb_permission)<br>등록된 사용자를 특정 현장에 권한 등급(lev)과 함께 배정 및 연결"]
|
||||
C --> D["💻 4. 시스템 사용 (tb_data / tb_official_doc_file)<br>배정받은 권한 레벨(Owner, Sub-Master, Worker, Viewer)에
|
||||
맞춰 시스템 기능 활용"]
|
||||
|
||||
%% Styling
|
||||
style A fill:#fff1f2,stroke:#f43f5e,stroke-width:2px;
|
||||
style B fill:#fff1f2,stroke:#f43f5e,stroke-width:2px;
|
||||
style C fill:#fff1f2,stroke:#f43f5e,stroke-width:2px;
|
||||
style D fill:#be123c,stroke:#be123c,stroke-width:2px,color:#fff;
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
2247
PM관리자화면개발산출물_20260611/테이블명세서.html
Normal file
2247
PM관리자화면개발산출물_20260611/테이블명세서.html
Normal file
File diff suppressed because it is too large
Load Diff
793
PM관리자화면개발산출물_20260611/통합관리자대시보드_구현가이드.html
Normal file
793
PM관리자화면개발산출물_20260611/통합관리자대시보드_구현가이드.html
Normal file
@@ -0,0 +1,793 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ko">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>통합 관리자 대시보드 구현 계획 및 테이블 명세 가이드</title>
|
||||
<!-- Google Fonts - Inter & Noto Sans KR -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Noto+Sans+KR:wght@300;400;500;700&display=swap" rel="stylesheet">
|
||||
|
||||
<!-- Mermaid JS for Dynamic Diagrams -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js"></script>
|
||||
<script>
|
||||
mermaid.initialize({
|
||||
startOnLoad: true,
|
||||
theme: 'forest',
|
||||
themeVariables: {
|
||||
primaryColor: '#1e5149',
|
||||
primaryTextColor: '#fff',
|
||||
lineColor: '#1e5149',
|
||||
secondaryColor: '#142e29'
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
:root {
|
||||
--primary: #1e5149;
|
||||
--primary-dark: #142e29;
|
||||
--primary-light: #e9eeed;
|
||||
--primary-border: #d2dcdb;
|
||||
--accent: #4db251;
|
||||
--bg: #f4f7f6;
|
||||
--card-bg: #ffffff;
|
||||
--text-main: #1f2937;
|
||||
--text-muted: #4b5563;
|
||||
--code-bg: #1e293b;
|
||||
--code-text: #f8fafc;
|
||||
--shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.05), 0 2px 4px -2px rgba(0, 0, 0, 0.05);
|
||||
--sidebar-width: 280px;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Inter', 'Noto Sans KR', sans-serif;
|
||||
background-color: var(--bg);
|
||||
color: var(--text-main);
|
||||
line-height: 1.7;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
/* Sidebar Navigation */
|
||||
aside {
|
||||
width: var(--sidebar-width);
|
||||
background: linear-gradient(180deg, var(--primary-dark) 0%, #0c1a18 100%);
|
||||
color: #ffffff;
|
||||
height: 100vh;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
overflow-y: auto;
|
||||
border-right: 1px solid rgba(255, 255, 255, 0.1);
|
||||
padding: 24px;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
aside h2 {
|
||||
font-size: 1.15rem;
|
||||
font-weight: 700;
|
||||
margin-bottom: 24px;
|
||||
color: var(--accent);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.15);
|
||||
padding-bottom: 12px;
|
||||
}
|
||||
|
||||
aside ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
aside li {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
aside a {
|
||||
color: #b3c5c2;
|
||||
text-decoration: none;
|
||||
font-size: 0.9rem;
|
||||
display: block;
|
||||
padding: 8px 12px;
|
||||
border-radius: 6px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
aside a:hover, aside li.active a {
|
||||
color: #ffffff;
|
||||
background-color: rgba(255, 255, 255, 0.08);
|
||||
padding-left: 16px;
|
||||
}
|
||||
|
||||
aside .category-title {
|
||||
font-size: 0.75rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
color: #628781;
|
||||
margin: 16px 0 8px 12px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
/* Main Content */
|
||||
main {
|
||||
margin-left: var(--sidebar-width);
|
||||
flex-grow: 1;
|
||||
padding: 40px 50px;
|
||||
max-width: 1000px;
|
||||
}
|
||||
|
||||
header {
|
||||
border-bottom: 2px solid var(--primary-border);
|
||||
padding-bottom: 24px;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
header h1 {
|
||||
font-size: 2.2rem;
|
||||
font-weight: 700;
|
||||
margin: 0 0 10px 0;
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
header .subtitle {
|
||||
font-size: 1.1rem;
|
||||
color: var(--text-muted);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Content Sections */
|
||||
section {
|
||||
background-color: var(--card-bg);
|
||||
border-radius: 12px;
|
||||
padding: 30px;
|
||||
margin-bottom: 30px;
|
||||
box-shadow: var(--shadow);
|
||||
border: 1px solid var(--primary-border);
|
||||
}
|
||||
|
||||
h2.section-title {
|
||||
font-size: 1.4rem;
|
||||
font-weight: 700;
|
||||
color: var(--primary);
|
||||
margin-top: 0;
|
||||
margin-bottom: 20px;
|
||||
border-left: 4px solid var(--accent);
|
||||
padding-left: 12px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.15rem;
|
||||
font-weight: 600;
|
||||
color: var(--primary-dark);
|
||||
margin-top: 24px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0 0 16px 0;
|
||||
color: var(--text-main);
|
||||
}
|
||||
|
||||
/* Callouts (GitHub alerts) */
|
||||
.callout {
|
||||
border-left: 4px solid;
|
||||
padding: 16px;
|
||||
margin-bottom: 20px;
|
||||
border-radius: 0 8px 8px 0;
|
||||
}
|
||||
|
||||
.callout-note {
|
||||
background-color: #f0f7ff;
|
||||
border-color: #0066cc;
|
||||
color: #004080;
|
||||
}
|
||||
|
||||
.callout-important {
|
||||
background-color: #fff8f8;
|
||||
border-color: #ef4444;
|
||||
color: #991b1b;
|
||||
}
|
||||
|
||||
.callout-title {
|
||||
font-weight: 700;
|
||||
margin-bottom: 6px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
/* Tables */
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin: 20px 0;
|
||||
font-size: 0.9rem;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
border: 1px solid var(--primary-border);
|
||||
}
|
||||
|
||||
th {
|
||||
background-color: var(--primary);
|
||||
color: #ffffff;
|
||||
font-weight: 600;
|
||||
text-align: left;
|
||||
padding: 12px 16px;
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 12px 16px;
|
||||
border-bottom: 1px solid var(--primary-border);
|
||||
color: var(--text-main);
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
tr:last-child td {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
tr:nth-child(even) td {
|
||||
background-color: #f9fafb;
|
||||
}
|
||||
|
||||
/* Code Blocks */
|
||||
pre {
|
||||
background-color: var(--code-bg);
|
||||
color: var(--code-text);
|
||||
padding: 18px;
|
||||
border-radius: 8px;
|
||||
font-family: 'Consolas', 'Courier New', Courier, monospace;
|
||||
font-size: 0.85rem;
|
||||
overflow-x: auto;
|
||||
margin: 16px 0;
|
||||
box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: 'Consolas', 'Courier New', Courier, monospace;
|
||||
background-color: #f1f5f9;
|
||||
color: #0f172a;
|
||||
padding: 2px 6px;
|
||||
border-radius: 4px;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
pre code {
|
||||
background-color: transparent;
|
||||
color: inherit;
|
||||
padding: 0;
|
||||
border-radius: 0;
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
.method-badge {
|
||||
display: inline-block;
|
||||
padding: 3px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 700;
|
||||
color: #ffffff;
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
.method-get { background-color: #0ea5e9; }
|
||||
.method-post { background-color: #22c55e; }
|
||||
.method-put { background-color: #f59e0b; }
|
||||
.method-delete { background-color: #ef4444; }
|
||||
|
||||
.api-url {
|
||||
font-weight: 600;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
/* List Styling */
|
||||
ul {
|
||||
padding-left: 20px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
li {
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
/* Diagram Container */
|
||||
.diagram-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin: 24px 0;
|
||||
background-color: #ffffff;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--primary-border);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!-- Sidebar Navigation -->
|
||||
<aside>
|
||||
<h2>📁 PM_ver4 Admin Guide</h2>
|
||||
<ul>
|
||||
<li><a href="#overview">가이드 소개</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="category-title">01. 시스템 아키텍처</div>
|
||||
<ul>
|
||||
<li><a href="#architecture">전체 구조 & 로직</a></li>
|
||||
<li><a href="#migration">단계별 구현 계획</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="category-title">02. 데이터 모델 (ERD)</div>
|
||||
<ul>
|
||||
<li><a href="#schema-design">스키마 구조 & 관계</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="category-title">03. 화면별 데이터 매핑</div>
|
||||
<ul>
|
||||
<li><a href="#screen-dashboard">1. 종합 용량 및 접속자</a></li>
|
||||
<li><a href="#screen-project">2. 프로젝트 관리</a></li>
|
||||
<li><a href="#screen-banner">3. 실시간 배너 공지</a></li>
|
||||
<li><a href="#screen-user">4. 사용자 관리</a></li>
|
||||
<li><a href="#screen-audit">5. 감사 로그 조회</a></li>
|
||||
<li><a href="#screen-policy">6. 자동 삭제 정책 설정</a></li>
|
||||
<li><a href="#screen-codes">7. 공통 코드 관리</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="category-title">04. 백엔드 API 설계</div>
|
||||
<ul>
|
||||
<li><a href="#api-endpoints">핵심 CRUD API 목록</a></li>
|
||||
</ul>
|
||||
</aside>
|
||||
|
||||
<!-- Main Content -->
|
||||
<main>
|
||||
<header id="overview">
|
||||
<h1>통합 관리자 대시보드 구현 계획 및 테이블 명세 가이드</h1>
|
||||
<p class="subtitle">관리자 화면(Admin Panel) 마이그레이션 및 데이터베이스 연동 설계서</p>
|
||||
</header>
|
||||
|
||||
<!-- Section 1 -->
|
||||
<section id="architecture">
|
||||
<h2 class="section-title">1. 전체 시스템 아키텍처 및 구현 계획</h2>
|
||||
<p>본 설계서는 <code>관리자화면_통합대시보드_UI제안.html</code> 정적 파일의 사용자 동작 시뮬레이션을 프로덕션 급 서버 인프라에 안착하기 위한 설계 명세입니다. 시스템 환경은 Node.js / PostgreSQL / MinIO S3 및 Redis Queue 아키텍처를 준수합니다.</p>
|
||||
|
||||
<div class="diagram-container">
|
||||
<div class="mermaid">
|
||||
graph TD
|
||||
Client[Browser Admin Page] -->|REST API Requests| Backend[Express.js Web Server]
|
||||
Client -->|WebSocket Event Channel| SocketServer[Socket.io Server]
|
||||
Backend -->|SQL Query / PG client| DB[(PostgreSQL Database)]
|
||||
Backend -->|Enqueue / Monitor| Redis[(Redis / BullMQ Queue)]
|
||||
SocketServer -->|Active Session Cache| Memory[Node.js Process Memory]
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="callout callout-note">
|
||||
<div class="callout-title">ℹ️ 백엔드 연동 방식</div>
|
||||
<div class="callout-content">
|
||||
본 시스템은 관리 업무의 연속성 확보 및 동시성 처리를 위해 REST API 채널 외에도 Socket.io 서버 채널을 동시에 가용하여 실시간 웹소켓 통신을 처리하도록 구성됩니다.
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Section 2 -->
|
||||
<section id="migration">
|
||||
<h2 class="section-title">구현 단계별 개발 로드맵</h2>
|
||||
|
||||
<h3>[1단계] 데이터베이스 스키마 확장 및 마이그레이션</h3>
|
||||
<ul>
|
||||
<li>공통 코드 분류 및 세부 값 제어를 위한 <code>code_master</code> 및 <code>code_detail</code> 테이블 신설.</li>
|
||||
<li><strong>기존 테이블 활용 (컬럼 추가 없음)</strong>: 기존에 존재하는 프로젝트 테이블(<code>tb_project</code>)의 <code>category</code> 컬럼과 사용자 테이블(<code>tb_user</code>)의 <code>"group"</code> 컬럼을 <code>code_detail.base_code</code> 외래키 참조 구조로 그대로 연동하여 불필요한 테이블 팽창 및 컬럼 추가를 차단합니다.</li>
|
||||
<li>시스템 공통 자동 보존 및 삭제 임계치를 독립적으로 제어하기 위한 <strong><code>tb_system_policy</code> (시스템 공통 정책 테이블)</strong> 신설.</li>
|
||||
<li>실시간 배너 공지 이력 보관을 위한 <code>tb_banner_notice</code> 및 정기 자동삭제 실행 결과를 기록하기 위한 <code>tb_auto_clean_log</code> 신설.</li>
|
||||
</ul>
|
||||
|
||||
<h3>[2단계] 백엔드 RESTful API 및 WebSocket 핸들러 개발</h3>
|
||||
<ul>
|
||||
<li><strong>REST API</strong>: 각 기능 및 공통 코드 데이터의 CRUD API Endpoint 구축 (Express Controller 연동).</li>
|
||||
<li><strong>WebSocket</strong>: <code>socket.js</code>에 접속한 클라이언트 세션을 <code>users</code> 메모리 맵에 보관 및 관리자가 강제퇴장(<code>forcedLogout</code>) 이벤트를 호출하면 해당 클라이언트 소켓의 접속을 끊고 세션을 제거하도록 구현.</li>
|
||||
</ul>
|
||||
|
||||
<h3>[3단계] 프론트엔드 어드민 대시보드 UI 연동</h3>
|
||||
<ul>
|
||||
<li>프로토타입 디자인 컨셉(Pretendard 서체, Forest Green 테마 색상)을 CSS 변수로 그대로 반영.</li>
|
||||
<li>모달 다이얼로그와 탭 렌더링에 실시간 API 데이터 통신 로직 바인딩.</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<!-- Section 3 -->
|
||||
<section id="schema-design">
|
||||
<h2 class="section-title">2. 데이터베이스 스키마 설계 (ERD)</h2>
|
||||
<p>기존에 이미 보유한 <code>tb_project.category</code> 및 <code>tb_user."group"</code> 구조를 활용한 관계 정의는 다음과 같습니다.</p>
|
||||
|
||||
<div class="diagram-container">
|
||||
<div class="mermaid">
|
||||
classDiagram
|
||||
class code_master {
|
||||
+main_code : VARCHAR(30) PK
|
||||
+main_code_nm : VARCHAR(100)
|
||||
+use_yn : CHAR(1)
|
||||
+rmk : VARCHAR(255)
|
||||
}
|
||||
class code_detail {
|
||||
+main_code : VARCHAR(30) PK/FK
|
||||
+sub_code : VARCHAR(30) PK
|
||||
+base_code : VARCHAR(61) UNIQUE
|
||||
+code_nm : VARCHAR(100)
|
||||
+sort_ord : INT
|
||||
+use_yn : CHAR(1)
|
||||
+rmk : VARCHAR(255)
|
||||
}
|
||||
class tb_project {
|
||||
+project_id : VARCHAR(50) PK
|
||||
+project_nm : VARCHAR(100)
|
||||
+category : VARCHAR(50) FK
|
||||
+limit_storage : INT
|
||||
+is_active : BOOLEAN
|
||||
}
|
||||
class tb_system_policy {
|
||||
+policy_id : INT PK
|
||||
+policy_key : VARCHAR(50) UNIQUE
|
||||
+limit_file_count : INT
|
||||
+limit_days : INT
|
||||
+is_active : BOOLEAN
|
||||
+upd_date : TIMESTAMP
|
||||
}
|
||||
class tb_user {
|
||||
+user_id : VARCHAR(50) PK
|
||||
+user_nm : VARCHAR(50)
|
||||
+user_pw : VARCHAR(255)
|
||||
+company : VARCHAR(50)
|
||||
+dept : VARCHAR(50)
|
||||
+position : VARCHAR(50)
|
||||
+group : VARCHAR(50) FK
|
||||
+is_resigned : BOOLEAN
|
||||
}
|
||||
class tb_permission {
|
||||
+project_id : VARCHAR(50) PK/FK
|
||||
+user_id : VARCHAR(50) PK/FK
|
||||
+lev : INT
|
||||
}
|
||||
class tb_banner_notice {
|
||||
+banner_id : SERIAL PK
|
||||
+project_id : VARCHAR(50) FK
|
||||
+reg_date : DATE
|
||||
+start_date : DATE
|
||||
+end_date : DATE
|
||||
+notice_text : TEXT
|
||||
+status_code : VARCHAR(61) FK
|
||||
}
|
||||
class tb_log {
|
||||
+log_id : SERIAL PK
|
||||
+project_id : VARCHAR(50)
|
||||
+activity : VARCHAR(100)
|
||||
+user_id : VARCHAR(50)
|
||||
+user_ip : VARCHAR(50)
|
||||
+log_date : TIMESTAMP
|
||||
+path_arr : TEXT[]
|
||||
}
|
||||
code_master "1" --> "0..*" code_detail
|
||||
code_detail "1" --> "0..*" tb_project : "category 참조"
|
||||
code_detail "1" --> "0..*" tb_user : "group 참조"
|
||||
code_detail "1" --> "0..*" tb_banner_notice : "status_code 참조"
|
||||
tb_project "1" --> "0..*" tb_permission
|
||||
tb_user "1" --> "0..*" tb_permission
|
||||
tb_project "1" --> "0..*" tb_banner_notice
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Section 4 -->
|
||||
<section id="screen-dashboard">
|
||||
<h2 class="section-title">3. 화면별 연동 테이블 및 데이터베이스 상세 매핑</h2>
|
||||
|
||||
<h3>📊 화면 1: 종합 용량 및 접속자 현황 (Dashboard)</h3>
|
||||
<p><strong>주요 데이터 흐름 및 활용 테이블:</strong></p>
|
||||
<ul>
|
||||
<li><strong>전체 사용 용량 KPI & 현장별 게이지바</strong>:
|
||||
<code>tb_project</code> 테이블의 한도 용량(<code>storage_byte</code>)과 <code>tb_data</code> 테이블의 실 누적 용량(<code>data_size</code>) 합산 결과 및 파일 건수를 <code>COUNT()</code>하여 렌더링.
|
||||
</li>
|
||||
<li><strong>실시간 접속자 세션 목록</strong>:
|
||||
데이터베이스를 경유하지 않고 Node.js 메인 프로세스(<code>socket.js</code>)의 메모리 세션 해시맵에서 직접 연결 상태(ID, IP, 위치 경로)를 추출.
|
||||
</li>
|
||||
<li><strong>대기 중인 압축작업</strong>:
|
||||
Redis(BullMQ Queue) 대기열 API를 조회하여 보류 중인 백그라운드 압축 다운로드 개수 파악.
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section id="screen-project">
|
||||
<h3>🏗️ 화면 2: 프로젝트 관리 (Project Management)</h3>
|
||||
<p><strong>주요 데이터 흐름 및 활용 테이블:</strong></p>
|
||||
<ul>
|
||||
<li><strong>좌측 프로젝트 그리드</strong>: <code>tb_project</code> 테이블 전체 데이터와 <code>code_detail</code>의 카테고리 코드 한글 명칭(TDC, GPD 등)을 <code>tb_project.category</code> 외래키 관계를 조인하여 렌더링.</li>
|
||||
<li><strong>우측 참여 권한 사용자 목록</strong>: <code>tb_permission</code> $\bowtie$ <code>tb_user</code> 조인을 통해 해당 현장에 기속된 사용자 리스트업 및 등급 인라인 셀렉트 업데이트.</li>
|
||||
<li><strong>사용자 배정 추가 모달</strong>: <code>tb_user</code>의 전체 사용자 데이터와 현재 프로젝트에 속해 있지 않은 유저 차집합 연산으로 가용 유저 목록 구성.</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section id="screen-banner">
|
||||
<h3>📢 화면 3: 실시간 배너 공지 (Banner Notice)</h3>
|
||||
<p><strong>주요 데이터 흐름 및 활용 테이블:</strong></p>
|
||||
<ul>
|
||||
<li><strong>공지사항 이력 및 등록 폼</strong>: <code>tb_banner_notice</code> 테이블 CRUD 매핑.</li>
|
||||
<li><strong>송출 상태 계산</strong>:
|
||||
오늘 날짜 기준 <code>start_date</code>와 <code>end_date</code> 조건을 체크하여 공통코드 매핑 상태(<code>NOTICE_STATUS_active</code>, <code>NOTICE_STATUS_scheduled</code>, <code>NOTICE_STATUS_expired</code>)를 동적 할당.
|
||||
</li>
|
||||
<li><strong>검색 필터</strong>: 송출상태 및 등록일 범위(From ~ To) 기준 <code>WHERE status_code = ? AND reg_date BETWEEN ? AND ?</code> 쿼리 연동.</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section id="screen-user">
|
||||
<h3>👥 화면 4: 사용자 관리 (User Management)</h3>
|
||||
<p><strong>주요 데이터 흐름 및 활용 테이블:</strong></p>
|
||||
<ul>
|
||||
<li><strong>좌측 계정 목록</strong>: <code>tb_user</code> 테이블 및 <code>code_detail</code> 권한그룹(기존 <code>"group"</code> 컬럼 재사용) 매핑 렌더링.</li>
|
||||
<li><strong>우측 참여 프로젝트 목록</strong>: <code>tb_permission</code> $\bowtie$ <code>tb_project</code> 조인을 활용하여 선택 유저가 소속된 모든 프로젝트 ID 및 현장명 출력.</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section id="screen-audit">
|
||||
<h3>🔎 화면 5: 감사 로그 조회 (Audit Logs)</h3>
|
||||
<p><strong>주요 데이터 흐름 및 활용 테이블:</strong></p>
|
||||
<ul>
|
||||
<li><strong>시스템 감사 이력 목록</strong>:
|
||||
파일 삭제/이동/다운로드 이벤트를 적재하는 <code>tb_log</code> 테이블을 필터 검색 쿼리(사용자 ID, 액션타입)와 연동하여 리스트 렌더링.
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section id="screen-policy">
|
||||
<h3>⚙️ 화면 6: 자동 보존 및 파일 삭제 정책 설정 (Delete Policy)</h3>
|
||||
<p><strong>주요 데이터 흐름 및 활용 테이블:</strong></p>
|
||||
<ul>
|
||||
<li><strong>시스템 공통 자동 삭제 정책 설정 폼</strong>:
|
||||
프로젝트 단위 설정을 탈피하고, 신설된 <code>tb_system_policy</code>의 단일 글로벌 레코드를 제어.
|
||||
</li>
|
||||
<li><strong>자동 삭제 정기 실행 이력</strong>:
|
||||
정기 스케줄러 배치 구동 시의 로그 기록을 <code>tb_auto_clean_log</code>에서 리스트업. 설정값 저장 및 기록 적재 시 프로젝트 ID 대신 <code>'SYSTEM'</code> 기록 식별자를 사용.
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section id="screen-codes">
|
||||
<h3>🔑 화면 7: 공통 코드 관리 (Common Code Management)</h3>
|
||||
<p><strong>주요 데이터 흐름 및 활용 테이블:</strong></p>
|
||||
<ul>
|
||||
<li><strong>상단 마스터 대분류</strong>: <code>code_master</code> 테이블 CRUD.</li>
|
||||
<li><strong>하단 상세 소분류</strong>: <code>code_detail</code> 테이블 CRUD.
|
||||
<ul>
|
||||
<li>대분류 선택이 없을 시 소분류 등록 폼 진입 차단 유효성 제어.</li>
|
||||
<li>소분류의 <code>base_code</code> 컬럼은 <code>main_code || '_' || sub_code</code> 조합으로 자동 동기화.</li>
|
||||
<li>대분류 삭제 시 외래키 <code>ON DELETE CASCADE</code> 명세를 통하여 소분류 레코드가 데이터베이스에서 연쇄적으로 자동 청소되도록 설정.</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<!-- Section 5 -->
|
||||
<section id="api-endpoints">
|
||||
<h2 class="section-title">4. 핵심 백엔드 CRUD API Endpoint 설계안</h2>
|
||||
<p>프론트엔드-백엔드 간 통신을 위해 구성되어야 할 RESTful API 리스트입니다.</p>
|
||||
|
||||
<h3>1. 프로젝트 관리 API (<code>/api/admin/projects</code>)</h3>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 15%;">메소드</th>
|
||||
<th style="width: 35%;">엔드포인트</th>
|
||||
<th style="width: 50%;">설명</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><span class="method-badge method-get">GET</span></td>
|
||||
<td class="api-url">/</td>
|
||||
<td>전체 프로젝트 및 카테고리 정보 조회</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="method-badge method-post">POST</span></td>
|
||||
<td class="api-url">/</td>
|
||||
<td>신규 프로젝트 등록</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="method-badge method-put">PUT</span></td>
|
||||
<td class="api-url">/:id</td>
|
||||
<td>특정 프로젝트 내용(카테고리 category 값, 용량제한, 활성화) 갱신</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="method-badge method-delete">DELETE</span></td>
|
||||
<td class="api-url">/:id</td>
|
||||
<td>프로젝트 영구 삭제 (참여 권한 및 메타데이터 자동 CASCADE)</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h3>2. 프로젝트 권한 배정 API (<code>/api/admin/permissions</code>)</h3>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 15%;">메소드</th>
|
||||
<th style="width: 35%;">엔드포인트</th>
|
||||
<th style="width: 50%;">설명</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><span class="method-badge method-get">GET</span></td>
|
||||
<td class="api-url">/project/:projectId</td>
|
||||
<td>특정 현장에 참여 중인 유저 목록 조회</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="method-badge method-post">POST</span></td>
|
||||
<td class="api-url">/assign</td>
|
||||
<td>현장에 특정 사용자 권한 신규 부여 (다중 배정)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="method-badge method-put">PUT</span></td>
|
||||
<td class="api-url">/update</td>
|
||||
<td>배정 유저의 권한 등급(lev) 수정</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="method-badge method-delete">DELETE</span></td>
|
||||
<td class="api-url">/remove</td>
|
||||
<td>현장 참여 권한 배정 제외 (매핑 행 삭제)</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h3>3. 실시간 배너 공지 API (<code>/api/admin/banners</code>)</h3>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 15%;">메소드</th>
|
||||
<th style="width: 35%;">엔드포인트</th>
|
||||
<th style="width: 50%;">설명</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><span class="method-badge method-get">GET</span></td>
|
||||
<td class="api-url">/</td>
|
||||
<td>배너 송출 이력 조회 (상태, 날짜검색 필터 지원)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="method-badge method-post">POST</span></td>
|
||||
<td class="api-url">/</td>
|
||||
<td>신규 배너 공지 작성 및 등록</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="method-badge method-put">PUT</span></td>
|
||||
<td class="api-url">/stop/:id</td>
|
||||
<td>공지 수동 송출 중지 (상태를 expired로 강제 업데이트)</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h3>4. 사용자 관리 API (<code>/api/admin/users</code>)</h3>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 15%;">메소드</th>
|
||||
<th style="width: 35%;">엔드포인트</th>
|
||||
<th style="width: 50%;">설명</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><span class="method-badge method-get">GET</span></td>
|
||||
<td class="api-url">/</td>
|
||||
<td>전체 계정 정보 및 권한그룹 조회</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="method-badge method-get">GET</span></td>
|
||||
<td class="api-url">/:id/permissions</td>
|
||||
<td>해당 유저가 참여 권한을 지닌 프로젝트 목록 조회</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="method-badge method-post">POST</span></td>
|
||||
<td class="api-url">/</td>
|
||||
<td>신규 사용자 계정 등록 (패스워드 bcrypt 암호화)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="method-badge method-put">PUT</span></td>
|
||||
<td class="api-url">/:id</td>
|
||||
<td>사용자 정보(권한 그룹 group 포함) 및 재직 상태 갱신</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="method-badge method-delete">DELETE</span></td>
|
||||
<td class="api-url">/:id</td>
|
||||
<td>사용자 계정 삭제 (권한 정보 및 설정 연쇄 삭제)</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h3>5. 공통 코드 관리 API (<code>/api/admin/common-codes</code>)</h3>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 15%;">메소드</th>
|
||||
<th style="width: 35%;">엔드포인트</th>
|
||||
<th style="width: 50%;">설명</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><span class="method-badge method-get">GET</span></td>
|
||||
<td class="api-url">/masters</td>
|
||||
<td>대분류 마스터 코드 목록 조회</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="method-badge method-post">POST</span></td>
|
||||
<td class="api-url">/masters</td>
|
||||
<td>신규 대분류 등록</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="method-badge method-put">PUT</span></td>
|
||||
<td class="api-url">/masters/:code</td>
|
||||
<td>대분류 수정</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="method-badge method-delete">DELETE</span></td>
|
||||
<td class="api-url">/masters/:code</td>
|
||||
<td>대분류 삭제 (하위 소분류 자동 연쇄 삭제)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="method-badge method-get">GET</span></td>
|
||||
<td class="api-url">/details/:mainCode</td>
|
||||
<td>선택된 대분류에 해당하는 소분류 정렬 조회</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="method-badge method-post">POST</span></td>
|
||||
<td class="api-url">/details</td>
|
||||
<td>신규 소분류 등록 (base_code 자동 연산 생성)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="method-badge method-put">PUT</span></td>
|
||||
<td class="api-url">/details/:mainCode/:subCode</td>
|
||||
<td>소분류 수정 (명칭, 정렬순서, 사용여부)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="method-badge method-delete">DELETE</span></td>
|
||||
<td class="api-url">/details/:mainCode/:subCode</td>
|
||||
<td>소분류 삭제</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h3>6. 시스템 공통 보존 정책 API (<code>/api/admin/system-policy</code>)</h3>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 15%;">메소드</th>
|
||||
<th style="width: 35%;">엔드포인트</th>
|
||||
<th style="width: 50%;">설명</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><span class="method-badge method-get">GET</span></td>
|
||||
<td class="api-url">/</td>
|
||||
<td>시스템 글로벌 보존 정책 조회</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="method-badge method-post">POST</span></td>
|
||||
<td class="api-url">/update</td>
|
||||
<td>글로벌 보존 정책 값 갱신 및 저장</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
586
PM관리자화면개발산출물_20260611/통합관리자대시보드_화면설계서.html
Normal file
586
PM관리자화면개발산출물_20260611/통합관리자대시보드_화면설계서.html
Normal file
@@ -0,0 +1,586 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ko">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>PM_ver4 통합 관리자 어플리케이션 화면설계서</title>
|
||||
<!-- Google Fonts - Inter & Noto Sans KR -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Noto+Sans+KR:wght@300;400;500;700&display=swap" rel="stylesheet">
|
||||
|
||||
<style>
|
||||
:root {
|
||||
--primary: #1e5149;
|
||||
--primary-dark: #142e29;
|
||||
--primary-soft: #e9eeed;
|
||||
--border: #d2dcdb;
|
||||
--accent: #4db251;
|
||||
--bg: #f4f7f6;
|
||||
--card-bg: #ffffff;
|
||||
--text-main: #1f2937;
|
||||
--text-muted: #4b5563;
|
||||
--shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.05), 0 2px 4px -2px rgba(0, 0, 0, 0.05);
|
||||
--sidebar-width: 280px;
|
||||
|
||||
/* UI Status Colors */
|
||||
--color-active: #4db251;
|
||||
--bg-active: #eef8ee;
|
||||
--color-inactive: #a5b9b6;
|
||||
--bg-inactive: #f1f5f9;
|
||||
--color-danger: #ef4444;
|
||||
--bg-danger: #fee2e2;
|
||||
--color-warning: #ff9800;
|
||||
--bg-warning: #fff5e6;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Inter', 'Noto Sans KR', sans-serif;
|
||||
background-color: var(--bg);
|
||||
color: var(--text-main);
|
||||
line-height: 1.7;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
/* Sidebar Navigation */
|
||||
aside {
|
||||
width: var(--sidebar-width);
|
||||
background: linear-gradient(180deg, var(--primary-dark) 0%, #0c1a18 100%);
|
||||
color: #ffffff;
|
||||
height: 100vh;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
overflow-y: auto;
|
||||
border-right: 1px solid rgba(255, 255, 255, 0.1);
|
||||
padding: 24px;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
aside h2 {
|
||||
font-size: 1.15rem;
|
||||
font-weight: 700;
|
||||
margin-bottom: 24px;
|
||||
color: var(--accent);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.15);
|
||||
padding-bottom: 12px;
|
||||
}
|
||||
|
||||
aside ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
aside li {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
aside a {
|
||||
color: #b3c5c2;
|
||||
text-decoration: none;
|
||||
font-size: 0.9rem;
|
||||
display: block;
|
||||
padding: 8px 12px;
|
||||
border-radius: 6px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
aside a:hover, aside li.active a {
|
||||
color: #ffffff;
|
||||
background-color: rgba(255, 255, 255, 0.08);
|
||||
padding-left: 16px;
|
||||
}
|
||||
|
||||
aside .category-title {
|
||||
font-size: 0.75rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
color: #628781;
|
||||
margin: 16px 0 8px 12px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
/* Main Content */
|
||||
main {
|
||||
margin-left: var(--sidebar-width);
|
||||
flex-grow: 1;
|
||||
padding: 40px 50px;
|
||||
max-width: 1000px;
|
||||
}
|
||||
|
||||
header {
|
||||
border-bottom: 2px solid var(--border);
|
||||
padding-bottom: 24px;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
header h1 {
|
||||
font-size: 2.2rem;
|
||||
font-weight: 700;
|
||||
margin: 0 0 10px 0;
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
header .subtitle {
|
||||
font-size: 1.1rem;
|
||||
color: var(--text-muted);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Content Sections */
|
||||
section {
|
||||
background-color: var(--card-bg);
|
||||
border-radius: 12px;
|
||||
padding: 30px;
|
||||
margin-bottom: 30px;
|
||||
box-shadow: var(--shadow);
|
||||
border: 1px solid var(--border);
|
||||
}
|
||||
|
||||
h2.section-title {
|
||||
font-size: 1.4rem;
|
||||
font-weight: 700;
|
||||
color: var(--primary);
|
||||
margin-top: 0;
|
||||
margin-bottom: 20px;
|
||||
border-left: 4px solid var(--accent);
|
||||
padding-left: 12px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.2rem;
|
||||
font-weight: 600;
|
||||
color: var(--primary-dark);
|
||||
margin-top: 24px;
|
||||
margin-bottom: 12px;
|
||||
border-bottom: 1px solid #f1f5f9;
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 1.05rem;
|
||||
font-weight: 600;
|
||||
color: var(--primary);
|
||||
margin-top: 16px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0 0 16px 0;
|
||||
color: var(--text-main);
|
||||
}
|
||||
|
||||
/* Color Chips */
|
||||
.color-palette {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 16px;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.color-chip {
|
||||
flex: 1 1 200px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
background-color: #ffffff;
|
||||
box-shadow: var(--shadow);
|
||||
}
|
||||
|
||||
.color-box {
|
||||
height: 60px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.color-details {
|
||||
padding: 10px 12px;
|
||||
}
|
||||
|
||||
.color-name {
|
||||
font-weight: 700;
|
||||
font-size: 0.85rem;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.color-value {
|
||||
font-family: monospace;
|
||||
font-size: 0.8rem;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
/* Layout Preview map */
|
||||
.layout-preview {
|
||||
background-color: #1e293b;
|
||||
color: #cbd5e1;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
font-family: monospace;
|
||||
font-size: 0.8rem;
|
||||
white-space: pre;
|
||||
overflow-x: auto;
|
||||
margin: 20px 0;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
/* Status Badge */
|
||||
.status-badge {
|
||||
display: inline-block;
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 0.8rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.status-badge-active { background-color: var(--bg-active); color: var(--color-active); }
|
||||
.status-badge-inactive { background-color: var(--bg-inactive); color: var(--color-inactive); }
|
||||
.status-badge-danger { background-color: var(--bg-danger); color: var(--color-danger); }
|
||||
.status-badge-warning { background-color: var(--bg-warning); color: var(--color-warning); }
|
||||
|
||||
/* Tables */
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin: 16px 0;
|
||||
font-size: 0.88rem;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
border: 1px solid var(--border);
|
||||
}
|
||||
|
||||
th {
|
||||
background-color: var(--primary-soft);
|
||||
color: var(--primary-dark);
|
||||
font-weight: 600;
|
||||
text-align: left;
|
||||
padding: 10px 14px;
|
||||
border-bottom: 2px solid var(--border);
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 10px 14px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
color: var(--text-main);
|
||||
}
|
||||
|
||||
tr:last-child td {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
/* Lists */
|
||||
ul {
|
||||
padding-left: 20px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
li {
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.spec-card {
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
margin-bottom: 16px;
|
||||
background-color: #f9fafb;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!-- Sidebar Navigation -->
|
||||
<aside>
|
||||
<h2>📁 PM_ver4 Spec</h2>
|
||||
<ul>
|
||||
<li><a href="#overview">설계서 소개</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="category-title">01. 디자인 시스템</div>
|
||||
<ul>
|
||||
<li><a href="#design-system">디자인 가이드라인</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="category-title">02. 공통 프레임</div>
|
||||
<ul>
|
||||
<li><a href="#layout">App Frame 레이아웃</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="category-title">03. 화면별 UI/UX 명세</div>
|
||||
<ul>
|
||||
<li><a href="#screen-dashboard">📊 종합 용량 및 접속자</a></li>
|
||||
<li><a href="#screen-project">🏗️ 프로젝트 관리</a></li>
|
||||
<li><a href="#screen-banner">📢 실시간 배너 공지</a></li>
|
||||
<li><a href="#screen-user">👥 사용자 관리</a></li>
|
||||
<li><a href="#screen-audit">🔎 감사 로그 조회</a></li>
|
||||
<li><a href="#screen-policy">⚙️ 보관 및 삭제 설정</a></li>
|
||||
<li><a href="#screen-codes">🔑 공통 코드 관리</a></li>
|
||||
</ul>
|
||||
</aside>
|
||||
|
||||
<!-- Main Content -->
|
||||
<main>
|
||||
<header id="overview">
|
||||
<h1>PM_ver4 통합 관리자 어플리케이션 화면설계서</h1>
|
||||
<p class="subtitle">UI/UX Specification - 대시보드 및 관리자 화면 기능 명세서</p>
|
||||
</header>
|
||||
|
||||
<!-- Section 1 -->
|
||||
<section id="design-system">
|
||||
<h2 class="section-title">1. 공통 UI 가이드라인 및 디자인 시스템</h2>
|
||||
<p>관리자 패널의 모든 화면 레이아웃, 컴포넌트 명세, 사용자 액션 및 데이터 정합성 검증 규칙은 본 시스템의 디자인 규칙을 준수합니다.</p>
|
||||
|
||||
<h3>① 색상 토큰 (Color Tokens)</h3>
|
||||
<div class="color-palette">
|
||||
<div class="color-chip">
|
||||
<div class="color-box" style="background-color: #1e5149;"></div>
|
||||
<div class="color-details">
|
||||
<div class="color-name">Primary Forest Green</div>
|
||||
<div class="color-value">#1e5149</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="color-chip">
|
||||
<div class="color-box" style="background-color: #142e29;"></div>
|
||||
<div class="color-details">
|
||||
<div class="color-name">Dark Teal Sidebar</div>
|
||||
<div class="color-value">#142e29</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="color-chip">
|
||||
<div class="color-box" style="background-color: #d2dcdb;"></div>
|
||||
<div class="color-details">
|
||||
<div class="color-name">Light Green Gray Border</div>
|
||||
<div class="color-value">#d2dcdb</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="color-chip">
|
||||
<div class="color-box" style="background-color: #e9eeed;"></div>
|
||||
<div class="color-details">
|
||||
<div class="color-name">Soft Accent Green BG</div>
|
||||
<div class="color-value">#e9eeed</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3>② 서체 (Typography)</h3>
|
||||
<ul>
|
||||
<li><strong>서체 패밀리</strong>: <code>'Pretendard Variable', 'Pretendard', 'Inter', 'Noto Sans KR', sans-serif</code></li>
|
||||
<li><strong>글꼴 크기 명세</strong>:
|
||||
<ul>
|
||||
<li>페이지 메인 타이틀: <code>1.25rem</code> (700 Bold)</li>
|
||||
<li>카드 타이틀: <code>1.05rem</code> (700 Bold)</li>
|
||||
<li>일반 본문 및 테이블 데이터: <code>0.875rem</code> (500 Medium / 600 Semi-Bold)</li>
|
||||
<li>Muted 보조 텍스트: <code>0.8rem</code> / <code>0.75rem</code></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h3>③ 공통 그리드 & 테이블 (Table Grid Rules)</h3>
|
||||
<ul>
|
||||
<li><strong>순번 표시 (NO)</strong>: 모든 테이블 그리드의 1열은 데이터 인덱스 번호(<code>NO</code>)를 필수 노출합니다.</li>
|
||||
<li><strong>가로 구분선</strong>: 답답한 디자인을 방지하기 위해 세로 테두리(Vertical Borders)는 일절 노출하지 않으며, 가로 행 구분선만 노출합니다.</li>
|
||||
<li><strong>행 선택 인터랙션</strong>: 마우스 호버 시 <code>var(--primary-soft)</code> 배경색을 지정하며, 클릭 행 활성화 시 텍스트 두께 변경으로 인한 줄높이 왜곡이 없도록 패딩 상속(inherit) 속성을 적용하여 균일한 행 높이를 유지합니다.</li>
|
||||
</ul>
|
||||
|
||||
<h3>④ 공통 모달 팝업 (Modal Overlay Rules)</h3>
|
||||
<ul>
|
||||
<li><strong>백드롭</strong>: <code>rgba(20, 30, 29, 0.6)</code> 반투명 딤 및 <code>backdrop-filter: blur(4px)</code> 효과로 모달 포커스를 고정합니다.</li>
|
||||
<li><strong>트랜지션</strong>: <code>fade-in</code> 및 <code>slide-up (translateY 30px to 0)</code>이 일괄 적용되어 매끄러운 팝업 동작을 보장합니다.</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<!-- Section 2 -->
|
||||
<section id="layout">
|
||||
<h2 class="section-title">2. 레이아웃 구조 설계 (App Frame Layout)</h2>
|
||||
<p>LNB, 상단 헤더, 그리고 메인 콘텐츠 탭 영역의 수평/수직 분할 아키텍처는 다음과 같습니다.</p>
|
||||
|
||||
<div class="layout-preview">
|
||||
+-------------------------------------------------------------------------+
|
||||
| LNB (좌측 사이드바) | Main Header (상단 헤더) |
|
||||
| 📁 PM_ver4 Admin | [Header Title] [Admin Profile] |
|
||||
|-------------------------+-----------------------------------------------|
|
||||
| - Dashboards | Main Content (메인 콘텐츠 탭 영역) |
|
||||
| 📊 종합 용량/접속자 | |
|
||||
| - 프로젝트 관리 | +-----------------------------------------+ |
|
||||
| 🏗️ 프로젝트 관리 | | 카드 1 (필터 / 테이블 리스트) | |
|
||||
| 📢 실시간 배너 공지 | +-----------------------------------------+ |
|
||||
| - 사용자 및 권한 | | 카드 2 (상세정보 뷰 / 팝업 연동 리스트) | |
|
||||
| 👥 사용자 관리 | +-----------------------------------------+ |
|
||||
| - 시스템 감사 및 환경 | |
|
||||
| 🔎 감사 로그 조회 | |
|
||||
| ⚙️ 자동 삭제 설정 | |
|
||||
| 🔑 공통 코드 관리 | |
|
||||
+-------------------------------------------------------------------------+</div>
|
||||
</section>
|
||||
|
||||
<!-- Section 3 -->
|
||||
<section id="screen-dashboard">
|
||||
<h2 class="section-title">3. 화면별 상세 UI 및 기능 설계</h2>
|
||||
|
||||
<h3>📊 화면 1: 종합 용량 및 접속자 현황 (Dashboard)</h3>
|
||||
<p>상단 3열 KPI 요약 카드와 하단 스토리지 프로그레스바 및 실시간 소켓 접속자 테이블 구조입니다.</p>
|
||||
|
||||
<div class="spec-card">
|
||||
<h4>① 주요 UI 컴포넌트</h4>
|
||||
<ul>
|
||||
<li><strong>스토리지 KPI 카드</strong>: 전체 현장의 총 한도 용량 대비 누적 사용 용량 실시간 합산 표시 (예: <code>💾 9.70 GB / 20 GB</code>).</li>
|
||||
<li><strong>접속자 KPI 카드</strong>: 현재 소켓 서버 연결 세션 수 노출.</li>
|
||||
<li><strong>압축작업 KPI 카드</strong>: Redis(BullMQ) 내 대기중인 압축 건수.</li>
|
||||
<li><strong>현장별 스토리지 사용 현황</strong>: 각 프로젝트 ID/명칭, 게이지바 및 사용량 정보(GB / 백분율% / 파일수량개) 동시 렌더링.</li>
|
||||
</ul>
|
||||
|
||||
<h4>② 실시간 접속 현황 테이블 사양</h4>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 10%;">NO</th>
|
||||
<th style="width: 25%;">사용자 ID</th>
|
||||
<th style="width: 25%;">접속 IP</th>
|
||||
<th style="width: 25%;">현재 조회 경로</th>
|
||||
<th style="width: 15%;">작업</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td>admin_test</td>
|
||||
<td>127.0.0.1</td>
|
||||
<td><code>/PM_TEST_01/archive</code></td>
|
||||
<td><span class="status-badge status-badge-danger" style="cursor: pointer;">강제퇴장</span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="screen-project">
|
||||
<h3>🏗️ 화면 2: 프로젝트 관리 (Project Management)</h3>
|
||||
<p>좌측 프로젝트 목록 그리드와 우측 프로젝트별 참여자 및 배정 통제 그리드 구조입니다.</p>
|
||||
|
||||
<div class="spec-card">
|
||||
<h4>① 프로젝트 목록 테이블 사양 (좌측 카드)</h4>
|
||||
<p>출력 컬럼: <code>NO</code> | <code>프로젝트 ID</code> | <code>현장명</code> | <code>카테고리</code> | <code>용량 제한(GB)</code> | <code>상태</code> | <code>관리(수정/삭제)</code></p>
|
||||
<ul>
|
||||
<li>행 클릭 시, 우측의 '참여 권한 사용자 목록'이 해당 프로젝트 정보로 자동 리바인딩됩니다.</li>
|
||||
<li><strong>삭제 제한</strong>: 관련 테이블(tb_data, tb_official_doc_file, tb_banner_notice 등)에 현장 ID 사용 이력이 있으면 삭제 불가능하며 경고 메시지가 발생합니다.</li>
|
||||
<li><strong>신규 프로젝트 등록 및 수정 모달 (projectModalOverlay)</strong>: 프로젝트 ID(수정 시 Readonly), 프로젝트명, 단축명, 카테고리 Select, 스토리지 제한(GB), 상태를 편집합니다.</li>
|
||||
</ul>
|
||||
|
||||
<h4>② 참여 권한 사용자 목록 (우측 카드 - 병합 영역)</h4>
|
||||
<p>출력 컬럼: <code>NO</code> | <code>사용자 ID</code> | <code>이름</code> | <code>부서/직급</code> | <code>권한 등급</code> | <code>작업(배정제외)</code></p>
|
||||
<ul>
|
||||
<li><strong>권한 등급 변경</strong>: 인라인 셀렉터(Admin, Sub-Master, Worker, Viewer)로 권한 레벨 즉시 업데이트.</li>
|
||||
<li><strong>사용자 배정 추가 팝업 모달 (assignModalOverlay)</strong>: 현재 현장에 미배정된 사용자들을 체크박스로 다중 선택하여 일괄 추가합니다. 또한 목록 선택식을 지원하기 위해 우측 하단 배정 대기 목록에서도 '즉시 배정' 단축 버튼을 제공합니다.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="screen-banner">
|
||||
<h3>📢 화면 3: 실시간 배너 공지 (Banner Notice)</h3>
|
||||
<p>상단 배너 등록 폼 카드 및 하단 이력 검색 조건 필터와 이력 목록 그리드 구조입니다.</p>
|
||||
|
||||
<div class="spec-card">
|
||||
<h4>① 배너 공지 등록 폼</h4>
|
||||
<ul>
|
||||
<li>입력 필드: 대상 프로젝트 선택(특정 현장 또는 전체 현장 'all' 매핑), 등록일(임의 편집 지원), 시작일, 종료일, 공지 자막 텍스트.</li>
|
||||
<li>송출 등록 제출 시, 오늘 일자와 비교하여 즉시 이력에 추가되고 상태 배지가 실시간 부여됩니다.</li>
|
||||
</ul>
|
||||
|
||||
<h4>② 이력 목록 필터 및 이력 테이블</h4>
|
||||
<ul>
|
||||
<li><strong>검색 필터</strong>: 송출 상태(전체, 송출중, 예약됨, 만료) 및 등록일(from ~ to) 날짜 범위 지정 검색.</li>
|
||||
<li><strong>테이블 명세</strong>: <code>NO</code> | <code>등록일</code> | <code>대상 프로젝트</code> | <code>공지 내용</code> | <code>시작일</code> | <code>종료일</code> | <code>송출 상태</code> | <code>작업</code></li>
|
||||
<li><strong>송출 중지 통제</strong>: 아직 기간이 유효한 행(송출중, 예약됨)에만 <code>[송출 중지]</code> 버튼이 노출 및 활성화되며, 이미 만료된 이력은 <code>[중지 완료]</code> 비활성 텍스트로 대체하여 이중 제어를 차단합니다.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="screen-user">
|
||||
<h3>👥 화면 4: 사용자 관리 (User Management)</h3>
|
||||
<p>좌측 사용자 마스터 리스트 및 우측 선택된 사용자의 참여 권한 프로젝트 리스트업 구조입니다.</p>
|
||||
|
||||
<div class="spec-card">
|
||||
<h4>① 사용자 계정 목록 (좌측 카드)</h4>
|
||||
<p>출력 컬럼: <code>NO</code> | <code>아이디</code> | <code>이름</code> | <code>소속/직급</code> | <code>그룹</code> | <code>상태</code> | <code>관리(수정/삭제)</code></p>
|
||||
<ul>
|
||||
<li>행 클릭 시, 해당 사용자가 참여하고 있는 프로젝트 리스트가 우측 카드에 즉시 바인딩됩니다.</li>
|
||||
<li><strong>삭제 제한</strong>: 권한 테이블(tb_permission)에 현장 배정/참여 권한 정보가 등록되어 있으면 삭제가 불가능하며 경고 메시지가 발생합니다.</li>
|
||||
<li><strong>사용자 등록 및 수정 모달 (userModalOverlay)</strong>: 아이디(수정 시 Readonly), 패스워드, 이름, 회사명, 부서, 직급, 권한 그룹 지정 select, 재직 상태(재직/퇴직잠금) 지정.</li>
|
||||
</ul>
|
||||
|
||||
<h4>② 권한부여 프로젝트 목록 (우측 카드)</h4>
|
||||
<p>출력 컬럼: <code>NO</code> | <code>프로젝트 ID</code> | <code>프로젝트명</code> | <code>부여 권한 등급</code></p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="screen-audit">
|
||||
<h3>🔎 화면 5: 감사 로그 조회 (Audit Logs)</h3>
|
||||
<p>파일 조작 중요 이벤트(삭제, 이동, 다운로드) 목록 및 검색 조회 화면입니다.</p>
|
||||
|
||||
<div class="spec-card">
|
||||
<h4>① 감사 로그 목록 사양</h4>
|
||||
<p>출력 컬럼: <code>NO</code> | <code>일시</code> | <code>프로젝트</code> | <code>사용자 ID</code> | <code>접속 IP</code> | <code>조작 액션</code> | <code>조작 대상 경로(코드박스 스타일)</code></p>
|
||||
<ul>
|
||||
<li>필터링 항목: 사용자 ID 검색 입력란, 조작 액션 Dropdown, 검색 기능.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="screen-policy">
|
||||
<h3>⚙️ 화면 6: 자동 보관 및 파일 삭제 정책 설정 (Delete Policy)</h3>
|
||||
<p>시스템 글로벌 일괄 정책 설정 영역, 실시간 예정 시나리오 요약, 그리고 배치 처리 이력 구조입니다.</p>
|
||||
|
||||
<div class="spec-card">
|
||||
<h4>① 시스템 공통 자동 삭제 정책 설정 폼 [글로벌 정책 공통화]</h4>
|
||||
<ul>
|
||||
<li><strong>입력 필드</strong>: 정책 활성화 여부(Toggle/Select), 최소 유지 파일 개수 기준(숫자 입력), 자동 삭제 제한 기한(일) (숫자 입력).</li>
|
||||
<li><strong>글로벌 통합</strong>: 기존의 프로젝트 개별 Dropdown은 완전히 배제하고, 전체 현장에 동일하게 일괄 반영합니다.</li>
|
||||
</ul>
|
||||
|
||||
<h4>② 보존 정책 실시간 요약 (Dynamic Summary)</h4>
|
||||
<ul>
|
||||
<li>폼의 입력값을 변경하는 즉시 요약 영역 텍스트가 시나리오 문구로 동적 조합되어 나타납니다.</li>
|
||||
<li>예: <code>"현재 전체 공통 설정에 따라, 각 현장의 보관 파일 수가 100개 미만이고 30일이 지나면 자동 삭제 배치 스케줄러가 작동합니다."</code></li>
|
||||
</ul>
|
||||
|
||||
<h4>③ 자동 삭제 처리 이력 테이블 사양</h4>
|
||||
<p>출력 컬럼: <code>NO</code> | <code>자동 처리 일자</code> | <code>프로젝트 ID</code> | <code>삭제 처리 폴더 경로</code> | <code>적용 기준</code> | <code>처리 결과(성공 배지)</code></p>
|
||||
<ul>
|
||||
<li>정책 값 저장 완료 시 이력 로그의 대상 프로젝트 ID 자리에는 <code>'SYSTEM'</code>이 기입됩니다.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="screen-codes">
|
||||
<h3>🔑 화면 7: 공통 코드 관리 (Common Code Management)</h3>
|
||||
<p>대분류 마스터 및 세부 코드 리스트가 배치되는 상하 2단 수직 정렬 레이아웃 구조입니다.</p>
|
||||
|
||||
<div class="spec-card">
|
||||
<h4>① 대분류 코드 마스터 (상단 카드)</h4>
|
||||
<p>출력 컬럼: <code>NO</code> | <code>대분류 코드</code> | <code>대분류 코드명</code> | <code>사용</code> | <code>관리(수정/삭제)</code></p>
|
||||
<ul>
|
||||
<li>행을 선택(click)하면 해당 행이 하이라이트(selected)되며, 하단의 세부 코드 그리드가 동적으로 새로고침됩니다.</li>
|
||||
<li><strong>대분류 등록 및 수정 모달 (codeMasterModalOverlay)</strong>: 대분류 코드, 명칭, 사용여부, 비고 설명 입력.</li>
|
||||
</ul>
|
||||
|
||||
<h4>② 세부 소분류 코드 목록 (하단 카드)</h4>
|
||||
<p>출력 컬럼: <code>NO</code> | <code>소분류 코드</code> | <code>조합 코드 (base_code)</code> | <code>코드 명칭</code> | <code>정렬 순서</code> | <code>사용</code> | <code>관리(수정/삭제)</code></p>
|
||||
<ul>
|
||||
<li><strong>유효성 방어 차단</strong>: 상단 대분류 테이블에서 행을 클릭하여 선택하지 않은 상태에서는 하단의 <code>[➕ 세부코드 등록]</code> 버튼이 강제 비활성화(disabled)되며, 팝업 접근 시 안내 팝업 및 경고 텍스트(<code>"상단에서 대분류 코드를 선택해 주세요."</code>)를 노출합니다.</li>
|
||||
<li><strong>조합 코드(base_code)</strong>: 소분류 생성 완료 제출 시, <code>대분류코드_소분류코드</code> 형태로 자동 결합되어 저장됩니다.</li>
|
||||
<li><strong>삭제 제한 (RESTRICT)</strong>: 마스터 대분류 코드를 삭제할 경우, 하위 세부 코드(code_detail)가 존재하면 대분류 삭제가 차단되고 경고 메시지를 노출합니다. (세부 코드가 먼저 삭제되어 비어있을 때만 대분류 삭제 가능)</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user