Web UI
13.1 개요
섹션 제목: “13.1 개요”아래 loopback URL은 제품 노드 안에서의 로컬 점검 예시입니다.
원격 브라우저는 설치 시 설정한 관리 주소의 https://<관리-주소>/ui/와 wss://를 사용합니다.
| 항목 | 값 |
|---|---|
| URL | http://127.0.0.1:8080/ui/ |
| 이벤트 센터 | http://127.0.0.1:8080/ui#/ops-triage |
| bootstrap admin | admin / configured password |
| 전용 admin | 설치 직후 RBAC admin 역할 사용자 추가 권장 |
| 앱 셸 | 고정 사이드바 트리(236px) + topbar(브레드크럼·통합 검색·세션) + 글로벌 statusbar |
| JS 모듈 | app.js + app.bundle.js + modules/*.js 30개 + i18n.js + sw.js |
| 프레임워크 | Vanilla JS (단일 번들 + 임베디드 UI) |
13.2 페이지 구조
섹션 제목: “13.2 페이지 구조”로그인 화면(#login-page)은 좌측에 무인증 GET /health 기반 실데이터 노드 상태 패널(상태
dot·NODE/VERSION/UPTIME·KVM/DISK 점검 2종), 우측에 인증 카드를 배치한 2열 구성이다.
좌측
패널은 부트 1회 + 30초 간격으로 갱신되고(_loginHealthTick), 화면이 960px 이하로 좁아지면
1열로 접힌다.
활성 테마(13.3)를 로그인 화면부터 그대로 따른다 — 로그인 전용 배색은 없다.
운영 콘솔은 고정 사이드바 트리(왼쪽 236px), topbar(브레드크럼·통합 검색·세션 상태), 메인 콘텐츠, 글로벌 statusbar(6세그먼트)로 구성된다.
사이드바는 항상 펼쳐진 트리이며(구셸의 3탭 전환식 사이드바 폐지), Ctrl+B로 접고 펼 수 있다.
┌──────┬───────────────────────────────────────────────────────┐│ │ topbar: 브레드크럼 · 통합 검색(Ctrl+K) · 세션 상태 ││ 사이드│───────────────────────────────────────────────────────┤│ 바 트리│ 메인 콘텐츠 ││(236px)│ 대시보드 · VM · 컨테이너 · 인프라 · 관제 · 시스템 · 도움말 ││ │───────────────────────────────────────────────────────┤│ │ statusbar: VM · 컨테이너 · Critical · 보안 · 스토리지 · 자가치유 │└──────┴───────────────────────────────────────────────────────┘사이드바 섹션별 내용:
| 섹션 | 항목 |
|---|---|
| (무제) | 운영 대시보드 |
| 워크로드 | 가상 머신(요약·콘솔·스냅샷·성능·타임라인), 컨테이너, 템플릿 |
| 인프라 | 네트워크, OVN SDN, 오버레이 네트워크, 보안 그룹, 스토리지, 백업, iSCSI 타깃, DPDK, SR-IOV, 커넥션 풀, GPU 장치, 토폴로지, 클라우드 마이그레이션, 호스트 상태 |
| 관제 | 운영 개요, 이벤트 센터, 알림, 보안 이벤트, 감사 로그, 활동 로그, VM/스토리지/호스트 모니터, 히트맵, API 성능, 자가치유 |
| 시스템 | 계정과 권한(ADMIN), API 관리(ADMIN), 설정 관리 |
| 도움말 | 도움말, Swagger API |
운영 대시보드는 호스트 상태·워크로드·최근 경고를 한 화면에 모은 진입 화면이다.
위에서
아래로 네 블록으로 고정되며, 어떤 블록도 사용자가 숨기거나 순서를 바꾸지 않는다 — 구버전의
표시 항목 on/off 위젯 토글(localStorage 영속)·빠른 작업 그리드·화면 안 상태바·Chart.js 2차트는
전역 statusbar와 아래 표로 흡수돼 폐지됐다.
| 블록 | 내용 |
|---|---|
| 게이지 4타일 | 호스트 CPU · 메모리 · 최악 ZFS 풀 · 호스트 디스크. 각 타일은 현재값·선택 창의 peak·warn/crit 눈금을 함께 표시한다 |
| 워크로드 통합 표 | VM과 LXC 컨테이너를 한 표에서 본다. 상태(실행·중지·오류)·유형(VM·LXC) 2축 필터 칩, 상태 dot/pill, 인라인 CPU/MEM 게이지, 선택 창 CPU 스파크라인, 행 hover 시 나타나는 퀵액션(시작·중지·콘솔) — 행을 클릭하면 해당 VM 요약 또는 컨테이너 화면으로 이동한다 |
| 알림 · 보안 이벤트 피드 | 알림과 Suricata 보안 이벤트를 최신순으로 합쳐 상위 8건. 행을 클릭하면 알림 또는 보안 이벤트 화면으로 이동한다 |
| 자가치유 승인 대기 | 승인 대기 중인 자가치유 액션 상위 3건을 정책·사유·경과 시간과 함께 표시하고, 카드에서 바로 승인·거부한다(ADMIN 전용) |
우측 상단 타임세그(Live · 15m · 1h)가 게이지의 peak 계산 창과 워크로드 표 스파크라인의
표시 구간을 함께 바꾼다.
창 전환·필터 칩 변경·폴링 갱신은 해당 블록 내부만 다시 그리므로
화면 전체가 스켈레톤으로 되돌아가거나 스크롤이 초기화되지 않는다.
필터 칩 상태는 URL 쿼리
(?status=running&type=vm)에 반영돼 링크 공유·새로고침 후에도 복원된다.
13.3 테마 시스템
섹션 제목: “13.3 테마 시스템”4-테마 시스템 — 2026-04-11 Supanova Taste Layer 도입 후 Supanova 변형만
남기고, 2026-04-26 접근성 후속으로 고대비 변형을 추가했다.
2026-05-08에는
대시보드 선택지를 단순화하기 위해 emerald와 light 변형을 제거했으며, R4 데스크톱
리뉴얼에서 승인된 supanova-mockup 운영 콘솔 팔레트를 네 번째 변형으로 고정했다.
ADR-0016에 따라 localStorage에 영속 저장하고, 새로고침 없이 즉시 전환한다.
| 테마 id | accent | 설명 |
|---|---|---|
supanova (기본) | Teal-500 #14b8a6 | 차분한 청록, 운영 콘솔 중성 톤 |
supanova-cyan | Cyan-600 #0891b2 | 기존 brand cyan 연속감, 채도 경계 |
supanova-hicontrast | Yellow #facc15 | 고대비 접근성 변형 |
supanova-mockup | Cyan #00e0ff | 데스크톱 리뉴얼 목업 팔레트(--bg #0a0e14) |
테마 id allowlist는 index.html의 프리부트 스크립트에도 같이 박혀 있어, 선택한 테마는
새로고침 직후 첫 페인트부터(로그인 화면 포함) 그대로 적용된다.
테마 편집기로 만든
커스텀 테마가 활성인 동안 선택기에는 CUSTOM 항목이 표시되지만 선택은 불가하다 —
고를 수 있게 두면 커스텀 값이 기본 테마로 강등되기 때문이다.
공통 스택: self-hosted Pretendard + local Outfit fallback, Double-Bezel 카드, spring motion
(cubic-bezier(0.16,1,0.3,1)), @supports not (color-mix) 폴백.
Supanova 금칙(neon glow, clip-path, cyan→magenta 그라디언트) 전량 제거.
레거시/삭제 테마 id(pure-light, midnight-blue, supanova-emerald,
supanova-light 등)는 inline head script가
자동으로 supanova로 마이그레이션.
테마 선택기는 상단 바가 아니라 상단 우측 톱니(⚙) 버튼 → 설정(Preferences) 모달 안에 있다
(2026-07-28 셸 정리로 이동 — 상단 바는 크럼·검색·상태 표시만 남겼다).
같은 모달에서 언어도
바꾼다.
13.4 DESIGN.md 시각 규격
섹션 제목: “13.4 DESIGN.md 시각 규격”UI 시각 규격은 상위 제품/운영 가이드인 docs/GUIDE.md와 분리해
루트 DESIGN.md에서 관리한다.
DESIGN.md는 색상 token,
typography, component state, dashboard density, table/card/button/modal 규칙을
정의하고, ui/samples/design-system-preview.html은
같은 ui/style.css 위에서 그 규칙을 확인하는 preview HTML이다.
UI visual 변경, ui/samples/ 변경, ui/docs.html/ui/guide.html/ui/guide-content.md의 시각
규격 연결을 바꾸면 다음 검증을 Level 1에 포함한다.
python3 scripts/check_design_md.pybash tests/integration/test_design_md_surface.shPCV_NO_DEPLOY=1 scripts/bundle-ui.shnode --check ui/app.bundle.jsgit diff --checkDESIGN.md의 Reference Pattern Borrowing 규칙은 외부 제품의 브랜드를 복제하지 않고 운영 콘솔에 필요한 패턴만 차용하는 기준이다.
현재 샘플은 ui/samples/design-borrowing-mockup.html이며, 실제 적용 화면은 운영 > 이벤트 센터의 ops-triage 라우트다.
13.5 i18n 국제화
섹션 제목: “13.5 i18n 국제화”i18n.js에서 ko/en 2개 언어, 280+키를 관리한다.
| 항목 | 값 |
|---|---|
| 지원 언어 | 한국어 (ko), English (en) |
| 키 수 | 280+ |
| HTML data-i18n | 86개 요소 |
| 전환 함수 | t(key) / applyI18n() |
| 즉시 전환 | 새로고침 불필요 |
| 전환 위치 | 상단 우측 톱니(⚙) → 설정(Preferences) 모달의 Language |
13.6 Service Worker 오프라인 캐싱
섹션 제목: “13.6 Service Worker 오프라인 캐싱”sw.js는 정적 파일을 Network-First 전략으로 갱신하고 실패 시 캐시로 폴백한다.
API/WebSocket 요청에는 개입하지 않으며, 네트워크 단절 시에도 마지막으로 캐시된 UI 접근을 지원한다.
13.7 WebSocket 실시간 이벤트
섹션 제목: “13.7 WebSocket 실시간 이벤트”ws://127.0.0.1:8080/api/v1/ws/events— 메트릭 push (10초)ws://127.0.0.1:8080/api/v1/ws/vnc— noVNC WebSocket 프록시
유휴 타임아웃: 300초 미활동 시 자동 종료.
최대 동시 연결: 1,000.
13.8 커맨드 팔레트 (통합 검색)
섹션 제목: “13.8 커맨드 팔레트 (통합 검색)”Ctrl+K로 커맨드 팔레트를 열어 빠른 네비게이션 및 액션 실행.
팔레트가 곧 통합 검색이다 —
질의를 입력하면 결과가 명령 · 가상 머신 · 컨테이너 · 페이지 그룹으로 나뉘어 나오고,
컨테이너는 이름뿐 아니라 IP로도 찾을 수 있다.
VM 항목을 고르면 해당 VM을 선택한 채
VM 화면으로, 컨테이너 항목을 고르면 그 컨테이너를 선택한 채 컨테이너 화면으로 이동한다.
빈 질의일 때는 예전처럼 명령 목록만 보여준다.
별도의 전역 검색 오버레이는 없다.
Ctrl+Shift+F, /, topbar의 검색 상자는 모두 같은
팔레트를 연다(이미 열려 있으면 새 창을 겹치지 않고 입력란으로 포커스만 되돌린다).
기타 키보드 단축키(? 오버레이가 보여주는 목록과 동일):
Ctrl+N— 새 VM ·Ctrl+D— VM 설정 ·Ctrl+P— 환경설정Ctrl+B— 사이드바 접기/펼치기 ·F11— 전체 화면?— 키보드 도움말 ·Esc— 대화상자 닫기
수식키 없는 단일 키도 등록돼 있다(입력 필드에 포커스가 있으면 무시된다).
/ 통합 검색 · n 새 VM · g 대시보드 · m 운영 개요.
VM 목록 화면에서는 j/k(또는 ↑/↓)로 행을 옮기고 Enter로 요약 화면에 들어간다.
13.9 반응형 디자인
섹션 제목: “13.9 반응형 디자인”| 브레이크포인트 | 대상 | 적용 |
|---|---|---|
| <= 1024px | iPad | 사이드바 축소, 그리드 조정 |
| <= 768px | 모바일 | 햄버거 메뉴, 단일 컬럼 |
| <= 480px | 소형 기기 | 최소 레이아웃, 터치 스와이프 |
13.10 접근성
섹션 제목: “13.10 접근성”- ARIA 레이블 전체 적용
- 포커스 트랩 (모달 내 Tab 순환)
- 키보드 네비게이션 활성화
- WCAG 2.1 AA 수준 대비율
13.11 모듈 구조
섹션 제목: “13.11 모듈 구조”ui/├── index.html├── login.html├── style.css├── app.js # 메인 엔트리포인트├── app.bundle.js # modules/*.js 단일 번들├── sw.js # Service Worker├── i18n.js # 국제화└── modules/ ├── endpoints.js # EP 레지스트리 (하드코딩 금지) ├── api.js # unwrapData/unwrapList, fetch 래퍼 ├── ui.js # customConfirm, 토스트, 기본 UI helper ├── uxlib.js # escape, sanitizer, UI 유틸리티 ├── modal.js # 모달 helper ├── charts.js # 차트 렌더링 ├── vm.js # VM CRUD, 스냅샷, 디스크 ├── container.js # 컨테이너 관리 ├── network.js # 네트워크 관리 ├── storage.js # 스토리지 관리 ├── monitor.js # 모니터링 대시보드, 운영 이벤트 센터 ├── security.js # 보안 이벤트 UI ├── cloud.js # Cloud Migration ├── help.js # 도움말, Swagger API ├── nav.js # 네비게이션, 라우팅, 이벤트 센터 route ├── theme.js # 테마 관리 ├── accounts.js # 계정/RBAC UI ├── advanced.js # 고급 운영 UI └── selfhealing.js # Self-healing UIcustomConfirm()은 확인 메시지를 escape한 뒤 줄바꿈만 안전하게 렌더링한다.
확인창에 강조가 필요해도 호출부에서 <br>, <b> 같은 HTML 조각을 넘기지 않는다.
13.12 보안 헤더와 로컬 정적 자산
섹션 제목: “13.12 보안 헤더와 로컬 정적 자산”운영 Web UI는 강한 CSP를 기본으로 둔다.
원칙은 외부 런타임 호출을 허용하지 않고, 브라우저가 필요한 자산을 같은 origin의 /ui/ 아래에서 받도록 고정하는 것이다.
| 항목 | 기준 |
|---|---|
| 진입점 | /ui와 /ui/ 모두 자산 기준 경로가 /ui/가 되도록 index.html에 <base href="/ui/"> 유지 |
| 아이콘 | 외부 런타임 API를 사용하지 않고 inline SVG symbol 또는 ui/vendor/coolicons/coolicons.svg 같은 로컬 파일 사용 |
| Chart.js | ui/vendor/chart.umd.min.js로 self-host, sourceMappingURL 제거 |
| noVNC | ui/vendor/novnc/novnc.esm.js로 self-host, 외부 ESM import 금지 |
| 폰트 | ui/vendor/pretendard/pretendard.css와 woff2 파일로 self-host |
| PWA manifest | icon-192.png, icon-512.png를 반드시 배포 |
| PNG MIME | Content-Type: image/png + X-Content-Type-Options: nosniff |
| Service Worker | API/WebSocket은 개입하지 않고 /ui/ 정적 자산만 캐시 |
| WebSocket | URL에 토큰을 넣지 않고 연결 후 첫 메시지로 인증 |
| Metrics | UI fetch는 Authorization: Bearer ... 헤더를 붙여 호출 |
권장 CSP/Permissions-Policy 예시는 다음과 같다.
add_header Permissions-Policy "accelerometer=(), autoplay=(), camera=(), display-capture=(), encrypted-media=(), fullscreen=(self), geolocation=(), gyroscope=(), microphone=(), midi=(), payment=(), usb=()" always;add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; font-src 'self'; connect-src 'self' wss://purecvisor.example.com https://purecvisor.example.com; img-src 'self' data: blob:; frame-src 'none'; object-src 'none'; base-uri 'self'; form-action 'self'" always;호환 엔드포인트 purecvisor-compat.example.com를 별도 server block으로 운영하면 connect-src의 host도 해당 도메인으로 맞춘다.
정적 파일 배포 검증은 두 도메인의 docs.html, app.bundle.js, guide-content.md, sw.js 해시가 같은지 확인한 뒤 완료 처리한다.
nginx가 앞단 reverse proxy로 동작하고 nosniff를 켠 경우, PWA 아이콘은 데몬 프록시보다 nginx 정적 location으로 직접 내려주는 편이 안전하다.
location ~ ^/ui/.+\.(png|ico|svg|webp|woff|woff2)$ { root /usr/local/share/purecvisor; types { image/png png; image/x-icon ico; image/svg+xml svg; image/webp webp; font/woff woff; font/woff2 woff2; } try_files $uri =404;}데몬 직접 서빙 경로도 .png, .ico, .svg, .webp MIME을 제공해야 한다.
이 경로는 nginx 없는 로컬 설치나 장애 분석 시 동일한 manifest 동작을 보장하기 위한 폴백이다.
noVNC는 반드시 /ui/vendor/novnc/novnc.esm.js에서 로드한다.
app.bundle.js 안에 https://cdn.jsdelivr.net/npm/@novnc/novnc 같은 외부 ESM import가 남아 있으면 운영 CSP에서 차단된다.
정적 파일 교체 후에는 /ui/vendor/novnc/novnc.esm.js가 200 application/javascript로 응답하고, 공개 app.bundle.js에서 외부 CDN 문자열이 검색되지 않는지 확인한다.
해시 라우팅은 #/page를 표준으로 사용한다.
공개 안내나 외부 링크가 #page 형식으로 들어와도 ui/modules/uxlib.js의 parser가 같은 page로 정규화해야 한다.
예: /ui#ops-triage와 /ui#/ops-triage는 모두 운영 이벤트 센터를 렌더링해야 한다.
13.13 VM 메모리·I/O 화면 시정과 확인 범위
섹션 제목: “13.13 VM 메모리·I/O 화면 시정과 확인 범위”2026-09-07 개발선의 지정 시정은 다음 계약을 다룹니다.
목록의 순서와 현재 열린 창이 바뀌어도 작업 대상과 결과 표시가 일치하도록 보완했습니다.
- I/O 조회·적용은 창을 열 때 정한 VM 이름/UUID를 확인합니다.
목록 재정렬, 대상 삭제와 동명 VM 교체를 구분합니다. - 메모리 통계·I/O의 늦은 응답은 요청한 창과 최신 요청에만 표시합니다.
이미 닫힌 창의 응답이 새 창을 바꾸지 않습니다. - RPC 오류는 정상 빈 결과와 구분합니다.
자동 닫기 timer도 원래 창에 속합니다. - 검색 팔레트가 겹쳐 열려 있어도 아래에서 열린 원래 창은 완료 상태를 받습니다.
개발선 UI 전체 60파일 505 PASS, 최종 독립 대조 44 PASS와 두 운영 노드의 정적 자산·기본 페이지·health 확인을 완료했습니다.
이 수치를 공개 소스의 시험 집계에 합산하거나 모든 실제 VM 변경 효과의 인증으로 사용하지 않습니다.
공개 소스의 시정 포함 여부는 해당 릴리스 diff와 회귀 결과로 별도 확인해야 합니다.
공개 스냅샷의 모니터링 차트에도 누적 RX/TX를 속도 단위로 표시하는 경로와 표본 간격을 5초로 고정하는 경로가 남아 있습니다.
해당 차트의 시간·속도 값만으로 운영 임계값을 판단하지 말고 원본 counter의 증가량과 실제 수집 간격을 함께 확인하세요.
관련 시정·독립 재검토는 진행 중입니다.