20 KiB
운영자 UI 동등성 인벤토리
기준일: 2026-07-15
제품과 판정 기준
현재 이관 앱은 웹사이트가 아니라 .NET 8 / C# / WinUI 3 / WebView2 Windows 데스크톱
프로그램인 MBN_STOCK_WEBVIEW.LegacyParityApp이다. WebView2는 패키지 안의 로컬
HTML/CSS/JavaScript를 표시한다.
Web은 원본 UI를 표현하고 사용자 intent를 전달한다. 원본 이벤트의 상태 전이, DB identity,
playlist, PageN과 송출 상태는 LegacyOperatorController의 C# snapshot이 권위다. Web 메시지는
LegacyBridgeProtocol의 strict JSON allowlist에 없는 필드·값·명령을 거부한다.
이 문서의 판정은 다음을 구분한다.
| 판정 | 의미 |
|---|---|
| UI/이벤트 이관 | 원본 컨트롤과 클릭 이벤트가 실제 LegacyParityApp 화면 → strict bridge → C# workflow로 연결됨 |
| 자동 동등성 | 원본 inventory·DTO·mutation·호출 순서·경계값을 자동 테스트로 비교함 |
| 개발 DB 통합 | 실제 개발 Oracle/MariaDB에서 create/readback/delete/absence를 확인함 |
| 개발 PGM 통합 | 개발 Tornado2 Network Monitoring과 PGM 화면을 함께 확인함 |
| 외부자산 필요 | 코드 경로는 있으나 Git/MSIX 밖의 실제 영상·배경이 제공되지 않음 |
원본 보존
- 원본
C:\Users\MD\source\repos\MBN_STOCK_N은 읽기 전용으로만 분석했다. - 원본
Cuts도 읽기 전용 검증에만 사용하며 수정·복사하지 않는다. - 비밀번호, 라이선스, 벤더 DLL, 인증서, 실제 컷·영상·배경과 로컬 설정은 Git에 넣지 않는다.
원본 Designer 이벤트 전수 감사
원본 *.Designer.cs가 실제로 구독하는 이벤트를 전수 조사한 결과는 186개 Designer
구독, 화면별 중복을 합친 171개 고유 핸들러다. 선언만 있고 Designer에서 구독하지 않는
메서드는 수에 넣지 않았다. 171개는 IMPL 131, DEAD 20, VISUAL 10, DRAG 9,
SAFETY 1로 모두 분류했다. 같은 이름의 핸들러라도 화면이 다르면 별도 바인딩으로 센다.
상세 원본 파일·구독 줄·분류 근거는
LEGACY_DESIGNER_EVENT_PARITY_AUDIT.md에 있다.
원본 실행 action inventory
동적 테마·전문가 DB 행과 전용 편집기의 데이터 행은 아래 고정 수량에 중복해 더하지 않는다.
| 집합 | 원본 UI | 수량 | 현재 경계 | 상태 |
|---|---|---|---|---|
| fixed action | MainForm 해외·환율·지수와 UC1 | 328 | LegacyFixedActionCatalog + strict action/section intent |
이관 |
| 업종 action | UC1 + UC2 | 44 | LegacyIndustryActionCatalog + C# section batch |
이관 |
| 종목 컷 | MainForm 좌측 검색·컷 | 31 | LegacyStockCutCatalog |
이관 |
| 비교 action | UC1 + UC3 | 9 | 24개 target + LegacyComparisonWorkflowController |
이관 |
| 업무 탭 | MainForm TabControl | 10 | identity 기반 활성화·순서 변경 | 이관 |
328개 fixed action 중 수동 전제 6개는 임의 DTO로 만들지 않는다. 개인·외국인·기관
순매도 3개는 FSell의 신뢰된 5행 입력으로, 5단·6종목·12종목 트리의 VI placeholder 3개는
원본 VIList가 만든 s5074/5074 5개 단위 PageN으로만 활성화한다.
화면 단위 현황
| 원본 화면 | 이관된 핵심 동작 | 현재 구현 | 개발 DB/파일 검증 | 개발 PGM/자산 경계 |
|---|---|---|---|---|
| MainForm | 종목 검색, 31컷, playlist, 배경, 송출, 단축키, 종료 확인 | LegacyParityApp shell + C# controller | 실제 DB 검색/read smoke PASS | 송출 최종 결과는 검증 문서에 기록 |
| UC1 | 328 fixed, 업종·비교 트리, 부모/leaf batch | C# 폐쇄 inventory와 원자적 section materialize | read-only selector 검증 | 장면별 PGM은 별도 |
| UC2 | 업종 검색, 두 슬롯 회전·교환, 44 action | industry workflow/controller | Oracle read PASS | 장면별 PGM은 별도 |
| UC3 | 비교 대상·검색·쌍 CRUD·9 action | comparison workflow/controller | 원본 8쌍 1회 import·재시작 영속성 검증 | 장면별 PGM은 별도 |
| UC4 | KRX/NXT 테마 검색·preview·5/6/12 선택 | theme workflow; 목록 행 단일 클릭 | KRX/NXT selector PASS | 외부 scene 자산과 PGM 별도 |
| UC5 | 해외업종·해외종목·해외지수 | overseas workflow/controller | Oracle selector PASS | s6001 영상 13개 외부자산 미제공 |
| UC6 | 전문가 검색·추천 preview·5행 선택 | expert workflow; 목록 행 단일 클릭 | selector PASS | 장면별 PGM은 별도 |
| UC7 | 거래정지 검색·현재/120일 캔들 | trading-halt workflow; 원본 단일 클릭 | Oracle selector PASS | 장면별 PGM은 별도 |
| GraphE | 4개 재무 CRUD, 정렬·찾기, 행 더블클릭 playlist 추가 | LegacyManualFinancialWorkflow |
개발 DB 4개 테이블 write PASS | 전용 scene PGM 별도 |
| FSell | 개인·외국인·기관 각 5행, 확인 시 저장+추가+닫기 | LegacyManualListsWorkflow + trusted store |
원본 형식 read와 격리 write 검증 | s5025 trusted 파일 전제 |
| VIList | 종목 추가·삭제·이동, 5개 단위 page, 확인 시 저장+추가+닫기 | LegacyManualListsWorkflow + trusted store |
원본 형식 read와 격리 write 검증 | s5074 전용 입력 PGM 별도 |
| PList | 목록, 행 더블클릭 load/save, 확인, 삭제 | LegacyNamedPlaylistWorkflow |
개발 DB create/save/read/delete/absence PASS | 복원 항목별 scene PGM 별도 |
| AList | 다음 코드와 프로그램명 신규 생성 | named playlist workflow에 통합 | 위 named playlist 시나리오로 PASS | 해당 없음 |
| ThemeA | KRX/NXT 테마 생성·편집·삭제, 이름 중복확인, 종목 구성·순서 | LegacyOperatorCatalogWorkflow + exact-title 조회 |
개발 DB KRX/NXT 각 1개 PASS | scene PGM 별도 |
| EList | 전문가 생성·편집·삭제, 추천 종목·금액·드래그 순서 | LegacyOperatorCatalogWorkflow + opaque row reorder |
개발 DB expert 1개 PASS | scene PGM 별도 |
UC4/UC6의 결과 행은 고정 action 트리의 click handler에 버블링하지 않는다. UC4 테마, UC6 전문가와 UC7 거래정지는 원본처럼 단일 클릭으로 선택/playlist 동작을 수행하고, fixed/industry/comparison 트리의 원본 double-click과 독립적으로 처리한다.
MainForm 이벤트 매핑
| 원본 이벤트 | 원본 의미 | LegacyParityApp 동작 |
|---|---|---|
Form1_Load |
자산·DB·탭·Tornado 초기화 | WebView local UI와 C# composition root 초기화; 기본 DryRun |
tabControl1_SelectedIndexChanged |
탭별 최초 데이터 조회 | 10개 identity별 C# activation gate; 최초 진입 1회 load, 재진입 snapshot 유지 |
| 탭 drag 이벤트 | 10개 탭 표시 순서 교환 | dragover 대상 진입 시 identity와 예상 위치를 C#이 검증해 교환; 중복 전달은 no-op, active tab·playlist 불변 |
| 검색 버튼/Enter | 종목 검색 | 원본 KOSPI→KOSDAQ→NXT 순서와 마지막 이름 일치 규칙 유지 |
| 컷 click/double-click/drag | 원본 31컷 선택·추가 | plain/Ctrl/Shift/Ctrl+Shift와 원본 double-click 판정, batch 추가 |
| playlist click/key/buttons/drag | 선택, enabled, 이동, 삭제, 다중 선택 행 이동 | 선택된 행은 원래 순서를 보존한 한 블록으로 임의 위치에 이동; C# playlist authority와 PREPARED/PROGRAM/pending/unknown mutation 차단 유지 |
| F2/F3 | 배경 선택·없음 | trusted sibling background root만 허용; 누락·reparse·escape fail closed |
| F8 | TAKE IN | idle이면 내부 PREPARE → TAKE IN을 한 번에 실행 |
| NEXT | Page NEXT 또는 다음 행 | 같은 scene PageN 여부를 C#이 판정 |
| Escape | TAKE OUT | modal이 없을 때만 StopAll 기반 TAKE OUT intent |
| FormClosing | 종료 확인 | 기본 취소 확인창; 자동 TAKE OUT/반대 명령 없음 |
FarPoint 대체와 클릭 규칙
FarPoint UI는 Web table/list로 표현한다. 다음 의미는 원본과 같이 유지한다.
- plain/Ctrl/Shift/Ctrl+Shift 선택과 현재 행
- column header 정렬과 현재 표시 순서
- 위/아래 이동, 선택/전체 삭제와 enabled 토글
- playlist의 비연속 다중 선택을 한 블록으로 유지하는 행 drag와 임의 before/after drop
- UC6/EList 추천 행의 opaque
rowId기반 drag 재정렬 - single-click과 double-click을 컨트롤별로 구분
- 부모 section 전체 추가는 모든 자식을 먼저 materialize한 뒤 한 번에 commit
- 수동/지원 불가 child가 있으면 부분 추가 없이 전체 차단
- modal이 열리면 F2/F3/F8/Escape, Delete/Space/Home/End 등 전역 동작 격리
- stale generation, 예상하지 않은 row id, 추가 payload field를 strict bridge에서 거부
GraphE 행 double-click은 fresh DB record를 다시 확인한 뒤 한 행을 추가하고 닫는다. Theme 결과 double-click은 원본의 5행 현재가 경로를 사용한다. PList 행 double-click은 현재 save/load 모드에 맞춰 동작하며, 이름만으로 DB identity를 추정하지 않는다.
drag 이관 경계
| 원본 동작 | Web 입력 | C# 권위와 중복 안전 |
|---|---|---|
PlayList_DragDrop/DragOver |
내부 custom MIME와 opaque source/target rowId, before/after |
source가 선택 행이면 현재 선택 전체를 표시 순서대로 이동한다. 체크박스에서 시작한 drag는 거부하고 PREPARED/PROGRAM/pending/unknown에서는 변경하지 않는다. |
tab_DragOver/MouseDown/Move/Up |
drop을 기다리지 않고 dragover로 hover-swap-tabs 전송 |
C# 탭 identity와 expectedSourceIndex/expectedTargetIndex가 현재 순서와 일치할 때만 교환하므로 같은 메시지의 중복 전달이 순서를 되돌리지 않는다. |
UC6 FpSpread2.AllowDragDrop |
ThemeA/EList 편집행의 내부 custom MIME와 opaque rowId |
워크플로가 임의 before/after 위치를 계산한다. 존재하지 않는 ID, 같은 행, 추가 payload 필드는 strict bridge에서 거부하거나 no-op 처리한다. |
실제 마우스 검증에서는 playlist 행 drop이 조상 drop zone으로 버블링해 현재 선택된 컷을
한 번 더 추가하는 문제가 드러났다. 조상은 이제 exact legacy-selected-cuts text/plain
token만 처리하고 playlist row custom MIME와 외부 text는 무시한다. 따라서 한 row drop에서
reorder와 cut append가 함께 발생하지 않는다.
체크박스 origin도 브라우저 동작을 반영해 보강했다. Chromium의 dragstart.target은 실제
pointer가 체크박스에서 시작했어도 draggable 조상 행일 수 있다. pointerdown 때 checkbox
origin row를 기억해 이어지는 dragstart를 거부하고 pointerup/pointercancel/dragend에서 지운다.
회귀 테스트는 이 target 형태, 다중선택 보존, row MIME의 조상 무시, 외부 text 거부와 exact
selected-cut token의 1회 처리를 DOM event 순서로 재현한다.
ThemeA 이름 중복확인
원본 ThemeA의 중복확인 버튼을 operator-catalog-name-check로 이관했다. Web은 이름만
check-operator-catalog-theme-name intent로 전송하고 KRX/NXT, 코드와 테이블 identity는
C# workflow가 소유한다. 선택된 provider의 SB_LIST에 exact title parameterized
COUNT(*) 조회를 한 번 수행하며 DB mutation은 실행하지 않는다. 중복, 사용 가능,
결과 불명 상태를 구분하고, 조회 결과를 확정할 수 없으면 쓰기 없이 fail closed한다.
원본처럼 편집 중 자기 행을 제외하지 않으므로 이름을 바꾸지 않은 행은 중복으로 판정된다.
원클릭 TAKE IN과 숨김 PREPARE
원본 운영 화면의 핵심 동작은 별도 PREPARE를 먼저 누르는 것이 아니라 F8/TAKE IN 한 번으로 송출하는 것이다. 현재 보이는 TAKE IN은 idle에서도 활성 조건을 만족하면 다음을 실행한다.
- playlist selection과 page를 C# snapshot으로 고정
- DB/신뢰 파일 DTO와 PageN을 fresh load
- 장면 mutation과 transaction 순서를 적용해
Prepare정확히 1회 - 성공한 prepared scene에
TakeIn정확히 1회
엔지니어링용 native PREPARE control과 explicit workflow는 테스트를 위해 유지하지만 Web UI에서
hidden이다. TAKE IN 실패·timeout·OutcomeUnknown이면 자동 재시도하지 않으며 성공한
prepared ownership을 추측해 지우지 않는다.
Visual Studio Development Live 시작 경계
공유 MBN_STOCK_WEBVIEW.slnLaunch와 패키지 시작 프로필은
MBN_STOCK_WEBVIEW.LegacyParityApp을 명시적인 시작 프로젝트로 사용한다. 저장소의
기본값은 계속 DryRun이고 이 Development Live 부트스트랩은 Release에서 적용되지 않는다.
Development Live는 정확한 --development-live
인자, Debug 빌드, %LOCALAPPDATA% 아래 Git 제외 승인 파일의 세 조건을 모두 만족한
프로세스에서만 구성된다.
승인 파일은 정확한 다섯 속성, 최대 4096바이트, canonical JSON, exact authorization
문자열과 독립 검수된 native/Interop SHA-256 두 개를 요구한다. 알 수 없는 속성, 중복 속성,
reparse 경로, I/O 오류 또는 일부 환경 설정 실패는 모두 fail closed한다. Live authorization은
process scope에서 마지막에 설정되고 MainWindow/engine 생성 시 한 번 캡처된다. 이 경로는
기존 trustedLiveOutputEnabled, scene allowlist, asset 검사와 명령 안전 게이트를 완화하지
않는다. 상세 절차는
DEVELOPMENT_LIVE_VISUAL_STUDIO.md에 있다.
2026-07-15 실제 UI·package·연결 검증
설치한 signed Release x64 MSIX는 15,213,188 bytes, SHA-256
357E17426C45FF516592EE0177D0F95502A98AF084759348ABC191BA5ED21FD5다. 설치와
package-context 실행을 확인했다. Release에 --development-live를 전달한 회차는 Debug-only
gate 때문에 DryRun으로 남았고 Tornado TCP 연결과 Network Monitoring 변경이 없었다.
실제 WebView 마우스/CDP 입력의 확인 범위는 다음과 같다.
삼성검색 결과 63개,삼성출판사선택, 선택한 컷 3개를 한 번의 drop으로 playlist 3행에 추가- playlist multi-row block reorder와 enabled 체크박스 origin drag 차단
- mouse release 전에 dragover로 시장 탭 순서 교환
- EList 추천행 5개 drag reorder. 수량 입력칸에서 시작한 실제 마우스 drag는 순서를 바꾸지 않고, 행의 비입력 영역에서 시작한 drag만 순서를 바꾸는 것을 확인
- ThemeA 결과 500건과 exact-title
중복확인read 성공
별도 승인된 Development Live 회차의 장비 로컬 시간(KST) 14:28:28에 HELLO request와
success를 각 1회, 14:43:10에 BYE 1회를 확인했다. scene, PREPARE, TAKE 계열, DB write는
모두 0건이다. 최종 Network Monitoring 원문은 300,007자, SHA-256
45858322F60C135858002EB6243D0474F51F1965E23D2332D397C3A3B5DB8049다. 이 회차는
Live 연결/해제 경로만 검증하며 scene/PGM 방송 송출 성공을 뜻하지 않는다.
PageN과 NEXT
| scene | page size | 규칙 |
|---|---|---|
s5074 |
5 | 테마/전문가/VI 등 5행 경로 |
s5077 |
6 | 6종목 테마 경로 |
s5088 |
12 | 12종목 테마 경로 |
pageCount = min(20, ceil(itemCount / pageSize))를 사용한다. 0행, 정확한 경계,
마지막 부분 페이지, 20페이지 cap, 21페이지 요청 거부, 같은 scene의 GetPlayingScene
Page NEXT와 다음 enabled playlist 항목 NEXT를 자동 테스트로 비교한다.
개발 DB write 증적
실제 개발 Oracle/MariaDB에서 production executor를 사용한 9개 충돌 격리 시나리오가 create/save → fresh readback → delete → absence를 통과했다.
| 기능 | 시나리오 |
|---|---|
GraphE INPUT_PIE/GROW/SELL/PROFIT |
4 |
| PList/AList named playlist | 2 |
| ThemeA KRX/NXT | 2 |
| EList expert | 1 |
| 합계 | 9 |
이는 개발 DB 통합 증거이며 운영 DB write를 수행했다는 뜻이 아니다. write 재시도는 없고, 결과가 불명확하면 cleanup과 다음 시나리오도 실행하지 않는 fail-closed 규칙을 유지한다.
read-only·scene·asset 증적
- 실제 Oracle/MariaDB 필수 scene selector 33/33, 전체 query 58건 PASS
- 원본 scene builder 자동 기준선 35/35
- MainForm 도달 runtime 34개 builder와 active alias 45개
- 승인된 Cuts에 45 alias 대응
.t2s존재 s5006Video\큐브배경.vrv1개와s6001국가 영상 13개는 외부자산 미제공- sibling
배경\기본.vrv도 외부자산 미제공
마지막 14개 영상과 기본.vrv의 부재는 UI/scene 코드 실패가 아니다. 해당 기능은 자산이
제공될 때까지 COM 전에 차단하며 임의 파일로 대체하지 않는다.
구현·테스트 빠른 링크
| 영역 | 현재 실행 구현 | 주요 테스트/근거 |
|---|---|---|
| WinUI 3 + WebView2 shell | MainWindow.xaml.cs, Web app, Web layout | thin client |
| C# 업무 권위 | LegacyOperatorController.cs | LegacyApplication tests |
| strict bridge | LegacyBridgeProtocol.cs | LegacyBridge tests |
| 송출 | MainWindow.Playout.cs, playout-ui.js, LegacyPlayoutWorkflow.cs | LegacyPlayoutWorkflowTests.cs |
| 수동 목록 | MainWindow.ManualLists.cs, LegacyManualListsWorkflow.cs | manual-list tests |
| GraphE | LegacyManualFinancialWorkflow.cs | manual-financial tests |
| PList/AList | LegacyNamedPlaylistWorkflow.cs | named-playlist tests |
| ThemeA/EList | LegacyOperatorCatalogWorkflow.cs | catalog tests |
| Designer 이벤트 감사 | LEGACY_DESIGNER_EVENT_PARITY_AUDIT.md | 186 subscriptions / 171 unique handlers |
| VS Development Live | DEVELOPMENT_LIVE_VISUAL_STUDIO.md | strict local authorization와 Debug-only bootstrap |
| scene matrix | SCENE_EQUIVALENCE.md | 35/35 builder, 45 alias, PageN matrix |
| 2026-07-15 증적 | LEGACY_PARITY_VALIDATION_20260715.md | signed Release, 실제 UI와 제한된 Live 연결 범위 |
현재 signed Release MSIX의 hash·크기·설치·DryRun 실행과 위 실제 마우스/CDP UI 범위는 확인했다. 최종 회귀는 .NET Debug/Release x64에서 각각 2,446/2,446, Web Node에서 438/438, Visual Studio 2026 Debug/Release x64 빌드에서 경고 0·오류 0이다. 다만 HELLO/BYE만 수행한 Live 회차를 scene PREPARE/TAKE/PGM 성공으로 확대하지 않는다.