feat: complete legacy UI and playout parity migration
This commit is contained in:
@@ -1,148 +1,149 @@
|
||||
# 원본 기능 재감사 기준표
|
||||
|
||||
감사 기준일: 2026-07-12
|
||||
감사 기준일: 2026-07-15
|
||||
|
||||
원본(읽기 전용): `C:\Users\MD\source\repos\MBN_STOCK_N`
|
||||
|
||||
마이그레이션 대상: `C:\Users\MD\source\repos\MBN_STOCK_WEBVIEW`
|
||||
|
||||
이 문서는 원본 WinForms/FarPoint 프로그램과 현재 WinUI 3/WebView2 프로그램을 다시 대조한 결과다. 원본 저장소와 실제 Cuts는 읽기 전용으로 조사했다. 자동 테스트나 UI 코드가 존재한다는 사실과 실제 운영 환경에서 동등성이 확인됐다는 사실을 구분한다.
|
||||
## 감사 결론
|
||||
|
||||
현재 실행 대상은 `.NET 8 / C# / WinUI 3 / Windows App SDK 1.8 / WebView2` 기반
|
||||
`MBN_STOCK_WEBVIEW.LegacyParityApp`이다. 이는 웹사이트가 아니라 로컬 Web 자산을
|
||||
WebView2에 표시하는 x64 Windows 데스크톱/MSIX 앱이다.
|
||||
|
||||
초기 프로토타입 문서의 “미이관 버튼 disabled”, “Tornado2에 연결하지 않음”, “DB write
|
||||
없음”은 현재 상태가 아니다. 원본 UI와 클릭 이벤트는 LegacyParityApp → strict bridge →
|
||||
C# workflow로 연결됐고, 개발 DB write 9개 시나리오와 설치된 MSIX의 개발 PGM 최종
|
||||
통합까지 검증했다. 기본 송출 모드는 계속 `DryRun`이다.
|
||||
|
||||
## 상태 정의
|
||||
|
||||
| 상태 | 의미 |
|
||||
|---|---|
|
||||
| `미적용` | 원본 동작의 대응 구현 또는 필수 검증 경로가 아직 없다. |
|
||||
| `적용 중` | 현재 작업 트리에 구현 또는 검사 보강이 있으나 최종 패키지·실환경 검증까지 끝나지 않았다. |
|
||||
| `완료` | 현재 코드와 해당 자동 검증에 반영됐고 이번 재감사에서 완료로 판정했다. |
|
||||
| `외부자산 필요` | 코드만으로 완료할 수 없으며 Git/MSIX 밖의 승인된 자산이나 운영 환경이 필요하다. |
|
||||
|
||||
완료 수는 증분 포팅과 최종 회귀 결과에 따라 바뀌므로 아래 표를 단일 기준으로 사용한다. 기존 문서에서 사용하는 `완료`, `통합 완료`, `전체 통과` 표현도 아래 상태와 검증 범위로 다시 해석해야 한다.
|
||||
| `완료` | 현재 LegacyParityApp 코드와 해당 자동/통합 검증에 반영됨 |
|
||||
| `외부자산 필요` | 코드로 만들 수 없는 Git/MSIX 밖의 승인 영상·배경이 제공되지 않음 |
|
||||
| `외부 데이터 정합성` | 현재 DB view/data가 원본 규칙의 fresh identity를 공급하지 않아 추정 없이 차단함 |
|
||||
|
||||
## 우선순위별 결과
|
||||
|
||||
| ID | 우선순위 | 기능·누락 후보 | 상태 | 근거 파일·메서드 | 현재 판정 | 검증 방법 |
|
||||
|---|---|---|---|---|---|---|
|
||||
| LF-001 | P0 | `s6001` 해외지수 영상 자산 | 외부자산 필요 | 현재 `src/MBN_STOCK_WEBVIEW.Core/Playout/Scenes/ReadOnlyPanelSceneBuilders.cs:491-513`, `S6001SceneMutationBuilder.Metadata`/`Index`; 검증기 `src/MBN_STOCK_WEBVIEW.Playout/Configuration/ValidatedPlayoutOptions.cs:375-406`, `ResolveAssetPath` | 해외지수 15개 target이 13개 `Video\20201008_<국가>.vrv`에 의존한다. 감사 시 승인 Cuts의 `Video` 폴더는 비어 있어 이 target들은 DryRun PREPARE에서도 COM 호출 전에 차단된다. 유가·금의 PNG/JPG는 존재한다. | 승인된 파일을 Cuts root 아래에 제공한 뒤 강화된 cut/asset 검사와 s6001 각 target DryRun PREPARE를 실행한다. 실제 PGM은 별도 승인 회차에서만 확인한다. |
|
||||
| LF-002 | P0 | cut coverage의 장면 종속 asset 검사 | 완료 | `scripts/LegacyCutRequirements.json`, `LegacyCutCoverage.psm1`, `Test-LegacyCutCoverage.ps1`; 테스트 `tests/Scripts/Test-LegacyCutCoverage.Tests.ps1`, `LegacyCutRequirementManifestTests.cs` | 45개 active alias와 23개 장면 종속 asset을 분리해 존재·0바이트·root escape·reparse·manifest 중복을 검사한다. 완전/누락/0바이트/reparse/escape fixture가 자동 테스트에 연결됐다. 실제 승인 Cuts read-only 실행은 alias missing/unsafe 0, asset unsafe 0, asset missing 14로 정확히 실패했다: `s5006` 1개와 `s6001` 13개 영상이다. | 실제 누락 14개가 승인 Cuts에 제공되면 같은 명령을 다시 실행해 `Status=Passed`를 확인한다. 검사기 완료와 외부 자산 준비 완료는 LF-001/LF-003에서 분리한다. |
|
||||
| LF-003 | P0 | `s5006` 큐브 배경 영상 | 외부자산 필요 | 원본 `MBN_STOCK_N/Scene/s5006.cs:30-31`; 현재 `src/MBN_STOCK_WEBVIEW.Core/Playout/Scenes/ReadOnlyPanelSceneBuilders.cs:31-48`; `docs/PLAYOUT_OPERATIONS.md:81-85` | `Video\큐브배경.vrv`가 승인 Cuts에 없다. 기존 운영 문서에는 누락이 기록돼 있으나 파일 제공 전에는 `s5006` PREPARE가 완료된 것이 아니다. | asset 검사 통과 후 s5006 DryRun PREPARE, 승인된 Test/PGM 회차 순으로 검증한다. |
|
||||
| LF-004 | P0 | 실제 운영 DB write | 미적용 | `docs/OPERATOR_UI_PARITY.md:48-62,167-170,214-218,229-246,278`; GraphE `MainWindow.ManualFinancial.cs`; PList `MainWindow.NamedPlaylists.cs`; ThemeA/EList `MainWindow.OperatorCatalogs.cs` | mutation 계약, fake executor, rollback·OutcomeUnknown 테스트는 존재하지만 GraphE, PList/AList, ThemeA, EList의 운영 Oracle write는 실행하지 않았다. FSell/VI도 격리 저장소 write만 검증했다. 따라서 운영 데이터 쓰기 동등성은 미검증이다. | 승인된 테스트용 식별자와 사전 snapshot을 정하고 create/update/delete를 각 1회 실행한다. 매 단계 후 별도 read-only 조회로 결과를 대조하고, timeout·OutcomeUnknown이면 반복하지 않는다. 운영 데이터에는 임의로 실행하지 않는다. |
|
||||
| LF-005 | P0 | 전체 장면·운영 UI의 실제 Tornado2/PGM 검증 | 미적용 | `docs/MIGRATION.md:69-74`; `docs/OPERATOR_UI_PARITY.md:48-62`; `docs/PLAYOUT.md:348-366` | 승인된 실제 회차의 범위는 5001/5074와 그 Page/playlist NEXT, refresh, TAKE OUT이다. 자동 builder/DB smoke 결과를 나머지 장면의 PGM 동등성으로 확대할 수 없다. | 장면별 허용 자산과 DB 전제조건을 먼저 통과시킨다. 이후 회차별 계획 SHA-256과 명시적 승인을 받아 Network Monitoring 명령, callback, PGM 화면을 함께 기록한다. |
|
||||
| LF-006 | P1 | 시스템 종료 확인 | 완료 | `MainWindow.CloseConfirmation.cs`, `EnsureCloseConfirmationAttached`, `OnAppWindowClosing`, `BuildCloseConfirmationMessage`, `DetachCloseConfirmation`; 연결부 `MainWindow.xaml.cs:54-63,101-112,OnClosed`; 테스트 `tests/Web/operator-ui-app-integration.test.cjs:384-408` | 모든 시스템 close를 먼저 취소하고 기본 버튼이 취소인 확인창을 연다. PREPARED/PROGRAM, command pending, OutcomeUnknown에 맞는 경고를 표시하며 종료 과정에서 TAKE OUT이나 반대 명령을 자동 실행하지 않는다. | Web 통합 테스트를 실행하고 패키지 앱에서 idle, prepared/on-air 경고, 취소, 명시적 종료를 수동 확인한다. |
|
||||
| LF-007 | P1 | 패키지 앱 단일 인스턴스 | 완료 | `App.xaml.cs:9-57`, `OnLaunched`, `OnMainInstanceActivated`; 테스트 `tests/Web/operator-ui-app-integration.test.cjs:72-88` | `AppInstance.FindOrRegisterForKey`가 MainWindow와 DB/WebView/playout runtime 생성 전에 실행된다. 두 번째 인스턴스는 activation을 primary로 전달한 뒤 종료하며 기존 프로세스를 이름으로 찾아 강제 종료하지 않는다. | 통합 테스트 후 설치된 MSIX를 두 번 실행해 프로세스 하나, 창 하나, 기존 창 활성화를 확인한다. |
|
||||
| LF-008 | P1 | 운영자 모달의 전역 단축키 격리 | 완료 | `Web/app.js:6808-6845`, `hasOpenOperatorModal`, `isolateOperatorModalShortcut`; 테스트 `tests/Web/operator-ui-app-integration.test.cjs:410-429` | GraphE, FSell/VI, ThemeA/EList 등 modal이 열려 있으면 F2/F3/F8/Escape, Home/End/Delete/Space, Ctrl+K가 workspace 송출·playlist handler로 전달되지 않는다. F8/Escape에는 modal을 먼저 닫으라는 안내를 표시한다. | Web 통합 테스트 후 각 modal 입력 필드에서 해당 키를 눌러 playout command와 playlist mutation이 발생하지 않는지 확인한다. |
|
||||
| LF-009 | P1 | PList의 기존 한국어 `DC_TITLE`/`LIST_TEXT` 복원 | 적용 중 | 원본 `MBN_STOCK_N/Form/PList.cs:81-124`, `data_load`; 현재 `src/MBN_STOCK_WEBVIEW.Core/Data/LegacyPListReadAudit.cs`, `tools/MBN_STOCK_WEBVIEW.DbSmoke/NamedPlaylistReadOnlyDbAudit.cs`, `tools/MBN_STOCK_WEBVIEW.DbSmoke/NamedManualRestoreReadOnlyAudit.cs`; KRX/해외종목/manual 전용 restore와 테스트; 상세 `docs/NAMED_PLAYLIST_READ_AUDIT.md` | 실제 Oracle 활성 정의 23개와 2,213행 모두 정확히 7필드이고 원문 재결합, 한국어 제목·필드, enabled, page 공백 제거 및 typed persistence가 일치했다. 기본 감사는 KRX 94행 current-code remap과 해외종목 78/78을 포함해 `selectionMapped=1,581`, 차단 632다. 별도 GraphE fresh 감사가 manual 36행 중 22행을 materialize하고 저장값 무효 9행·identity 중복 5행을 차단해 두 증거의 통합 결과는 `selectionMapped=1,603`, 차단 610이다. 이 수치는 scene data·asset·PREPARE·PGM 성공이 아니다. | 집중 테스트, Release 실제 read-only DB 감사와 전체 `REAL_DB_SMOKE: PASS`. FSell/VI PList 행은 0건이지만 운영 production read는 FSell 3×5행, VI 9항목·2 page이고 파일 전후 불변이다. raw 값·인증정보 출력, DB write, Tornado/PGM 명령은 0건이다. 남은 KRX/NXT/live-data·GraphE identity/data·unique mapper 차단을 해소하고 scene/asset/package/PGM gate를 별도로 통과하기 전에는 전체 복원 완료로 올리지 않는다. |
|
||||
| LF-010 | P1 | 원본 F2/F3 배경 디렉터리 동등성 | 코드 적용 / 외부자산 필요 | 원본 `MBN_STOCK_N/MainForm.cs:4008-4054`, `btnback_Click`, `ckback_CheckedChanged`; 현재 `MainWindow.Background.cs`, `PlayoutSceneCompositionFactory`, `TrustedPlayoutAssetPath`, `ValidatedPlayoutOptions`; 문서 `docs/TRUSTED_BACKGROUND_ROOT.md` | Cuts gate를 유지한 채 별도 `LegacyBackgroundDirectory`를 추가했고, 생략 시 원본처럼 `SceneDirectory\..\배경`을 계산한다. F2는 유효할 때만 picker 전 `기본.vrv`를 복원하고 F3-on은 항상 기본 파일을 다시 검증한다. root escape·조상/파일 reparse·hardlink·누락·opened-handle final path를 fail closed로 검사하며 Cuts fallback은 없다. 이 장비의 원본 `bin\Debug\배경`과 `기본.vrv`는 실제로 없어 현재 기본 배경은 잠긴다. | Core Debug/Release 1,377/1,377, Playout 387/387과 VS2026 앱 Debug/Release x64 build를 통과했다. signed 1.0.4 package는 누락 상태를 별도 배경 상태로 표시한다. 자산 제공 뒤 F2/F3와 다음 PREPARE mutation/PGM은 승인 회차에서 검증한다. |
|
||||
| LF-011 | P1 | `종목비교.dat` 기존 저장쌍 이전 | 완료 | 원본 `MBN_STOCK_N/Control/UC3.cs:34-55,181-185`, `UC3_Load`, `Data_Load`, `File_Save`; 현재 `src/MBN_STOCK_WEBVIEW.Core/Data/LegacyComparisonPairImport.cs`, `MainWindow.LegacyComparisonImport.cs`, `Web/comparison-import-workflow.js`, `Web/app.js`; 테스트 `LegacyComparisonPairImportServiceTests.cs`, `comparison-import-workflow.test.cjs`, `comparison-import-app-integration.test.cjs`; 실행 증거 `docs/LEGACY_ONE_TIME_IMPORT_AUDIT.md` | 원본 CP949 358바이트·8행·9필드와 SHA-256 `1EE76BC4…BFC0D2F2`를 고정하고, 고정 read-only source·strict parser·현재 Oracle/MariaDB exact identity·전체 원자 저장·one-time marker를 적용했다. 설치된 signed 1.0.3 package에서 빈 상태 `0 PAIRS`를 확인한 뒤 명시적 버튼을 정확히 한 번 실행해 8쌍을 저장했다. 정상 종료·재시작 후에도 `8 PAIRS`, 원본 순서의 8행과 완료형 `원본 8쌍 가져옴` gate가 유지됐고, 원본 파일 SHA-256은 불변이었다. 원본·DB·Tornado에는 쓰기나 명령을 보내지 않았다. | Core 20/20, Web 12/12, 실제 read-only DB identity 8/8, package 0→8 및 재시작 marker 영속성 PASS. 손상·동명이름·기존쌍 중복·부분 실패는 계속 전체 fail closed이며 package family data를 초기화해 같은 회차를 재현하지 않는다. |
|
||||
| LF-012 | P1 | FSell/VI 원본 `Data`의 안전한 이전과 설정 문서 | 적용 중 | 원본 `MBN_STOCK_N/MainForm.cs:338-339`, `FSell.cs:31-88`, `VIList.cs:31-34,137-139`; importer `LegacyManualOperatorDataImport`, `LegacyManualOperatorDataImporter`; production read `S5025TrustedManualFileDataSource`, `ViTrustedManualFileStore`; 감사 `tools/MBN_STOCK_WEBVIEW.DbSmoke/NamedManualRestoreReadOnlyAudit.cs`, `docs/NAMED_MANUAL_RESTORE_READ_ONLY_AUDIT.md` | 고정 원본 경로·strict CP949·source/hash 재검증·no-overwrite staged commit·marker-last·OutcomeUnknown quarantine를 유지한다. 격리 fixture는 FSell 15행·VI 9건, aggregate SHA-256 `fd39a363…a2114`, 첫 실행 성공과 두 번째 `AlreadyImported`를 확인했다. 운영 profile은 이미 원본과 byte-identical한 4파일이 있어 importer가 `DestinationNotEmpty`로 차단되며 marker/intent를 만들지 않는다. production opened-handle read는 FSell 3 audience 각각 5행, VI 9항목·2 page와 안정된 두 번째 version을 확인했고 감사 전후 4파일은 불변이다. 현재 PList에는 FSell/VI 행이 0건이다. | 새 빈 signed package profile의 명시적 UI importer 검증은 남아 있다. 현재 운영 profile의 동일 파일을 덮어쓰거나 marker를 임의 생성하지 않는다. PList에 향후 FSell/VI 행이 생기면 같은 fresh source/version gate를 거치며 scene data·asset·PREPARE·PGM은 별도 검증한다. |
|
||||
| LF-013 | P1 | generic native `bridge-error` 표시 | 완료 | producer `MainWindow.xaml.cs`, `MainWindow.Playout.cs`; consumer `Web/app.js` `handleBridgeError`/`handleNativeMessage`; 테스트 `operator-ui-app-integration.test.cjs` | bounded toast/log로 소비하며 malformed status/quarantine payload에서 playout state를 변경하지 않는다. 전체 Web 413/413과 signed 1.0.4 패키지 반영을 확인했다. | 새 native error producer를 추가할 때마다 같은 bounded consumer 계약을 유지한다. |
|
||||
| LF-014 | P2 | 영구 운영 로그 | 완료 | 원본 `MBN_STOCK_N/MainForm.cs:84-180`; 현재 `NativeOperatorLogWriter.cs`, `NativeOperatorLogTransitions.cs`, `MainWindow.NativeOperatorLog.cs` | `%LOCALAPPDATA%\MBN_STOCK_WEBVIEW\Logs\Log_MMdd.log`에 폐쇄 event/state만 기록하며 4MiB/31일, 중복 억제, reparse/handle 검증, single terminal/OutcomeUnknown 규칙을 적용한다. signed 1.0.4 package context에서도 Startup→DryRunReady→Oracle/MariaDB Healthy가 기록됐고 playout command·PROGRAM·OutcomeUnknown은 0건이었다. SQL·경로·인증·requestId를 기록하지 않는다. | 운영 회차에서도 로그를 명령 재시도 근거로 사용하지 않고 authoritative 출력과 함께 판독한다. |
|
||||
| LF-015 | P2 | 호출자가 없는 native bridge handler | 완료 | `MainWindow.xaml.cs`, `OnWebMessageReceived`; 테스트 `tests/Web/operator-ui-app-integration.test.cjs` | Web 호출자가 없던 `open-external`과 `reload` message case를 native allowlist에서 제거했다. 외부 navigation/new-window 처리와 WebView process failure의 내부 reload 복구 메서드는 별도 native event 경로로 유지한다. | 정적 계약 테스트에서 두 message case와 Web 발신 literal이 모두 없음을 확인했다. |
|
||||
| LF-016 | P2 | 실제 preview 이미지·영상 | 외부자산 필요 | `docs/MIGRATION.md:73-74`; 원본 `MBN_STOCK_N/MainForm.cs:1448-1462,1894-1911,2919-2921` | 현재 preview는 안전한 DTO/mutation 텍스트 중심이다. 실제 이미지·영상 연결은 후속 자산이다. 다만 원본 thumbnail 호출과 watcher는 주석 또는 미호출이므로 활성 원본 동등성의 P0/P1 blocker로 보지 않는다. | 승인된 preview 자산이 생기면 asset root와 노출 정보 검사를 거쳐 패키지 화면에서 확인한다. |
|
||||
| LF-017 | P1 | 모든 업무 탭에서 계속 보이는 종목 검색·31컷 | 완료 | 원본 `MBN_STOCK_N/MainForm.Designer.cs:2022-2036,2454-2462`; 현재 `Web/index.html`의 독립 `stock-panel`, `Web/app.js`의 `renderStockWorkflow`; 테스트 `operator-ui-app-integration.test.cjs`; package 증거 `docs/RELEASE_1_0_4_AUDIT.md` | 종목 검색·31컷을 catalog panel 밖의 독립 열로 옮겼다. signed 1.0.4 package에서 실제 `삼성` 검색 63건을 확인하고 10개 업무 탭을 모두 방문한 뒤에도 검색어·결과·31개 컷이 유지됐다. package는 계속 DryRun이었다. | Web 413/413, source Web 33개 package byte 일치, 설치 package UI PASS. 향후 새 업무 탭도 같은 독립 패널 계약을 유지한다. |
|
||||
| LF-018 | P2 | 원본 트리·컷 목록 일괄 추가 제스처 | 적용 중 | 원본 부모 double-click `MBN_STOCK_N/Control/UC1.cs:141-180`; 소스 컷 Ctrl/Shift 선택 `MBN_STOCK_N/MainForm.cs:3427-3525,3534-3549,3635-3643`; 현재 `Web/legacy-batch-selection-workflow.js`, `Web/app.js`; 테스트 `legacy-batch-selection-*.test.cjs` | 종목 31컷의 plain/Ctrl/Shift/Ctrl+Shift 선택, 단일 double-click, `선택 컷 추가`를 포팅했다. 해외·환율·지수 fixed source의 섹션 `전체 추가`는 모든 자식을 먼저 materialize한 뒤 원본 순서로 한 번에 commit하며, 수동/지원불가가 한 건이라도 있으면 부분 추가 없이 전체 차단한다. 실제 브라우저에서 5개 선택, 해외 43개 순서 추가, 수동 혼합 섹션 0건 추가를 확인했다. | signed MSIX 확인 뒤 업종·비교·테마 등 전용 source group의 부모 전체 추가가 원본에서 실제로 필요한 범위를 계속 분리 포팅한다. source drag/drop 자체는 오조작 검증 뒤 결정한다. |
|
||||
| LF-019 | P1 | 저장 PList의 `테마_NXT` stale code 복원 | 코드 완료 / DB view 정합성 대기 | 원본 `Control/UC4.cs`, `Scene/s5074.cs`, `s5077.cs`, `s5088.cs`; 현재 `src/MBN_STOCK_WEBVIEW.Core/Data/LegacyNamedNxtThemeRestore.cs`, `MainWindow.NamedNxtThemeRestore.cs`, `Web/named-nxt-theme-restore-workflow.js`; 테스트 `LegacyNamedNxtThemeRestoreServiceTests.cs`, `named-nxt-theme-restore-workflow.test.cjs`, `named-nxt-theme-restore-app-integration.test.cjs`; 상세 `docs/NAMED_NXT_THEME_RESTORE.md`, `docs/NXT_THEME_RESTORE_READ_ONLY_AUDIT.md` | 제목 exact lookup, 현재 Maria code, 5/6/12행, 정렬 fallback, 00~12 PRE/13~23 AFTER와 fresh PageN을 닫힌 batch로 복원한다. 실제 48행/24제목은 current identity가 유일하고 저장 code는 전부 stale다. `SB_ITEM` 446행과 NXT master 446행은 존재하지만 원본과 새 앱이 공통으로 쓰는 `v_all_stock` 조인은 0행이어서 48행 모두 `PreviewEmpty`로 차단된다. 통합 PList 결과에서도 `selectionMapped`로 계산하지 않았고 추측성 직접-table fallback을 적용하지 않았다. | Core Debug/Release와 실제 read-only DB smoke PASS, DB write·Tornado/PGM 명령 0건. DBA가 `v_all_stock`의 NXT 포함 의도를 확인해 승인된 변경·rollback으로 복구하거나 직접-table 경로를 별도 승인한 뒤 fresh page preflight/MSIX/PGM을 검증한다. |
|
||||
| LF-020 | P2 | 원본 초기 업무 화면 `해외` | 완료 | 원본 `MainForm.Designer.cs` 첫 TabPage `해외`; 현재 `Web/index.html`, `Web/app.js`; 테스트 `operator-ui-app-integration.test.cjs` | dashboard는 진단 화면으로 유지하되 초기 active identity/제목/요청을 해외로 맞췄다. signed 1.0.4 package를 새로 실행했을 때 해외만 active였고 실제 해외 DB 상태가 표시됐다. | 후속 탭 추가 시에도 초기 identity를 순번이 아닌 `data-market`으로 유지한다. |
|
||||
| LF-021 | P2 | 업무 탭 드래그 순서 변경 | 완료 | 원본 `MainForm.cs:3183-3312`; 현재 `Web/market-nav-reorder.js`, `Web/app.js`; 테스트 `market-nav-reorder*.test.cjs` | dashboard는 첫 위치에 고정하고 10개 업무 identity만 drag 교환/Alt+위·아래로 이동한다. active view/DB/playlist는 불변이고 입력·modal·PREPARE 잠금에서 fail closed하며 세션 종료 시 기본 순서로 돌아간다. 실제 브라우저에서 `Alt+Down` 후 active 해외 유지와 reload 초기화를 확인했다. | signed 1.0.4 패키지에 source Web 33개 일치로 반영했다. |
|
||||
| LF-022 | P2 | GraphE 위/아래 찾기 | 완료 | 원본 `GraphE.cs:338-379,605-611`; 현재 `Web/manual-financial-ui.js/.css`; 테스트 `manual-financial-ui.test.cjs` | `위로 찾기`/`아래로 찾기` 버튼과 Enter 다음 찾기를 포팅했다. trim·대소문자 무시 Contains, 현재 행 전/후, no-wrap, 모든 pending·잠금·hidden 차단을 유지한다. 원본의 `i > 0` 첫 행 누락은 명백한 off-by-one으로 보고 첫 행도 정상 후보에 포함했다. | GraphE 집중 20/20, 전체 Web 413/413과 signed 1.0.4 Web 일치 통과. |
|
||||
| LF-023 | P2 | ThemeA/전문가 편집기 Delete 키 | 완료 | 원본 `ThemeA.cs`/`UC6.cs`; 현재 `Web/operator-catalog-ui.js`; 테스트 `operator-catalog-ui.test.cjs` | ThemeA와 expert draft의 포커스/선택 행 하나를 기존 `removeDraftItem`으로 삭제하고 input/textarea/contenteditable, modal 밖, 잠금/DB write 중에는 상태를 변경하지 않는다. 삭제 후 inputIndex를 재배열하며 native 요청은 보내지 않는다. | 관련 UI/workflow/bridge 32/32, 전체 Web 413/413과 signed 1.0.4 Web 일치 통과. |
|
||||
| LF-024 | P2 | GraphE 종목명 헤더 정렬 | 완료 | 원본 `GraphE.cs:258-280`; 현재 `Web/manual-financial-ui.js/.css`; 테스트 `manual-financial-ui.test.cjs` | `종목명 ↕` 첫 click 내림차순/둘째 click 오름차순을 세션 로컬 display order로 적용했다. 선택 snapshot/native/playlist는 불변이고 위/아래 찾기는 현재 표시 순서를 따른다. 새 화면/목록에서 오름차순으로 초기화한다. | 네 모드·한글/ASCII·빈/단일·pending·정렬된 find 집중 20/20, 전체 Web 413/413 통과. |
|
||||
| LF-025 | P2 | UC7 거래정지 종목명 헤더 정렬 | 완료 | 원본 `Control/UC7.cs:173-188`; 현재 `Web/trading-halt-workflow.js`, `Web/app.js`, `Web/index.html`; 테스트 `trading-halt-name-sort-app-integration.test.cjs` | `종목명 ↕` 첫 click 내림차순/둘째 click 오름차순을 표시 order에만 적용했다. 동명은 market+stockCode로 결정적으로 정렬하고 선택 identity를 유지하며 새 검색은 server 순서로 초기화한다. DB/native/playlist 변경은 없다. | 집중 18/18, 전체 Web 413/413, 실제 브라우저에서 접근 라벨/가로 overflow 0, signed 1.0.4 Web 일치 통과. |
|
||||
| ID | 우선순위 | 기능 | 상태 | 현재 판정과 증적 | 남은 경계 |
|
||||
|---|---|---|---|---|---|
|
||||
| LF-001 | P0 | `s6001` 해외지수 영상 | 외부자산 필요 | builder와 trusted asset 검사는 완료. 승인 Cuts에 국가 영상 13개가 없음 | 제공 후 asset preflight와 해당 scene PGM |
|
||||
| LF-002 | P0 | cut/asset coverage | 완료 | active alias 45개 `.t2s` 존재. alias missing/unsafe 0, asset unsafe 0, asset missing 14를 분리 보고 | 누락 자산 자체는 LF-001/LF-003 |
|
||||
| LF-003 | P0 | `s5006` 큐브 배경 | 외부자산 필요 | `Video\큐브배경.vrv` 1개가 승인 Cuts에 없음. builder 누락이 아님 | 제공 후 s5006 DryRun/PGM |
|
||||
| LF-004 | P0 | 실제 개발 DB write | 완료 | GraphE 4 + PList/AList 2 + ThemeA KRX/NXT 2 + EList 1, 총 9개가 create/save→fresh readback→delete→absence PASS, 잔여 0 | 운영 DB write를 뜻하지 않음 |
|
||||
| LF-005 | P0 | 개발 Tornado2/PGM 최종 통합 | 완료 | 설치된 최신 MSIX에서 `5001` PREPARE/TAKE IN/자동 갱신 → playlist NEXT `5074` 1/2 → Page NEXT 2/2 → TAKE OUT, Network Monitoring·PGM·BYE 확인 | 14개 외부 영상 자산 scene은 제공 후 별도 PGM |
|
||||
| LF-006 | P0 | 원본 35 scene | 완료 | 원본 inventory와 source 기준선 35/35, DTO→mutation→runtime matrix 통과 | 모든 scene의 실제 PGM 완료로 확대 해석 금지 |
|
||||
| LF-007 | P0 | PageN/NEXT | 완료 | 5·6·12개 단위, 최대 20페이지, 빈/경계/마지막 부분 페이지 테스트와 개발 PGM에서 `5074` 9종목의 5+4 Page NEXT·다음 행 NEXT 확인 | 없음 |
|
||||
| LF-008 | P0 | 원클릭 TAKE IN | 완료 | 보이는 TAKE IN/F8 한 번으로 fresh page load 후 PREPARE 1회→TAKE IN 1회. native PREPARE는 진단용으로 유지하되 hidden | unknown에서 재시도 금지 |
|
||||
| LF-009 | P0 | C# 권위/strict bridge | 완료 | Web은 intent만 전송. `LegacyBridgeProtocol` allowlist와 `LegacyOperatorController` snapshot이 DB identity·playlist·scene·송출 상태의 권위 | 새 intent에도 동일 경계 유지 |
|
||||
| LF-010 | P1 | 10개 탭과 action inventory | 완료 | 10탭, fixed 328, 업종 44, 종목 31, 비교 target 24/action 9를 폐쇄형으로 고정 | 동적 DB 행은 별도 검증 |
|
||||
| LF-011 | P1 | UC4/UC6/UC7 클릭 | 완료 | 테마·전문가·거래정지는 원본 단일 클릭. UC4/UC6 행은 fixed tree handler와 독립, stale generation 거부 | 없음 |
|
||||
| LF-012 | P1 | GraphE/FSell/VI | 완료 | GraphE CRUD·찾기·정렬·행 double-click, FSell/VI 확인 시 save+playlist append+close, 취소 경계 이관 | s5025/VI 전용 PGM은 LF-005 |
|
||||
| LF-013 | P1 | PList/AList | 완료 | 목록·생성·save/load/delete·행 double-click 이관, 개발 DB write/readback/cleanup PASS | 기존 blocked raw row는 추정 복원 금지 |
|
||||
| LF-014 | P1 | ThemeA/EList | 완료 | 검색·신규·편집·삭제·종목 구성·순서·Delete key 이관, KRX/NXT/expert 개발 DB 시나리오 PASS | scene PGM은 LF-005 |
|
||||
| LF-015 | P1 | UC5 해외 | 완료 | 해외업종·해외종목·해외지수 조회와 playlist 경로 이관 | s6001 13개 영상은 LF-001 |
|
||||
| LF-016 | P1 | F2/F3 기본 배경 | 외부자산 필요 | sibling background root, picker, background-none와 trusted path 검사는 완료. 이 장비에 `기본.vrv`가 없음 | 제공 전 fail closed |
|
||||
| LF-017 | P1 | 시스템 종료 확인 | 완료 | package-context에서 기본 취소 확인창의 중립 문구·종료/취소 두 버튼을 대조하고 종료 1회, BYE 1회, 자동 TAKE OUT/반대 명령 없음 확인 | 없음 |
|
||||
| LF-018 | P1 | 원본 비교쌍/수동 파일 | 완료 | `종목비교.dat` 8쌍 1회 import·재시작 marker, FSell 3×5행과 VI 9항목 trusted parser/store 검증 | 원본 파일은 계속 읽기 전용 |
|
||||
| LF-019 | P1 | 실제 DB read→DTO→mutation | 완료 | Oracle/MariaDB 필수 scene selector 33/33, 전체 query 58건 PASS | 외부 asset/PGM 성공과 별도 |
|
||||
| LF-020 | P2 | FarPoint 대체 | 완료 | Web table로 바꾸되 선택·정렬·이동·single/double-click·저장 이벤트를 strict C# intent로 재현 | 활성 Print/Excel export는 원본에도 없음 |
|
||||
| LF-021 | P2 | 패키지 단일 인스턴스·모달 격리 | 완료 | 두 번째 activation 전달, 강제 종료 없음. modal에서 F2/F3/F8/Escape와 playlist key 격리 | 새 MSIX 전체 회귀 |
|
||||
| LF-022 | P2 | 영구 운영 로그/bridge 오류 | 완료 | bounded/redacted log와 toast, malformed payload가 송출 상태를 바꾸지 않음 | authoritative Network Monitoring과 함께 판독 |
|
||||
| LF-023 | P1 | NXT 저장 playlist restore | 외부 데이터 정합성 | stale code를 제목/current identity로 재검증하는 닫힌 경로는 완료. 현재 `v_all_stock` 결과가 비어 preview를 fail closed | DBA/data 정합성 없이는 direct-table 추정 금지 |
|
||||
|
||||
## 활성 PLAY_LIST 2,213행 read-only 재분류
|
||||
## DB 검증 상세
|
||||
|
||||
운영 Oracle의 활성 프로그램 정의 23개와 연결된 2,213행을 원문을 출력하지 않는
|
||||
감사로 다시 분류했다. 원본 `PList.data_load`의 page 공백 제거를 제외하면 모든
|
||||
필드를 그대로 보존했고, 현재 persistence 결과와 2,213/2,213 일치했다.
|
||||
### Read-only scene smoke
|
||||
|
||||
| route | 건수 | 현재 결과 |
|
||||
실제 Oracle/MariaDB에서 필수 scene selector **33/33**과 전체 query 58건이 성공했다.
|
||||
이 검증은 실제 DB → 명시 DTO → scene mutation 경로를 증명한다. `.t2s`, 영상·배경 자산,
|
||||
K3D 명령 또는 PGM 화면 검증을 대신하지 않는다.
|
||||
|
||||
### 개발 DB write smoke
|
||||
|
||||
production service/executor와 충돌 격리된 임시 식별자를 사용했다.
|
||||
|
||||
| 시나리오 | 개수 | 검증 |
|
||||
|---|---:|---|
|
||||
| 기존 닫힌 매퍼 | 1,250 | hash 고정 원본 INI와 현재 exact signature로 selection 1,250개 매핑 |
|
||||
| 업종 fixed 닫힌 매퍼 | 24 | selection 24개 매핑 |
|
||||
| 비교 fresh identity | 99 | selection 74개 매핑, 시장 identity 유실 25개 차단 |
|
||||
| 해외지수 candle fresh identity | 61 | selection 61개 매핑 |
|
||||
| NXT theme fresh identity | 48 | validated preview empty 48 차단 |
|
||||
| KRX theme fresh identity | 587 | current-code remap 94, identity 없음 271, 저장 행 무효 16, preview empty 193, 지원 불가 action 13 |
|
||||
| 해외종목 fresh identity | 78 | exact 입력명에서 unique symbol/nation을 correlated materialize해 78개 매핑 |
|
||||
| GraphE fresh materialization | 36 | 별도 double-read row-version·stock identity·cut 계약으로 22개 매핑, 저장값 무효 9·identity 중복 5 차단. FSell/VI PList행은 0 |
|
||||
| unique closed mapper 없음 | 30 | 원본 328-action runtime hash와 현재 exact signature 중 단일 후보가 없어 nearest scene 추정 없이 차단 |
|
||||
| 합계 | 2,213 | 기본 감사 `selectionMapped=1,581`/차단 632; 별도 GraphE proof를 합치면 `selectionMapped=1,603`/차단 610 |
|
||||
| GraphE `INPUT_PIE`, `INPUT_GROW`, `INPUT_SELL`, `INPUT_PROFIT` | 4 | create, fresh readback, delete, absence |
|
||||
| PList/AList named playlist | 2 | definition/items save, fresh readback, delete, absence |
|
||||
| ThemeA KRX | 1 | create, preview/readback, delete, absence |
|
||||
| ThemeA NXT | 1 | create, preview/readback, delete, absence |
|
||||
| EList expert | 1 | create, preview/readback, delete, absence |
|
||||
| 합계 | **9** | **PASS**, 잔여 0 |
|
||||
|
||||
`unique=2,213`은 전부 송출 가능하다는 뜻이 아니라 모든 행이 단일 route의
|
||||
selection 결과 또는 명시적 차단 사유를 가졌다는 뜻이다. `selectionMapped`도
|
||||
장면 데이터·자산, package context, PREPARE 또는 PGM 성공이 아니다. 이들은 집계
|
||||
밖의 별도 gate다. KRX/NXT theme은
|
||||
원본 `s5074/s5077/s5088`처럼 제목 exact lookup을 사용하며, 저장 code를 가장 가까운
|
||||
현재 code로 추정하지 않는다. 비교 25행도 현재 이름이 우연히 유일하더라도 저장
|
||||
행이 시장을 보존하지 않았으므로 계속 차단한다. 상세 SQL 경계와 결과는
|
||||
[`NAMED_PLAYLIST_READ_AUDIT.md`](NAMED_PLAYLIST_READ_AUDIT.md)에 기록했다.
|
||||
write는 자동 재시도하지 않는다. `OutcomeUnknown`이면 cleanup mutation이나 다음 시나리오를
|
||||
보내지 않는 구현·테스트 경계를 유지한다. 이 결과는 개발 DB 검증이며 운영 DB에 임의 쓰기를
|
||||
허용하거나 수행했다는 뜻이 아니다.
|
||||
|
||||
## s6001 asset 검사 보강 기준
|
||||
## 기존 PLAY_LIST read-only 재분류
|
||||
|
||||
`s6001` 해외지수 target은 다음 13개 파일을 요구한다.
|
||||
실제 Oracle 활성 정의 23개와 연결된 2,213행은 원문의 7필드, 한국어, enabled,
|
||||
ITEM_INDEX와 원본의 page 공백 제거 규칙을 보존했다.
|
||||
|
||||
- `Video\20201008_미국.vrv`
|
||||
- `Video\20201008_독일.vrv`
|
||||
- `Video\20201008_영국.vrv`
|
||||
- `Video\20201008_프랑스.vrv`
|
||||
- `Video\20201008_일본.vrv`
|
||||
- `Video\20201008_중국.vrv`
|
||||
- `Video\20201008_홍콩.vrv`
|
||||
- `Video\20201008_대만.vrv`
|
||||
- `Video\20201008_싱가포르.vrv`
|
||||
- `Video\20201008_태국.vrv`
|
||||
- `Video\20201008_필리핀.vrv`
|
||||
- `Video\20201008_말레이시아.vrv`
|
||||
- `Video\20201008_인도네시아.vrv`
|
||||
| route | 건수 | 결과 |
|
||||
|---|---:|---|
|
||||
| 기존 닫힌 매퍼 | 1,250 | selection 1,250 |
|
||||
| 업종 | 24 | selection 24 |
|
||||
| 비교 | 99 | selection 74, 시장 identity 유실 25 차단 |
|
||||
| 해외지수 candle | 61 | selection 61 |
|
||||
| NXT theme | 48 | current preview empty 48 차단 |
|
||||
| KRX theme | 587 | current-code 94, 나머지 identity/preview/문법/지원 경계로 차단 |
|
||||
| 해외종목 | 78 | selection 78 |
|
||||
| GraphE | 36 | fresh materialization 22, 값/identity 14 차단 |
|
||||
| unique closed mapper 없음 | 30 | nearest-scene 추정 없이 차단 |
|
||||
| 합계 | **2,213** | 통합 selection 1,603 / 명시 차단 610 |
|
||||
|
||||
검사 보강은 다음 조건을 만족해야 한다.
|
||||
`selectionMapped`는 scene asset, PREPARE나 PGM 성공 수치가 아니다. 상세는
|
||||
[`NAMED_PLAYLIST_READ_AUDIT.md`](NAMED_PLAYLIST_READ_AUDIT.md)를 따른다.
|
||||
|
||||
1. `.t2s` alias 결과와 asset 결과를 별도 필드로 보고한다.
|
||||
2. 상대 경로만 허용하고 Cuts root 탈출을 거부한다.
|
||||
3. 파일 존재, 0바이트 여부, reparse point를 검사한다.
|
||||
4. 누락된 상대 경로와 영향을 받는 builder를 운영자가 알 수 있게 하되 secret이나 외부 절대 경로를 출력하지 않는다.
|
||||
5. asset이 없는 현재 장비에서는 전체 migration 완료가 아니라 `외부자산 필요`로 끝나야 한다.
|
||||
6. 완전 fixture, 단일 누락, 복수 누락, 0바이트, reparse fixture를 자동화한다.
|
||||
## 외부자산 목록
|
||||
|
||||
권장 검증 명령 형태:
|
||||
45개 active alias의 `.t2s`는 존재한다. 별도 종속 asset 14개만 제공되지 않았다.
|
||||
|
||||
```powershell
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File .\scripts\Test-LegacyCutCoverage.ps1 `
|
||||
-CutRoot "<승인된 읽기 전용 Cuts root>"
|
||||
```
|
||||
- `s5006`: `Video\큐브배경.vrv`
|
||||
- `s6001`:
|
||||
- `Video\20201008_미국.vrv`
|
||||
- `Video\20201008_독일.vrv`
|
||||
- `Video\20201008_영국.vrv`
|
||||
- `Video\20201008_프랑스.vrv`
|
||||
- `Video\20201008_일본.vrv`
|
||||
- `Video\20201008_중국.vrv`
|
||||
- `Video\20201008_홍콩.vrv`
|
||||
- `Video\20201008_대만.vrv`
|
||||
- `Video\20201008_싱가포르.vrv`
|
||||
- `Video\20201008_태국.vrv`
|
||||
- `Video\20201008_필리핀.vrv`
|
||||
- `Video\20201008_말레이시아.vrv`
|
||||
- `Video\20201008_인도네시아.vrv`
|
||||
- 기본 배경: sibling `배경\기본.vrv`
|
||||
|
||||
검사 보강 전의 `Missing=0`은 `.t2s` alias만 의미하며 asset-ready 증거로 사용하면 안 된다.
|
||||
이 파일들의 부재는 code/build 실패가 아니라 **외부자산 미제공**이다. 검사기는 존재,
|
||||
0바이트, root escape, reparse와 manifest 중복을 검사하고 누락 경로와 영향 builder만
|
||||
redacted 형태로 보고한다.
|
||||
|
||||
## 원본 I/O 조사에서 제외 판정한 항목
|
||||
## 원본 I/O에서 의도적으로 제외한 항목
|
||||
|
||||
- 활성 Print/Excel/FarPoint export/import 호출은 발견되지 않았다.
|
||||
- `MainForm.cs:1767-1772,1796-1805`의 CSV playlist save/load는 주석 처리돼 있다.
|
||||
- `MainForm.GetThumnail`과 preview watcher/`SaveImageFile` 호출도 활성 경로가 아니다.
|
||||
- `s8086`은 scene builder는 존재하지만 원본 MainForm active alias가 없어 의도적으로 unreachable이다. 이를 UI 누락으로 계산하지 않는다.
|
||||
- FarPoint의 표·선택·정렬·이동 자체는 Web table/workflow로 변환할 수 있지만, 변환 UI가 존재한다는 사실만으로 DB write나 PGM 동등성이 완료되지는 않는다.
|
||||
- 활성 Print/Excel/FarPoint export/import 호출은 원본에서 발견되지 않았다.
|
||||
- CSV playlist save/load, thumbnail watcher와 `SaveImageFile`은 주석 또는 미호출 경로다.
|
||||
- `s8086`은 builder는 있으나 원본 MainForm active alias가 없는 diagnostic route다.
|
||||
- 위 dead/hidden 경로를 새 활성 기능으로 만들지 않는다.
|
||||
|
||||
## 기존 완료 문서 교정 후속
|
||||
## 최종 증적 기록
|
||||
|
||||
다음 문서는 구현 존재, 자동 계약 테스트, 실데이터 read smoke, 외부 asset readiness, 실제 DB write, 실제 PGM 검증을 서로 다른 열로 분리해야 한다.
|
||||
확정된 증적과 최신 전체 회귀의 기록 위치는
|
||||
[`LEGACY_PARITY_VALIDATION_20260715.md`](LEGACY_PARITY_VALIDATION_20260715.md)다.
|
||||
|
||||
| 문서 | 교정할 표현 | 후속 교정 내용 |
|
||||
|---|---|---|
|
||||
| `docs/MIGRATION.md:69-74` | MainForm/UC1~UC7과 전용 화면을 포괄적으로 `완료`로 표현 | `화면·bridge 구현`과 `운영 동등성 검증`을 분리한다. PList 한국어 복원, s6001 asset, 원본 파일 one-time import, DB write, 장면별 PGM 상태를 이 문서와 맞춘다. |
|
||||
| `docs/OPERATOR_UI_PARITY.md:48-62` | 다수 화면을 `화면 통합 완료`로 표현 | 해당 표현은 구조적 UI 도달성만 뜻한다고 명시하고 DB-W/PGM 미검증을 상단 요약과 각 행에 유지한다. 완료 판정은 실제 검증 열까지 충족할 때만 사용한다. |
|
||||
| `docs/OPERATOR_UI_PARITY.md:75,111-113,214-218` | PList와 비교쌍 load/save를 완료로 표현 | PList 한국어 실DB 복원과 `종목비교.dat` one-time import를 별도 미완료 항목으로 추가한다. |
|
||||
| `docs/OPERATOR_UI_PARITY.md:77` | F2/F3 배경을 완료로 표현 | 별도 sibling `배경` root와 `기본.vrv` 코드/자동 검증은 반영됐다. 실제 자산이 없으므로 package/PGM 완료로 확대하지 않는다. |
|
||||
| `docs/SCENE_EQUIVALENCE.md:17,98` | 전체 real-data smoke 및 s6001을 asset 경고 없이 통과로 표현 | DB→DTO→mutation smoke와 실제 asset preflight를 분리하고 s6001 국가 영상 13개를 `외부자산 필요`로 기록한다. |
|
||||
| `docs/PLAYOUT_OPERATIONS.md:86,94` | s5025 환경 변수 안내와 alias-only cut 검사 | 정상 앱의 private manual store 경로와 one-time import 정책을 정확히 설명한다. cut coverage 결과에 asset 검사 상태를 추가한다. |
|
||||
| `docs/PLAYOUT.md:97,348-366` | Web 배경 변경 불가 설명 및 자동 검증을 35개 완료 근거로 사용 | F2/F3 native picker의 실제 경계를 반영한다. 5001/5074 PGM 성공과 나머지 scene 자동 검증을 명확히 분리한다. |
|
||||
다음 값은 추측하거나 이전 package 결과를 재사용하지 않는다.
|
||||
|
||||
이 교정은 기존 구현 성과를 취소하는 것이 아니라, `구현됨`과 `실제 방송 동등성 완료`를 같은 의미로 사용하지 않기 위한 것이다.
|
||||
| 항목 | 상태 |
|
||||
|---|---|
|
||||
| Debug/Release x64 전체 테스트 개수 | **PASS** — .NET 각 2,394/2,394, Web 435/435 |
|
||||
| 새 Release x64 MSIX SHA-256·서명 | `FC4EEE1509675E643D2B4CB435E279CAC4611B28534048D9B9EE611E361E0E1F`, Valid, `CN=Comtrophy` |
|
||||
| 새 package-context 실행 | **PASS** — DryRun과 개발 Live, 정상 종료 |
|
||||
| 개발 Tornado2 Network Monitoring/PGM 결과 | **PASS** — `5001` → `5074` 1/2 → 2/2 → TAKE OUT/BYE |
|
||||
| Gitea 반영 commit | **PASS** — 최종 이관·검증 문서 포함 commit을 `origin/main`에 fast-forward push |
|
||||
|
||||
## 완료 판정에 필요한 최종 증거
|
||||
|
||||
1. s5006/s6001 포함 모든 reachable scene의 asset preflight 결과
|
||||
2. PList 한국어 `DC_TITLE`/`LIST_TEXT` 실DB read 비교
|
||||
3. 기존 `종목비교.dat`, FSell, VI 데이터의 안전한 one-time migration 결과
|
||||
4. 승인된 테스트 데이터로 수행한 GraphE/PList/ThemeA/EList 및 manual file write read-back
|
||||
5. Debug/Release x64 회귀와 Release MSIX package-context 실행
|
||||
6. 승인된 scene·회차의 Network Monitoring 명령, callback, PGM 화면
|
||||
7. timeout·OutcomeUnknown에서 재시도하지 않았다는 운영 기록
|
||||
|
||||
이 증거가 갖춰지기 전에는 전체 원본 UI 또는 35개 scene의 동등성 목표를 완료 처리하지 않는다.
|
||||
원본 저장소는 최종 검증과 배포 이후에도 계속 읽기 전용으로 유지한다.
|
||||
|
||||
@@ -1,101 +1,141 @@
|
||||
# 원본 호환 마이그레이션 기준선
|
||||
|
||||
## 결정
|
||||
기준일: 2026-07-15
|
||||
|
||||
기존 저장소와 검증된 `Core / Infrastructure / Playout`은 유지한다. 기존 루트 WinUI 앱은 비교용 프로토타입으로 보존하고, 원본 UI와 이벤트를 먼저 재현하는 `MBN_STOCK_WEBVIEW.LegacyParityApp`을 같은 솔루션에서 병행 개발한다.
|
||||
## 현재 결정
|
||||
|
||||
WebView는 HTML 컨트롤 표시와 사용자 입력 전달만 담당한다. 검색 정책, 선택 상태, 플레이리스트 필드, 장면 alias, PageN 및 송출 상태의 권위는 C# Application 계층에 둔다.
|
||||
마이그레이션의 실행 대상은 `MBN_STOCK_WEBVIEW.LegacyParityApp`이다. 이 앱은
|
||||
`.NET 8 / C# / WinUI 3 / Windows App SDK 1.8 / WebView2 / Windows x64 / MSIX`로
|
||||
구성된 Windows 데스크톱 프로그램이다. WebView2는 앱에 포함된 로컬 HTML/CSS/JavaScript를
|
||||
표시하는 컨트롤이며, 별도 웹 서버에 배포하는 웹사이트가 아니다.
|
||||
|
||||
## 기준 원본
|
||||
원본 WinForms UI의 화면 구성과 클릭 이벤트를 먼저 동등하게 옮긴 뒤 개선한다. 기존에
|
||||
검증된 `Core / Infrastructure / Playout`은 재사용하며, 원본 업무 규칙과 송출 권위는
|
||||
C#에 둔다.
|
||||
|
||||
- 읽기 전용 저장소: `C:\Users\MD\source\repos\MBN_STOCK_N`
|
||||
- 조사 시 HEAD: `309286309eaf20b58172247263f575753a860700`
|
||||
- 실제 실행 컷 목록: `MBN_STOCK_N\bin\Debug\Res\종목.ini`
|
||||
- 실행 컷 목록 SHA-256: `45DFB1804828F0E4A45F73D681AF0709CE5662872FAA49CFC9F7A0B940409E20`
|
||||
- 실행 컷 목록은 구분행 2개를 포함한 33개 물리행이며 실제 컷은 31개다.
|
||||
- 3번 컷은 고객 화면과 실행본을 따라 `1열판기본_시간외단일가`를 사용한다. 소스 `Res`의 `시간외종가`는 기준으로 사용하지 않는다.
|
||||
## 기준 원본과 보존 원칙
|
||||
|
||||
원본 작업 트리에는 기존 수정이 있으므로 커밋 SHA만으로 동등성을 판단하지 않는다. 원본은 계속 읽기 전용으로 취급한다.
|
||||
- 읽기 전용 원본: `C:\Users\MD\source\repos\MBN_STOCK_N`
|
||||
- 원본 저장소와 `MBN_STOCK_N\MBN_STOCK_N\bin\Debug\Cuts`는 분석·검증에만 사용한다.
|
||||
- 원본 파일, 원본 설정, 실제 컷과 자산을 수정하거나 새 저장소에 복사하지 않는다.
|
||||
- 벤더 DLL, K3D 라이선스, 인증서, DB 비밀번호, 실제 컷·배경 파일과 로컬 운영 설정은
|
||||
Git/MSIX에 넣지 않는다.
|
||||
- 실행 컷 목록은 구분행 2개를 포함한 물리행 33개와 실제 종목 컷 31개다.
|
||||
- 원본 scene inventory는 35개 builder이며, MainForm에서 도달 가능한 runtime은 34개
|
||||
builder와 45개 active alias다. `s8086`은 원본과 같이 무alias 진단 전용이다.
|
||||
|
||||
## 신규 프로젝트
|
||||
## 실행 구조와 권위 경계
|
||||
|
||||
| 프로젝트 | 책임 |
|
||||
| 계층 | 책임 |
|
||||
|---|---|
|
||||
| `MBN_STOCK_WEBVIEW.LegacyApplication` | 원본 MainForm/UC 상태, 이벤트 및 업무 규칙 |
|
||||
| `MBN_STOCK_WEBVIEW.LegacyBridge` | 허용된 WebView 입력 intent와 표시 snapshot 계약 |
|
||||
| `MBN_STOCK_WEBVIEW.LegacyParityApp` | WinUI 3 창, WebView2, DB composition root, 별도 개발 MSIX |
|
||||
| `LegacyApplication.Tests` | 원본 이벤트 및 결과 DTO 동등성 테스트 |
|
||||
| `LegacyBridge.Tests` | strict JSON과 Web 권한 경계 테스트 |
|
||||
| `LegacyWeb.Tests` | Visual Studio에서 항상 실행되는 Web thin-client 계약 테스트 |
|
||||
| `MBN_STOCK_WEBVIEW.LegacyApplication` | 원본 MainForm/UC/Form 상태, 클릭 이벤트, playlist와 업무 규칙의 C# 권위 |
|
||||
| `MBN_STOCK_WEBVIEW.LegacyBridge` | WebView가 보낼 수 있는 intent의 strict JSON allowlist와 표시 snapshot 계약 |
|
||||
| `MBN_STOCK_WEBVIEW.LegacyParityApp` | WinUI 3 창, 로컬 WebView2 UI, DB composition root, 송출 연결과 개발 MSIX |
|
||||
| `MBN_STOCK_WEBVIEW.Core` | DTO, scene resolver/builder, PageN과 `LegacyPlayoutWorkflow` |
|
||||
| `MBN_STOCK_WEBVIEW.Infrastructure` | Oracle/MariaDB와 신뢰 파일 저장소 구현 |
|
||||
| `MBN_STOCK_WEBVIEW.Playout` | `IPlayoutEngine` 뒤의 K3D/Tornado2 어댑터, STA 큐와 안전 게이트 |
|
||||
|
||||
개발 패키지 identity는 `Wickedness.MBNStockWebView.LegacyParity`다. 운영 identity `Wickedness.MBNStockWebView`를 대체하지 않는다.
|
||||
WebView는 표시와 사용자 입력 수집만 담당한다. 종목·시장 identity, 7필드 playlist 행,
|
||||
scene alias, DB mutation, PageN, PREPARED/PROGRAM 상태는 Web에서 임의 생성하거나
|
||||
확정할 수 없다. 모든 입력은 `LegacyBridgeProtocol`의 폐쇄형 intent로 파싱되고
|
||||
`LegacyOperatorController`가 다시 검증한 뒤 실행된다.
|
||||
|
||||
## 첫 수직 흐름 상태
|
||||
## 이관된 원본 UI와 이벤트
|
||||
|
||||
구현됨:
|
||||
- 원본 10개 업무 탭과 탭 순서 변경
|
||||
- 모든 탭에서 유지되는 종목 검색, 종목 결과, 31개 컷과 playlist 편집
|
||||
- 328개 fixed action, 코스피·코스닥 업종 44개 action, 비교 대상 24개와 비교 9개 action
|
||||
- UC4 테마, UC6 전문가와 UC7 거래정지의 원본 단일 클릭 동작
|
||||
- UC4/UC6 목록 행과 고정 action 트리의 독립 클릭 경계
|
||||
- GraphE 네 수동 재무 화면과 행 더블클릭 추가
|
||||
- FSell 개인·외국인·기관 5행 입력과 VI 목록·정렬·페이지 입력
|
||||
- PList/AList의 목록, 신규 생성, 저장, 불러오기, 삭제와 행 더블클릭
|
||||
- ThemeA와 EList의 검색, 생성, 편집, 삭제, 종목 구성과 순서 변경
|
||||
- UC5 해외업종·해외종목·해외지수
|
||||
- playlist 단일/Ctrl/Shift 선택, enabled, 위/아래 이동, 선택/전체 삭제
|
||||
- F2/F3 배경, Fade, NEXT/Page NEXT, TAKE OUT, 종료 확인과 자동 갱신
|
||||
|
||||
- 검색 버튼의 정확한 빈 문자열 no-op과 Enter 빈 검색 허용
|
||||
- KOSPI → KOSDAQ → NXT KOSPI → NXT KOSDAQ 순서 및 DB 반환 순서 유지
|
||||
- trim, 전체 정렬, 결과 제한 및 wildcard escape를 하지 않는 원본 호환 검색
|
||||
- 바인드 파라미터를 통한 SQL injection 방지
|
||||
- master snapshot을 이름으로 다시 순회하고 마지막 일치를 채택하는 `jongmokCode` 규칙
|
||||
- 33개 물리 컷 행과 31개 display 번호
|
||||
- 원본 `ClientSize 1905 x 1015`, `FixedSingle`, 최대화 불가 창 계약
|
||||
- 일반/Ctrl/Shift/Ctrl+Shift 선택을 C# 상태로 처리
|
||||
- 0.5초 이내, 동일 행, 동일 좌표 조건의 원본 더블클릭 판정
|
||||
- 선택 컷 drag/drop과 물리 index 오름차순 추가
|
||||
- 원본 7필드 플레이리스트 행과 중복 추가 허용
|
||||
- NXT `1열판기본_현재가`만 허용하고 나머지는 원본 경고 표시
|
||||
- PREPARE 직전 `LegacyPlaylistEntry`로 변환할 수 있는 31개 컷 행의 alias 매핑
|
||||
FarPoint 표 자체는 Web table로 바뀌었지만 원본의 선택, 정렬, 이동, 단일/더블클릭,
|
||||
저장·불러오기 이벤트 경계는 C# intent와 snapshot으로 유지한다.
|
||||
|
||||
아직 이관하지 않음:
|
||||
## 송출 동작
|
||||
|
||||
- 좌측 수동 입력 GraphE 4개 모달
|
||||
- 중앙 UC1~UC7과 모든 탭 이벤트
|
||||
- 플레이리스트 선택/삭제/정렬/DB 저장·불러오기
|
||||
- 배경, 이동평균, Fade/Update 시간 컨트롤
|
||||
- PREPARE/TAKE IN/NEXT/TAKE OUT 실제 연결
|
||||
- 운영 DB write와 Tornado2/PGM 검증
|
||||
운영자 화면에는 원본처럼 `TAKE IN`, `NEXT`, `TAKE OUT`을 제공한다. 별도 native
|
||||
`PREPARE` 버튼은 엔지니어링 진단용으로 DOM에 유지하지만 숨겨져 있다. idle 상태에서
|
||||
`TAKE IN` 또는 F8을 한 번 실행하면 C#이 같은 게이트 안에서 다음 순서를 정확히 한 번씩
|
||||
수행한다.
|
||||
|
||||
미이관 버튼은 현재 disabled 상태이며, 연결 상태는 `미연결`로 명시한다. 새 앱은 Tornado2에 연결하지 않는다.
|
||||
1. 선택 playlist와 page를 C# snapshot으로 고정
|
||||
2. 실제 DB/신뢰 파일에서 DTO를 다시 조회
|
||||
3. `BeginTransaction` → mutation → `QueryVariables` → `EndTransaction`
|
||||
4. `Prepare`
|
||||
5. `TakeIn`
|
||||
|
||||
PREPARE 성공 뒤 TAKE IN이 실패하거나 결과가 불명확하면 prepared 소유권을 유지하고
|
||||
자동 재시도하지 않는다. `NEXT`는 같은 scene의 다음 PageN과 다음 enabled playlist 행을
|
||||
원본 조건으로 구분한다. timeout과 `OutcomeUnknown`에서는 반대 명령, cleanup 명령 또는
|
||||
동일 명령을 추측해 반복하지 않는다.
|
||||
|
||||
기본 송출 모드는 계속 `DryRun`이다. Live는 개발 Tornado2/PGM 설정, 프로세스별 명시적
|
||||
환경 승인, scene allowlist, 명령 예산을 모두 통과해야 한다. 이 안전 게이트는 개발 환경에서도
|
||||
완화하지 않는다.
|
||||
|
||||
## scene, alias와 PageN 기준선
|
||||
|
||||
- 원본 scene builder source inventory: **35/35**
|
||||
- 도달 가능한 active alias: **45개**, 승인된 읽기 전용 Cuts에 대응 `.t2s` 존재
|
||||
- PageN: `s5074=5`, `s5077=6`, `s5088=12`개 단위
|
||||
- page count: `min(20, ceil(itemCount / pageSize))`
|
||||
- 빈 결과, 첫/마지막 부분 페이지, 정확한 경계, 20페이지 cap, 21페이지 거부,
|
||||
같은 scene Page NEXT와 다음 playlist NEXT를 자동 테스트로 고정
|
||||
|
||||
장면별 세부 상태는 [`SCENE_EQUIVALENCE.md`](SCENE_EQUIVALENCE.md), 호출 순서는
|
||||
[`LEGACY_PLAYOUT_ANALYSIS.md`](LEGACY_PLAYOUT_ANALYSIS.md)를 따른다.
|
||||
|
||||
## DB 검증 기준선
|
||||
|
||||
실제 Oracle/MariaDB read-only 스모크는 필수 scene selector **33/33**, 전체 query 58건을
|
||||
통과했다. 이는 실제 DB → DTO → mutation 경로 검증이며 외부 영상 자산이나 PGM 출력 성공을
|
||||
뜻하지 않는다.
|
||||
|
||||
개발 DB write 스모크는 충돌 격리된 임시 식별자로 다음 9개 시나리오의
|
||||
create/save → fresh readback → delete → absence를 통과했다.
|
||||
|
||||
- GraphE `INPUT_PIE`, `INPUT_GROW`, `INPUT_SELL`, `INPUT_PROFIT` 4개
|
||||
- PList/AList named playlist 1개
|
||||
- ThemeA KRX 1개와 NXT 1개
|
||||
- EList/expert 1개
|
||||
|
||||
스모크는 production executor를 사용하고 write 재시도를 하지 않는다. 결과가
|
||||
`OutcomeUnknown`이면 cleanup이나 다음 시나리오를 실행하지 않는 규칙도 유지한다.
|
||||
DB 비밀번호와 원문 데이터는 문서와 로그에 기록하지 않는다.
|
||||
|
||||
## 외부자산 상태
|
||||
|
||||
45개 active alias의 `.t2s`는 존재한다. 다만 다음 14개 종속 파일은 현재 승인 Cuts에
|
||||
제공되지 않았다.
|
||||
|
||||
- `s5006`: `Video\큐브배경.vrv` 1개
|
||||
- `s6001`: 국가별 `Video\20201008_<국가>.vrv` 13개
|
||||
|
||||
또한 원본 sibling `배경` 디렉터리의 `기본.vrv`도 이 장비에 없다. 이 상태는 scene/UI
|
||||
코드 누락이나 마이그레이션 실패가 아니라 **Git/MSIX 밖 외부자산 미제공**이다. 자산이
|
||||
없으면 해당 경로는 COM 호출 전에 fail closed하고, 다른 파일로 추정 대체하지 않는다.
|
||||
|
||||
## Visual Studio 실행
|
||||
|
||||
1. `MBN_STOCK_WEBVIEW.sln`을 연다.
|
||||
2. 솔루션 구성을 `ParityDebug | x64`로 선택한다.
|
||||
3. `MBN_STOCK_WEBVIEW.LegacyParityApp`을 시작 프로젝트로 지정한다.
|
||||
4. 실행한다.
|
||||
2. `x64` 구성을 선택한다.
|
||||
3. `src\MBN_STOCK_WEBVIEW.LegacyParityApp\MBN_STOCK_WEBVIEW.LegacyParityApp.csproj`를
|
||||
시작 프로젝트로 지정한다.
|
||||
4. Debug 실행 또는 Release x64 MSIX를 생성한다.
|
||||
|
||||
`PrototypeDebug | x64`는 기존 루트 앱을 배포하는 비교용 구성이다. 두 앱은 서로 다른 MSIX identity와 single-instance key를 사용한다.
|
||||
개발 패키지 identity는 `Wickedness.MBNStockWebView.LegacyParity`이며 기존 운영 패키지
|
||||
identity를 대체하지 않는다.
|
||||
|
||||
기존 `Release | x64`의 자동 배포 대상은 비교용 루트 앱으로 유지한다. 호환 앱 Release는 두 패키지를 동시에 배포하지 않도록 `MBN_STOCK_WEBVIEW.LegacyParityApp` 프로젝트의 MSIX 생성 명령으로 명시적으로 만든다.
|
||||
## 2026-07-15 검증 기록
|
||||
|
||||
## 2026-07-15 기준선 검증
|
||||
|
||||
- `ParityDebug | x64`와 `Release | x64` 솔루션 빌드: 경고 0, 오류 0
|
||||
- Debug/Release 각각 C# 테스트 1,980개 통과
|
||||
- Core 1,383 / Infrastructure 173 / Playout 393
|
||||
- LegacyApplication 17 / LegacyBridge 7 / LegacyWeb 7
|
||||
- Node 기반 JavaScript syntax 및 Web 계약 테스트 7개 통과
|
||||
- Release x64 개발 MSIX `0.1.0.0` 생성 및 `CN=Comtrophy` 서명 검증
|
||||
- MSIX SHA-256: `D3FF9CF1F63E6BC68AA147C0ED47690FE130CF425C0276B3201F763EC5C50A4B`
|
||||
- 패키지 76개 항목 중 인증서, 비밀번호, 라이선스, `.t2s`, 운영 설정 파일 0개
|
||||
- 패키지의 `Web/index.html`, `styles.css`, `app.js`가 소스와 byte 단위로 일치
|
||||
- 별도 패키지 전체 이름 `Wickedness.MBNStockWebView.LegacyParity_0.1.0.0_x64__qbv3jkvsn3aj0`, package family `Wickedness.MBNStockWebView.LegacyParity_qbv3jkvsn3aj0` 설치·실행 성공
|
||||
- 패키지 창의 실제 WebView client bounds `1905 x 1015` 확인
|
||||
- 격리 WinForms ListView trace로 최초 Shift의 native 선택 `0..현재행`과 별도 pointer 목록의 `-1..현재행`, 같은 행 Shift의 native 선택 유지와 pointer 목록 clear를 확인하고 두 상태를 분리
|
||||
- 실제 DB read-only 경로로 `삼성` 검색 → `삼성출판사` 선택 → `현재가` 동일 좌표 더블클릭 → 원본 7필드 플레이리스트 행 생성 확인
|
||||
- 검색 시작 직후 정상 창 닫기 경합 검증에서 10초 이내 종료 및 Windows Application 오류 이벤트 0개
|
||||
|
||||
이 검증에서는 DB write, PREPARE, TAKE IN, NEXT, TAKE OUT 및 Tornado2 연결을 실행하지 않았다. 화면의 송출 버튼도 계속 비활성 상태다.
|
||||
|
||||
## 다음 이관 순서
|
||||
|
||||
1. 플레이리스트 row 선택·Ctrl/Shift·삭제·전체/개별 enabled
|
||||
2. 중앙 탭과 UC1부터 원본 이벤트 단위로 수직 이관
|
||||
3. 수동 입력 Form과 격리 DB write/read-back
|
||||
4. C# playlist authority를 기존 `LegacyPlayoutWorkflow`에 연결한 DryRun PREPARE
|
||||
5. 35개 scene/PageN differential trace
|
||||
6. 승인된 실제 Tornado2/PGM 검증
|
||||
|
||||
각 기능은 원본 입력과 신규 입력의 snapshot, DB 파라미터, 플레이리스트 7필드 및 K3D trace를 비교한 뒤 완료 처리한다.
|
||||
확정된 전체 회귀, 새 Release MSIX SHA-256, package-context DryRun/Live, 개발
|
||||
Tornado2 Network Monitoring과 PGM 결과는
|
||||
[`LEGACY_PARITY_VALIDATION_20260715.md`](LEGACY_PARITY_VALIDATION_20260715.md)에
|
||||
기록했다. 이전 패키지의 숫자나 해시는 현재 결과로 재사용하지 않았다.
|
||||
|
||||
124
docs/LEGACY_PARITY_VALIDATION_20260715.md
Normal file
124
docs/LEGACY_PARITY_VALIDATION_20260715.md
Normal file
@@ -0,0 +1,124 @@
|
||||
# LegacyParityApp 이관 검증 증적 — 2026-07-15
|
||||
|
||||
이 문서는 현재 `MBN_STOCK_WEBVIEW.LegacyParityApp`의 구현·통합 최종 검증 결과를
|
||||
한곳에 모은다. 원본
|
||||
`C:\Users\MD\source\repos\MBN_STOCK_N`은 모든 조사와 테스트에서 읽기 전용으로
|
||||
취급했다.
|
||||
|
||||
## 제품 형태와 권위 경계
|
||||
|
||||
| 항목 | 결과 |
|
||||
|---|---|
|
||||
| 제품 형태 | `.NET 8 / C# / WinUI 3 / Windows App SDK 1.8 / WebView2 / x64 / MSIX` Windows 데스크톱 앱 |
|
||||
| 웹사이트 여부 | 웹사이트가 아님. WinUI 3 프로세스가 패키지의 로컬 Web 자산을 WebView2로 표시 |
|
||||
| 업무 권위 | `LegacyOperatorController`의 C# 상태와 검증된 DTO가 권위 |
|
||||
| Web 경계 | `LegacyBridgeProtocol`의 strict JSON intent allowlist; Web은 DB identity, scene alias, playlist 또는 송출 상태를 확정하지 않음 |
|
||||
| 벤더 경계 | COM/K3D/Tornado 구현은 `IPlayoutEngine` 뒤에 격리하고 STA 큐와 안전 게이트 유지 |
|
||||
| 기본 송출 모드 | `DryRun`; Live는 별도 개발 설정, 프로세스별 승인, allowlist와 명령 예산 필요 |
|
||||
| 불명확 결과 | timeout/`OutcomeUnknown`에서 자동 재시도·반대 명령·추측 cleanup 금지 |
|
||||
|
||||
## 원본 UI와 클릭 이벤트
|
||||
|
||||
| 범위 | 현재 증적 |
|
||||
|---|---|
|
||||
| 업무 탭 | 원본 10개 탭, identity 기반 활성화와 순서 변경 |
|
||||
| 종목 영역 | 모든 탭에서 유지되는 종목 검색, 결과, 31개 컷과 playlist 추가 |
|
||||
| 고정/업종/비교 | fixed 328개, 업종 44개, 비교 대상 24개와 action 9개를 폐쇄형 inventory로 고정 |
|
||||
| 독립 클릭 | UC4 테마, UC6 전문가, UC7 거래정지의 원본 단일 클릭과 UC4/UC6 행별 독립 처리 |
|
||||
| 전용 입력 | GraphE, FSell, VI, PList, AList, ThemeA, EList를 WebView 화면과 C# intent로 연결 |
|
||||
| 해외 | UC5 해외업종·해외종목·해외지수 조회와 playlist 생성 |
|
||||
| playlist | 단일/Ctrl/Shift 선택, enabled, 위/아래, 선택/전체 삭제, drag/batch 경계 |
|
||||
| FarPoint 대체 | Web table로 표현하되 원본 클릭·더블클릭·정렬·이동·저장 이벤트 의미 유지 |
|
||||
|
||||
상세 이벤트 대조는 [`OPERATOR_UI_PARITY.md`](OPERATOR_UI_PARITY.md)를 따른다.
|
||||
|
||||
## 송출 흐름과 장면
|
||||
|
||||
| 항목 | 결과 |
|
||||
|---|---|
|
||||
| 원클릭 TAKE IN | idle에서 보이는 `TAKE IN`/F8 한 번으로 `PrepareAndTakeInAsync`가 page load 1회, PREPARE 1회, TAKE IN 1회를 같은 게이트에서 수행 |
|
||||
| native PREPARE | 엔지니어링 진단용 control/intent는 유지하되 운영자 UI에서는 `hidden` |
|
||||
| 실패 경계 | PREPARE 후 TAKE IN 실패/unknown 시 prepared state 보존, 자동 재시도 없음 |
|
||||
| 장면 기준선 | 원본 builder **35/35** inventory와 mutation/runtime 자동 기준선 통과 |
|
||||
| active alias | **45개** alias의 대응 `.t2s`가 승인된 읽기 전용 Cuts에 존재 |
|
||||
| PageN | 5·6·12개 단위, 최대 20페이지, 경계·마지막 부분 페이지·21페이지 거부 테스트 |
|
||||
| NEXT | 같은 scene의 Page NEXT와 다음 enabled playlist 행 NEXT를 원본 조건으로 구분 |
|
||||
|
||||
## 실제 DB 검증
|
||||
|
||||
| 검증 | 결과 |
|
||||
|---|---|
|
||||
| Oracle/MariaDB read-only | **PASS** — 필수 scene selector 33/33, 전체 query 58건 |
|
||||
| GraphE write | **PASS** — `INPUT_PIE`, `INPUT_GROW`, `INPUT_SELL`, `INPUT_PROFIT` 각 create, fresh readback, delete, absence |
|
||||
| PList/AList write | **PASS** — named playlist 정의·항목 create/save, fresh readback, delete, absence |
|
||||
| ThemeA KRX write | **PASS** — create, preview/readback, delete, absence |
|
||||
| ThemeA NXT write | **PASS** — create, preview/readback, delete, absence |
|
||||
| EList write | **PASS** — expert create, preview/readback, delete, absence |
|
||||
|
||||
개발 DB write는 총 9개 충돌 격리 시나리오다. production service/executor를 사용했으며
|
||||
인증정보나 DB 원문을 출력하지 않았다. write 결과가 불명확한 경우에는 재시도와 cleanup을
|
||||
하지 않는 구현·테스트 경계를 유지한다.
|
||||
|
||||
## 외부자산 판정
|
||||
|
||||
| 자산 | 상태 | 판정 |
|
||||
|---|---|---|
|
||||
| 45 active alias `.t2s` | 존재 | alias 준비 완료 |
|
||||
| `s5006` `Video\큐브배경.vrv` | 1개 없음 | 외부자산 미제공; 코드 실패 아님 |
|
||||
| `s6001` 국가별 영상 | 13개 없음 | 외부자산 미제공; 코드 실패 아님 |
|
||||
| sibling `배경\기본.vrv` | 없음 | 외부자산 미제공; F2/F3/default background는 fail closed |
|
||||
|
||||
누락 자산은 Git 또는 MSIX에 임의로 넣지 않는다. 제공되면 같은 trusted-root,
|
||||
0-byte, reparse, root-escape 검사를 통과한 뒤 해당 scene만 다시 검증한다.
|
||||
|
||||
## 개발 Tornado2와 PGM 실측
|
||||
|
||||
2026-07-15 개발 Tornado2 `2.7.5.49 x64 DSX10.0 S.R2`의 단일 `30001/tcp`
|
||||
listener와 K3D x64 등록·라이선스를 고정한 뒤, 설치된 MSIX package-context 앱에서 다음
|
||||
시퀀스를 실행했다.
|
||||
|
||||
1. 삼성전자 검색 → `5001` playlist 추가
|
||||
2. 원본과 같은 fixed leaf **더블클릭**으로 VI 수동 목록 9종목 조회 → `5074` 2페이지 추가
|
||||
3. `PREPARE 5001 page 1` → `TAKE IN` → `OnScenePlayed` → 자동 갱신 정확히 1회
|
||||
4. `NEXT`로 다음 playlist 항목 `5074 page 1/2` → 자동 갱신 정확히 1회
|
||||
5. 같은 scene `Page NEXT`로 `5074 page 2/2`의 마지막 4종목 송출
|
||||
6. `TAKE OUT All` → `IDLE` → 정상 종료 확인 → `BYE`
|
||||
|
||||
첫 실측과 화면 증적용 known-outcome 재현은 모두 `OutcomeUnknown=false`로 끝났다. 불명확
|
||||
결과 뒤 명령 재시도는 없었다. Network Monitoring 최종 원문은 1,863줄,
|
||||
299,616문자, SHA-256
|
||||
`7CDB183A2518EF47E5CC3819C2EB14C06492DE0C8E018350A0633E0FBBB5B994`다. 두 실측을
|
||||
합쳐 `LOAD_SCENE` 4/4, `BEGIN_TRANSACTION` 12/12, `VALUE_SET` 258/258,
|
||||
`VISIBLE` 568/568, `SCENE_PREPARE` 12/12, `PLAY` 10/10, `STOPAL` 2/2,
|
||||
`UNLOAD_SCENE` 4/4가 요청/성공으로 일치했고 `SCENE_PLAYED` 10회를 수신했다. 정상
|
||||
종료에서는 `BYE` 요청 1회가 기록됐고 이후 established 연결과 앱/CDP process는 0개다.
|
||||
|
||||
화면 증적은 300프레임 중 72프레임이 검은 기준 화면과 달랐다. `5001` 삼성전자 화면,
|
||||
`5074` page 1의 5종목, page 2의 4종목을 직접 확인했고 TAKE OUT 뒤 기준 검은 화면으로
|
||||
복귀했다. 프레임 원본과 Network Monitoring 원문은 실제 컷·운영자료를 Git에 넣지 않는
|
||||
정책에 따라 로컬 `artifacts/final-validation-20260715`에만 보관한다.
|
||||
|
||||
## 최종 전체 회귀 기록
|
||||
|
||||
아래 값은 모든 병렬 변경과 fixed-leaf 더블클릭 회귀 테스트가 합쳐진 뒤 다시 실행한
|
||||
최종값이다.
|
||||
|
||||
| 항목 | 최종 결과 |
|
||||
|---|---|
|
||||
| Debug x64 전체 빌드·테스트 | **PASS** — .NET 2,394/2,394, VS 2026 `ParityDebug|x64`, 경고 0·오류 0 |
|
||||
| Release x64 전체 빌드·테스트 | **PASS** — .NET 2,394/2,394, VS 2026 `Release|x64`, 경고 0·오류 0 |
|
||||
| Web/LegacyParity 전체 테스트 | **PASS** — 47파일, 435/435 (`Test-WebPlayout` 415 + thin-client 20) |
|
||||
| Release x64 MSIX 경로·버전 | `artifacts/final-validation-20260715/package/MBN_STOCK_WEBVIEW.LegacyParityApp_0.1.0.0_x64_Test/MBN_STOCK_WEBVIEW.LegacyParityApp_0.1.0.0_x64.msix`, `0.1.0.0`, 15,200,545 bytes |
|
||||
| 새 MSIX SHA-256·서명 | `FC4EEE1509675E643D2B4CB435E279CAC4611B28534048D9B9EE611E361E0E1F`, **Valid**, `CN=Comtrophy` |
|
||||
| 설치된 package-context 실행 | **PASS** — `Wickedness.MBNStockWebView.LegacyParity_0.1.0.0_x64__qbv3jkvsn3aj0`, DryRun 전체 흐름과 Live 개발 PGM 실행, 정상 종료 |
|
||||
| 개발 Tornado2 Network Monitoring | **PASS** — HELLO, transaction/mutation/query/prepare/play, event, unload, STOPAL, BYE와 연결 해제 확인 |
|
||||
| 개발 PGM PREPARE → TAKE IN → NEXT/Page NEXT → TAKE OUT | **PASS** — `5001` → `5074` 1/2 → `5074` 2/2 → 검은 화면 복귀 |
|
||||
| Gitea main commit/push | **PASS** — 이 최종 검증 문서를 포함한 커밋을 `origin/main`에 fast-forward push |
|
||||
|
||||
## 판정 경계
|
||||
|
||||
- 35/35 자동 장면 동등성은 모든 장면의 실제 PGM 출력 완료를 뜻하지 않는다.
|
||||
- 33/33 DB selector 통과는 외부 영상/배경 자산 준비를 뜻하지 않는다.
|
||||
- 개발 DB 9개 write 시나리오 통과는 운영 DB에 쓰기를 허용하거나 수행했다는 뜻이 아니다.
|
||||
- 개발 Tornado2/PGM은 안전 게이트를 유지한 별도 통합 검증 대상이다.
|
||||
- 원본 저장소, 실제 Cuts, 라이선스와 인증정보는 계속 읽기 전용 또는 Git 외부로 유지한다.
|
||||
@@ -1,288 +1,175 @@
|
||||
# 운영자 UI 동등성 인벤토리
|
||||
|
||||
## 문서 목적과 판정 기준
|
||||
기준일: 2026-07-15
|
||||
|
||||
이 문서는 원본 WinForms 운영 화면의 사용자 동작을 새 WinUI 3/WebView 구현에 1:1로 연결한다. 장면 builder가 존재하는 것, 네이티브 bridge와 workflow 모듈이 존재하는 것, 실제 Web 화면에서 운영자가 사용할 수 있는 것은 서로 다른 완료 조건으로 취급한다.
|
||||
## 제품과 판정 기준
|
||||
|
||||
상태 표기는 다음과 같다. 아래 기존 행의 `화면 통합 완료` 표현도 `화면/계약 구현`과 같은 구조적 도달성만 뜻하며, 운영 동등성 완료로 읽지 않는다.
|
||||
현재 이관 앱은 웹사이트가 아니라 `.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에 없는 필드·값·명령을 거부한다.
|
||||
|
||||
이 문서의 판정은 다음을 구분한다.
|
||||
|
||||
| 판정 | 의미 |
|
||||
|---|---|
|
||||
| 화면/계약 구현 | 현재 `Web/index.html`과 `Web/app.js`에서 검색·선택·플레이리스트 추가 또는 저장 동작에 도달하며 native bridge·workflow 계약이 있다. |
|
||||
| 화면/계약 구현 / 실환경 검증 대기 | 화면, native bridge, workflow와 자동 테스트까지 연결됐지만 운영 DB write, 기존 데이터 identity/문자 복원 또는 해당 경로의 실제 PGM 확인을 수행하지 않았다. |
|
||||
| 운영 동등성 일부 검증 | 승인된 제한 데이터·scene·회차만 실제 DB/PGM으로 확인했다. 확인하지 않은 범위로 확대하지 않는다. |
|
||||
| 운영 동등성 완료 | 원본과 같은 입력·저장·재조회·identity·PGM 결과를 해당 기능 범위 전체에서 확인한 상태다. 현재 전체 UI에 이 판정을 적용하지 않는다. |
|
||||
| UI/이벤트 이관 | 원본 컨트롤과 클릭 이벤트가 실제 LegacyParityApp 화면 → strict bridge → C# workflow로 연결됨 |
|
||||
| 자동 동등성 | 원본 inventory·DTO·mutation·호출 순서·경계값을 자동 테스트로 비교함 |
|
||||
| 개발 DB 통합 | 실제 개발 Oracle/MariaDB에서 create/readback/delete/absence를 확인함 |
|
||||
| 개발 PGM 통합 | 개발 Tornado2 Network Monitoring과 PGM 화면을 함께 확인함 |
|
||||
| 외부자산 필요 | 코드 경로는 있으나 Git/MSIX 밖의 실제 영상·배경이 제공되지 않음 |
|
||||
|
||||
`DB-W`는 운영 데이터베이스에 실제 쓰기를 수행해 확인했는지를 뜻한다. `PGM`은 실제 Tornado2 PGM 출력으로 해당 UI 경로를 확인했는지를 뜻한다. fake executor, DryRun, SQL 계약 테스트는 자동 테스트 근거이지 실제 DB-W 또는 PGM 검증으로 계산하지 않는다.
|
||||
## 원본 보존
|
||||
|
||||
기능별 미적용·적용 중·외부자산 필요 판정과 완료에 필요한 증거는 [`LEGACY_FEATURE_AUDIT.md`](LEGACY_FEATURE_AUDIT.md)를 따른다.
|
||||
- 원본 `C:\Users\MD\source\repos\MBN_STOCK_N`은 읽기 전용으로만 분석했다.
|
||||
- 원본 `Cuts`도 읽기 전용 검증에만 사용하며 수정·복사하지 않는다.
|
||||
- 비밀번호, 라이선스, 벤더 DLL, 인증서, 실제 컷·영상·배경과 로컬 설정은 Git에 넣지 않는다.
|
||||
|
||||
## 원본 보존 상태
|
||||
## 원본 실행 action inventory
|
||||
|
||||
- 원본 저장소는 읽기 전용으로만 분석했고 파일을 수정하지 않았다.
|
||||
- 분석 시작 시 원본에 이미 존재하던 dirty 파일은 `MBN_STOCK_N/MainForm.cs`, `MBN_STOCK_N/RES/MmoneyCoder.ini` 두 개였다. 두 파일의 기존 변경을 그대로 보존했다.
|
||||
- 이 문서에는 비밀번호, 실제 방송 데이터, 자산·경로 해시를 기록하지 않는다.
|
||||
동적 테마·전문가 DB 행과 전용 편집기의 데이터 행은 아래 고정 수량에 중복해 더하지 않는다.
|
||||
|
||||
## 412개 실행 action의 고정 분해
|
||||
| 집합 | 원본 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 기반 활성화·순서 변경 | 이관 |
|
||||
|
||||
412는 다음 네 집합의 합이다. 동적 테마·전문가 목록과 GraphE 편집 행 수는 이 숫자에 중복해 더하지 않는다.
|
||||
328개 fixed action 중 수동 전제 6개는 임의 DTO로 만들지 않는다. 개인·외국인·기관
|
||||
순매도 3개는 FSell의 신뢰된 5행 입력으로, 5단·6종목·12종목 트리의 VI placeholder 3개는
|
||||
원본 VIList가 만든 `s5074/5074` 5개 단위 PageN으로만 활성화한다.
|
||||
|
||||
| 집합 | 원본 UI | 수량 | 새 구현 | 자동 근거 | 상태 |
|
||||
|---|---|---:|---|---|---|
|
||||
| 고정 action | `MainForm`의 해외·환율·지수 탭과 `Control/UC1.cs` | 328 | `Web/legacy-fixed-workflow.js` | `tests/Web/legacy-fixed-workflow.test.cjs`가 328개, 직접 생성 가능한 322개, 수동 전제 6개를 고정한다. | 화면 통합 완료. 수동 6개는 아래 대체 경계를 통과해야 한다. |
|
||||
| 업종 action | `Control/UC1.cs` + `Control/UC2.cs`의 코스피·코스닥 | 44 | `Web/industry-workflow.js`의 시장별 22개 | `tests/Web/industry-workflow.test.cjs` | 화면 통합 완료 |
|
||||
| 종목 action | `MainForm` 좌측 종목 검색·컷 목록 | 31 | `Web/operator-workflow.js` | `tests/Web/operator-workflow.test.cjs`가 실행 자산 순서의 31개를 고정한다. | 화면 통합 완료 |
|
||||
| 비교 action | `Control/UC1.cs` + `Control/UC3.cs` | 9 | `Web/comparison-workflow.js` | `tests/Web/comparison-workflow.test.cjs`가 24개 비교 대상과 9개 action을 별도로 고정한다. | 화면 통합 완료 |
|
||||
| 합계 | | **412** | | | |
|
||||
## 화면 단위 현황
|
||||
|
||||
고정 action 328개 안의 수동 전제 6개는 일반 고정 DTO로 임의 생성하지 않는다.
|
||||
|
||||
| 원본 고정 항목 | 수량 | 원본 전용 폼 | 새 대체 경계 | 관계 |
|
||||
|---|---:|---|---|---|
|
||||
| 개인·외국인·기관 순매도 상위(수동) | 3 | `Form/FSell.cs` | `MainWindow.ManualLists.cs`, `Web/manual-lists-workflow.js`, 신뢰된 s5025 파일 저장소 | 각 audience의 검증된 5행 입력이 있어야 `s5025/5025` 플레이리스트 항목을 만든다. |
|
||||
| 5단·6종목·12종목 섹션의 VI 발동(수동) placeholder | 3 | `Form/VIList.cs` | `MainWindow.ManualLists.cs`, `Web/manual-lists-workflow.js`, 신뢰된 VI 저장소 | 세 placeholder 모두 원본 VIList가 만든 하나의 `s5074/5074` 5행 페이지 결과로 대체된다. 6·12행 장면으로 바꾸지 않는다. |
|
||||
|
||||
현재 이 수동 6개는 FSell/VIList 대체 화면까지 통합됐다. 고정 카탈로그에서 임의 DTO로 직접 활성화하지 않고, 전용 입력 화면이 저장 후 재조회한 신뢰 데이터와 버전을 공급한 뒤에만 플레이리스트에 넣는다.
|
||||
|
||||
## 화면 단위 완료 요약
|
||||
|
||||
| 원본 화면 | 핵심 역할 | 화면/계약 구현 상태 | 운영 동등성 DB-W | 운영 동등성 PGM |
|
||||
| 원본 화면 | 이관된 핵심 동작 | 현재 구현 | 개발 DB/파일 검증 | 개발 PGM/자산 경계 |
|
||||
|---|---|---|---|---|
|
||||
| MainForm | 종목 검색, 31개 컷, playlist, 송출·배경·저장 | 화면/계약 구현. sibling `배경` root 코드는 적용됐고 실제 외부 배경 자산·package 검증 대기 | 없음/해당 없음 | 일부: 승인된 `5001`, `5074`, TAKE OUT |
|
||||
| UC1 | 328 fixed, 업종·비교 action 트리 | 412 action과 수동 6개 전용 편집 화면/계약 구현 | 해당 없음 | 일부: generic `5074`; 전체 action 미검증 |
|
||||
| UC2 | 업종 목록과 두 업종 선택 | 화면/계약 구현 | 해당 없음 | 미검증 |
|
||||
| UC3 | 비교 대상·저장 비교쌍·9 action | 화면/계약과 strict `종목비교.dat` one-time importer 구현 | importer는 read-only이며 DB-W 없음 | signed package에서 8쌍 1회 실행·재시작 영속성 통과; PGM 미검증 |
|
||||
| UC4 | 테마 검색·preview·5/6/12행 항목 | 조회/선택 및 ThemeA CRUD 화면/identity 계약 구현 | CRUD 미실행; 실제 KRX/NXT identity 미검증 | 미검증 |
|
||||
| UC5 | 해외업종·해외종목·해외지수 | 화면/계약 구현 | 해당 없음 | 미검증; `s6001` 국가 영상 13개 외부자산 필요 |
|
||||
| UC6 | 전문가 조회·추천 preview·5행 항목 | 조회/선택 및 EList/추천 CRUD 화면/identity 계약 구현 | CRUD 미실행; 실제 identity 미검증 | 미검증 |
|
||||
| UC7 | 거래정지 검색·현재/120일 캔들 | 화면/계약 구현 | 해당 없음 | 미검증 |
|
||||
| GraphE | 4개 수동 재무 CRUD와 playlist 추가 | 화면/계약 구현 / 실환경 검증 대기 | 미실행 | 미검증 |
|
||||
| FSell | 개인·외국인·기관 수동 순매도 5행 | 화면/계약, 원본 importer와 production parser 구현 | 운영 데이터 write 미실행 | 현재 3 audience×5행 read-only 검증; 기존 파일 불변 |
|
||||
| VIList | 수동 VI 목록·순서·5개 단위 page | 화면/계약, 원본 importer와 versioned production parser 구현 | 운영 데이터 write 미실행 | 현재 9항목·2 page read-only 검증; 기존 파일 불변 |
|
||||
| PList | 이름 있는 playlist 목록·저장·불러오기·삭제 | 화면/계약과 한국어 legacy/KRX/NXT/해외종목/manual restore 구현 | 운영 DB write 미실행 | 2,213행 무손상, 통합 selection 1,603/차단 610; scene/PGM 별도 |
|
||||
| AList | 프로그램 코드·이름 생성 | PList 화면/계약에 통합 | 운영 DB write 미실행 | 해당 없음 |
|
||||
| ThemeA | KRX/NXT 테마 구성 CRUD | 화면/identity 계약 구현 / 실환경 검증 대기 | 미실행 | 미검증 |
|
||||
| EList | 전문가 생성과 추천 구성 CRUD | 화면/identity 계약 구현 / 실환경 검증 대기 | 미실행 | 미검증 |
|
||||
| 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` | 개발 DB KRX/NXT 각 1개 PASS | scene PGM 별도 |
|
||||
| EList | 전문가 생성·편집·삭제, 추천 종목·금액·순서 | `LegacyOperatorCatalogWorkflow` | 개발 DB expert 1개 PASS | scene PGM 별도 |
|
||||
|
||||
## MainForm 매핑
|
||||
UC4/UC6의 결과 행은 고정 action 트리의 click handler에 버블링하지 않는다. UC4 테마,
|
||||
UC6 전문가와 UC7 거래정지는 원본처럼 **단일 클릭**으로 선택/playlist 동작을 수행하고,
|
||||
fixed/industry/comparison 트리의 원본 double-click과 독립적으로 처리한다.
|
||||
|
||||
원본 파일은 `MainForm.cs`와 `MainForm.Designer.cs`다.
|
||||
## MainForm 이벤트 매핑
|
||||
|
||||
| 원본 이벤트·컨트롤 | 원본 동작과 입력/저장 경계 | 새 구현 파일·bridge/workflow | 현재 상태 | 자동 테스트 근거 | DB-W / PGM |
|
||||
|---|---|---|---|---|---|
|
||||
| `Form1_Load`, `tabControl1_SelectedIndexChanged` | 실행 자산을 읽고 UC1~UC7을 탭에 배치하며 Tornado에 연결한다. | `MainWindow.xaml.cs`, `MainWindow.Playout.cs`, `Web/index.html`, `Web/app.js` | 화면 통합 완료 | `LegacyWebWorkflowIntegrationTests.cs`, `PlayoutBridgeProtocolTests.cs`, Web 전체 회귀 | DB-W 해당 없음 / PGM 일부 완료 |
|
||||
| `tab_MouseDown`/`tab_MouseMove`/`tab_DragOver`, `swapTabPages` | 원본 10개 업무 탭의 표시 순서를 drag 중 identity 단위로 교환한다. | `Web/market-nav-reorder.js`; dashboard 고정; `data-market` 기반 drag와 Alt+위/아래; 세션 한정 | 코드 적용 / signed package 검증 대기. active view·DB 요청·playlist 상태를 변경하지 않음 | 전용 10/10, 전체 Web 353/353, Debug/Release x64 build | DB-W·PGM 해당 없음 |
|
||||
| `button9_Click`, `txtSearch_KeyPress`, `searchStock`, `jongmokCode` | 코스피·코스닥·NXT 두 시장씩 검색하고 이름, 코드, 시장을 선택한다. | `src/MBN_STOCK_WEBVIEW.Core/Data/StockSearch.cs`; native `search-stocks`; `Web/operator-workflow.js` | 화면 통합 완료 | `LegacyStockSearchServiceTests.cs`, `operator-workflow.test.cjs` | DB-W 없음 / `5001` 경로 일부 PGM 완료 |
|
||||
| `listView1_*`, `GridView1_*`, `SetPlayList`, `PlayList_DragDrop` | 실행 자산의 종목 컷을 선택·드래그해 enabled, 시장, 종목, 종류, 세부사항, page, 종목코드 행을 만든다. | `Web/operator-workflow.js`, `Web/app.js`의 종목 workflow와 표준 playlist entry | 화면 통합 완료 | `operator-workflow.test.cjs`, `candle-options-workflow.test.cjs` | DB-W 없음 / 전체 31개 PGM 미검증 |
|
||||
| `b1_Click` (`주요매출 구성`, `성장성 지표`, `매출액`, `영업이익`) | `GraphE` 네 모드를 연다. | `MainWindow.ManualFinancial.cs`, `Web/manual-financial-workflow.js`, `Web/manual-financial-ui.js` | 화면 통합 완료 / 실환경 검증 대기 | GraphE 표 참조 | DB-W 없음 / PGM 없음 |
|
||||
| `PlayList_CellClick`, `PlayList_KeyPress`, `checkBox1_CheckedChanged`, `btnup_Click`, `btndown_Click`, `btndelete_Click`, `btntdelete_Click` | 단일·Ctrl·Shift 선택, Space/전체 송출 체크, 순서 이동, 선택·전체 삭제. 송출 중 이동·전체 삭제를 막는다. | `Web/app.js`의 playlist 상태, snapshot lock, `Web/playout-safety.js` | 화면 통합 완료 | `playout-safety.test.cjs`, named/각 workflow의 trusted restore 테스트 | DB-W 없음 / PGM 제어 일부 완료 |
|
||||
| `button5_Click`, `btnload_Click` | `PList`를 열어 이름 있는 DB 플레이리스트를 저장·불러온다. | `MainWindow.NamedPlaylists.cs`, `src/MBN_STOCK_WEBVIEW.Core/Data/NamedPlaylistPersistence.cs`, `src/MBN_STOCK_WEBVIEW.Infrastructure/Execution/OracleNamedPlaylistMutationExecutor.cs`, `Web/named-playlist-workflow.js` | 화면/계약 구현. 기존 한국어 행과 manual 항목의 실DB fresh restore는 적용 중 | named playlist Core/Infrastructure/Web/bridge 테스트 | DB-W 없음 / 복원 항목별 PGM 없음 |
|
||||
| `btnPrepare_Click`, `btnTake_Click`/F8, `btnNext_Click`, `btnTakeout_Click`/Esc | PREPARE, TAKE IN, 같은 장면 page NEXT 또는 다음 행 NEXT, StopAll 기반 TAKE OUT. | `MainWindow.Playout.cs`; `IPlayoutEngine`; native `playout-command`, `request-playlist-page-plans`; `Web/playout-safety.js` | 화면 통합 완료 | playout/Core/PageN/PGM sequence 테스트와 `playout-safety.test.cjs` | DB-W 해당 없음 / 승인된 `5001`, `5074` page 1·2 및 TAKE OUT만 PGM 확인 |
|
||||
| `btnback_Click`/F2, `ckback_CheckedChanged`/F3 | 허용 배경 파일 선택, 배경 없음 토글, 송출 중 변경 금지. | `MainWindow.Background.cs`; 별도 `LegacyBackgroundDirectory`; opened-handle/hardlink/reparse 검증; native `request-background-status`, `choose-background`, `toggle-background`; Web 배경 컨트롤 | sibling `배경` root와 `기본.vrv` 코드 적용. 현재 원본 외부 폴더가 없어 fail closed이며 package/PGM 검증 대기 | 별도 root 내부/escape/reparse/hardlink/opened-handle/누락 fixture, Core·Playout Debug/Release 전부 통과, 앱 Debug/Release build | DB-W 해당 없음 / 기능별 PGM 미검증 |
|
||||
| `timer1_Tick`, `timer2_Tick`, `timer3_Tick`, `MainForm_FormClosing`, `MainForm_Resize`, keyboard handlers | 자동 갱신, 상태 표시, 종료 정리, 단축키를 담당한다. | playout refresh scheduler, cancellation/lifetime hooks, Web 상태·단축키 | 화면 통합 완료 | refresh epoch/scheduler, playout safety, bridge 테스트 | DB-W 해당 없음 / 일부 PGM 완료 |
|
||||
|
||||
실제 PGM 완료는 이미 승인된 제한 회차의 `5001`과 `5074` 흐름만 뜻한다. 412개 전체나 아래 편집 화면의 실제 PGM 동등성을 뜻하지 않는다.
|
||||
|
||||
## UC1~UC7 매핑
|
||||
|
||||
### UC1 — 해외·환율·지수·업종·비교 action 트리
|
||||
|
||||
원본: `Control/UC1.cs`, `Control/UC1.Designer.cs`
|
||||
|
||||
| 원본 이벤트·버튼 | 원본 경계 | 새 구현 | 상태·검증 |
|
||||
|---|---|---|---|
|
||||
| `UC1_Load`, `INI_parser` | 탭별 실행 자산을 읽어 category/leaf TreeView를 구성한다. | `legacy-fixed-workflow.js`, `industry-workflow.js`, `comparison-workflow.js`의 폐쇄형 inventory | 화면 통합 완료. 각 Web 테스트가 수량·중복·매핑을 고정한다. |
|
||||
| `chE_CheckedChanged`, `treeView_BeforeSelect`, `treeView_BeforeExpand/BeforeCollapse`, `treeView_DrawNode`, `treeView_MouseDown` | 전체 expand/collapse와 트리 표시를 제어한다. | Web category/section 렌더링과 펼침 상태 | 화면 통합 완료. 시각 상호작용이며 DB-W/PGM 대상 아님. |
|
||||
| `treeView_DoubleClick` | category 전체 또는 leaf를 playlist에 넣고 5·6·12개 PageN, NXT 제한, 업종 쌍, 비교 쌍을 계산한다. FSell·VI 수동 항목이면 전용 폼으로 분기한다. | 고정 328, 업종 44, 비교 9 workflow의 `create*PlaylistEntry`; page plan bridge; 수동 6개는 `manual-lists-workflow.js`와 `manual-lists-ui.js`로 분리 | 전체 화면 통합 완료. 실제 DB-W 없음, 전체 PGM 미검증. |
|
||||
| `treeView_AfterSelect`, `btnswap_Click` | 원본에서 실질 동작이 없거나 주석 처리된 이벤트다. | 별도 기능 없음 | 의도적으로 추가 동작을 만들지 않는다. |
|
||||
|
||||
### UC2 — 코스피·코스닥 업종 선택과 쌍
|
||||
|
||||
원본: `Control/UC2.cs`, `Control/UC2.Designer.cs`
|
||||
|
||||
| 원본 이벤트·버튼 | 원본 경계 | 새 구현·bridge | 상태·검증 |
|
||||
|---|---|---|---|
|
||||
| `UC2_Load` | 코스피/코스닥 업종 이름·코드를 DB에서 읽는다. | `src/MBN_STOCK_WEBVIEW.Core/Data/IndustrySelection.cs`; native `request-industries` | 화면 통합 완료. `LegacyIndustrySelectionServiceTests.cs`, `industry-workflow.test.cjs`. |
|
||||
| `listbox_DoubleClick`, `btnswap_Click` | 선택 업종을 두 슬롯에 회전 입력하고 순서를 바꾼다. | `Web/industry-workflow.js`의 selected/primary/secondary 입력 | 화면 통합 완료. 시장 혼합과 빈 슬롯을 fail closed 한다. |
|
||||
| UC1 업종 leaf 실행 | 단일, 2열, 수익률 5종, 캔들 12종, 시장 전체 3종으로 시장별 22개를 만든다. | `industry-workflow.js` | 44개 화면 통합 완료. 실제 DB-W 없음 / 업종별 PGM 없음. |
|
||||
|
||||
### UC3 — 비교 대상·저장 비교쌍
|
||||
|
||||
원본: `Control/UC3.cs`, `Control/UC3.Designer.cs`
|
||||
|
||||
| 원본 이벤트·버튼 | 원본 경계 | 새 구현·bridge | 상태·검증 |
|
||||
|---|---|---|---|
|
||||
| `UC3_Load`, `Data_Load` | 24개 고정 비교 대상을 읽고 로컬 `종목비교.dat`의 저장쌍을 불러온다. | `Web/comparison-workflow.js`; 24개 typed target; schema-versioned pair list; `LegacyComparisonPairImport`의 고정 read-only source와 strict CP949 9필드 importer | signed package에서 실제 DB identity를 다시 읽어 8쌍을 한 번 가져왔고, 정상 재시작 뒤 private marker·순서를 확인했다. 원본을 계속 참조하지 않는다. |
|
||||
| `btnsearch_Click`, `listBox_DoubleClick`, `listBox1_DoubleClick`, `btnsearch2_Click`, `FpSpread2_CellDoubleClick` | 국내 종목, 24개 시장 대상, 해외 종목을 두 슬롯에 회전 입력한다. | native `search-stocks`, `search-world-stocks`; comparison target normalizer | 화면 통합 완료. `comparison-workflow.test.cjs`가 회전·시장·해외 identity를 검증한다. |
|
||||
| `btnswap_Click`, `btnAdd_Click`, `btn1/2/3/4_Click`, `File_Save` | 쌍 교환, 추가, 선택/전체 삭제, 위/아래 이동 후 저장한다. | comparison pair append/delete/delete-all/reorder와 Web storage; explicit legacy import button | 화면/계약 구현. 기존 CP949 파일은 exact 9필드·fresh DB identity·중복·500쌍 경계를 모두 통과해야 전체가 원자적으로 저장된다. 실제 8쌍 import와 재시작 marker를 확인했다. |
|
||||
| UC1 비교 leaf 실행 | 현재/예상 2열, 비교 캔들/라인, 5일·1/3/6/12개월 수익률의 9개 action | `comparison-workflow.js` | 9개 화면 통합 완료. 실제 DB-W 없음 / 비교 PGM 없음. |
|
||||
|
||||
### UC4 — 테마 검색·미리보기·송출 선택
|
||||
|
||||
원본: `Control/UC4.cs`, `Control/UC4.Designer.cs`
|
||||
|
||||
| 원본 이벤트·버튼 | 원본 경계 | 새 구현·bridge | 상태·검증 |
|
||||
|---|---|---|---|
|
||||
| `btn1_Click`, `GetThemeByKeyword` | KRX Oracle과 NXT 저장소에서 테마명·코드를 검색한다. | `src/MBN_STOCK_WEBVIEW.Core/Data/ThemeSelection.cs`; native `search-themes`; `Web/theme-workflow.js` | 검색 화면 통합 완료. `LegacyThemeSelectionServiceTests.cs`, `theme-workflow.test.cjs`. |
|
||||
| `FpSpread1_CellClick` | 선택 테마의 구성 종목과 입력 순서를 DB에서 읽고 중지 종목을 제외한다. | native `request-theme-preview`와 typed preview | 화면 통합 완료. 중복 key·시장 불일치·truncated preview를 거부한다. |
|
||||
| `btnnow_Click`, `btnpre_Click`, `btnnow_6_Click`, `btnnow_12_Click`, double-click | 5단 현재/예상, 6종목 현재, 12종목 현재를 입력순·상승률·하락률로 만들고 5/6/12 page 수를 계산한다. NXT 예상은 차단한다. | `theme-workflow.js`의 compatibility, page preview, playlist entry | 화면 통합 완료. 실제 DB-W 없음 / 테마 PGM 없음. |
|
||||
| `btn2_Click`, `btn3_Click`, `btn4_Click` | ThemeA 추가·편집 폼을 열거나 선택 테마와 구성 종목을 삭제한다. | `MainWindow.OperatorCatalogs.cs`, `Web/operator-catalog-workflow.js`, `Web/operator-catalog-ui.js` | ThemeA CRUD 화면 통합 완료. 실제 DB-W/PGM 없음. |
|
||||
|
||||
### UC5 — 해외업종·해외종목·해외지수
|
||||
|
||||
원본: `Control/UC5.cs`, `Control/UC5.Designer.cs`
|
||||
|
||||
| 원본 이벤트·버튼 | 원본 경계 | 새 구현·bridge | 상태·검증 |
|
||||
|---|---|---|---|
|
||||
| `UC5_Load` | 고정 해외지수 목록을 표시한다. | `Web/overseas-workflow.js`의 고정 지수 asset | 화면 통합 완료. |
|
||||
| `btnsearch1_Click`, `upjong_search` | 미국 해외업종의 입력명·표시명·symbol을 조회한다. | `src/MBN_STOCK_WEBVIEW.Core/Data/OverseasIndustryIndexSearch.cs`; native `search-overseas-industries` | 화면 통합 완료. `LegacyOverseasIndustryIndexSearchServiceTests.cs`. |
|
||||
| `btnsearch2_Click`, `jongmok_search` | US/TW 해외종목 이름·symbol을 조회한다. | `src/MBN_STOCK_WEBVIEW.Core/Data/WorldStockSearch.cs`; native `search-world-stocks` | 화면 통합 완료. `LegacyWorldStockSearchServiceTests.cs`. |
|
||||
| `btn1_Click`, `Account_Graph` | 업종/종목 현재 1열과 지수·종목 5/20/60/120/240일 캔들을 playlist에 넣는다. | `Web/overseas-workflow.js` | 화면 통합 완료. `overseas-workflow.test.cjs`; 실제 DB-W 없음 / 해외 PGM 없음. |
|
||||
|
||||
### UC6 — 전문가 목록·추천 종목·송출 선택
|
||||
|
||||
원본: `Control/UC6.cs`, `Control/UC6.Designer.cs`
|
||||
|
||||
| 원본 이벤트·버튼 | 원본 경계 | 새 구현·bridge | 상태·검증 |
|
||||
|---|---|---|---|
|
||||
| `UC6_Load`, `Data_Load`, `FpSpread1_CellClick` | `EXPERT_LIST`를 읽고 선택 전문가의 `RECOMMEND_LIST`를 PLAYINDEX 순서로 미리 본다. | `src/MBN_STOCK_WEBVIEW.Core/Data/ExpertSelection.cs`; native `search-experts`, `request-expert-preview`; `Web/expert-workflow.js` | 검색·선택 화면 통합 완료. `LegacyExpertSelectionServiceTests.cs`, `expert-workflow.test.cjs`. |
|
||||
| `btlistadd_Click` | 선택 전문가를 5행 page, 최대 20 page의 전문가 추천 항목으로 만든다. | `expert-workflow.js`의 native-normalized preview와 playlist entry | 화면 통합 완료. 실제 DB-W 없음 / 전문가 PGM 없음. |
|
||||
| `btnAdd_Click` | EList 신규 전문가 폼을 연다. | operator catalog expert create와 `operator-catalog-ui.js` | EList 신규 UI 통합 완료. |
|
||||
| `btnDelete_Click`, `btndel_Click`, `btnsave_Click`, 종목 검색·double-click | 전문가 삭제, 추천 종목 편집·삭제·재정렬·저장. 원본은 전문가/추천 행을 삭제 후 재삽입한다. | `MainWindow.OperatorCatalogs.cs`, `src/MBN_STOCK_WEBVIEW.Core/Data/ExpertCatalogPersistence.cs`, `src/MBN_STOCK_WEBVIEW.Infrastructure/Execution/OperatorCatalogMutationExecutor.cs`, `Web/operator-catalog-workflow.js`, `Web/operator-catalog-ui.js` | EList/전문가 CRUD 화면 통합 완료. 실제 DB-W/PGM 없음. |
|
||||
|
||||
### UC7 — 거래정지 종목
|
||||
|
||||
원본: `Control/UC7.cs`, `Control/UC7.Designer.cs`
|
||||
|
||||
| 원본 이벤트·버튼 | 원본 경계 | 새 구현·bridge | 상태·검증 |
|
||||
|---|---|---|---|
|
||||
| `btnsearch_Click`, `txts_KeyPress`, `search` | 거래정지 코스피와 코스닥 종목을 빈 검색 또는 부분 검색으로 합친다. | `src/MBN_STOCK_WEBVIEW.Core/Data/TradingHaltSelection.cs`; native `search-trading-halts`; `Web/trading-halt-workflow.js` | 화면 통합 완료. `LegacyTradingHaltSelectionServiceTests.cs`. |
|
||||
| `FpSpread1_CellClick` | 종목명 열 정렬 | Web 정규화 결과의 결정적 정렬 | 화면 통합 완료. |
|
||||
| `btn4_Click` (`현재가`, `120일 캔들`) | 시장·종목 identity로 현재 1열 또는 거래정지 120일 캔들 항목을 만든다. | `trading-halt-workflow.js` | 화면 통합 완료. `trading-halt-workflow.test.cjs`; 실제 DB-W 없음 / PGM 없음. |
|
||||
|
||||
## 전용 Form 매핑
|
||||
|
||||
### GraphE — 네 가지 수동 재무 편집기
|
||||
|
||||
원본: `Form/GraphE.cs`, `Form/GraphE.Designer.cs`
|
||||
|
||||
| 원본 이벤트·버튼 | 원본 입력·저장 경계 | 새 구현·bridge | 상태·검증 |
|
||||
|---|---|---|---|
|
||||
| `GraphE_Load`, `Data_Init`, `FpSpread1_CellClick`, `Data_Load` | 네 테이블 전체/검색 목록을 읽고 선택 행을 편집 DTO로 분해한다. | `src/MBN_STOCK_WEBVIEW.Core/Data/ManualFinancialScreens.cs`; native `request-manual-financial-list`, `request-manual-financial-load`; `Web/manual-financial-ui.js` | 화면 통합 완료 / 실환경 검증 대기 |
|
||||
| 종목 검색, `listBox_DoubleClick`, `upSearch_Click`, `downSearch_Click` | 정확한 종목을 선택하고 테이블 내 이름을 위/아래로 찾는다. | native stock master 재검증과 manual financial list/search | 화면 통합 완료. 이름만으로 시장·코드를 추정하지 않는다. |
|
||||
| `btadd_Click` | 기존 이름이면 update, 아니면 insert. `INPUT_PIE`, `INPUT_GROW`, `INPUT_SELL`, `INPUT_PROFIT`의 폐쇄형 필드를 저장한다. | native `create-manual-financial-record`, `update-manual-financial-record`; `src/MBN_STOCK_WEBVIEW.Infrastructure/Execution/OracleManualFinancialMutationExecutor.cs` | 화면 통합 완료. 단일 트랜잭션, optimistic `rowVersion`, no retry, OutcomeUnknown quarantine 구현. 실제 DB-W 없음. |
|
||||
| `btndel_Click`, `btnall_Click` | 선택 이름 삭제 또는 테이블 전체 삭제 확인 | native `delete-manual-financial-record`, `delete-all-manual-financial-records` | 화면 통합 완료. 실제 DB-W 없음. |
|
||||
| `btnAdd_Click`, row double-click, `Select_Item` | DB에 존재하는 행을 정확한 시장/종목과 결합해 1 page playlist 항목으로 추가한다. | native `request-manual-financial-selection`; `Web/manual-financial-workflow.js` | 화면 통합 완료. 저장 복원본은 fresh load + rowVersion/record 일치 + stock 재검증 + native selection 전까지 PREPARE 불가. PGM 없음. |
|
||||
|
||||
화면별 폐쇄형 매핑은 다음과 같다.
|
||||
|
||||
| 원본 모드 | 테이블 | 입력 | builder/code |
|
||||
|---|---|---|---|
|
||||
| 주요매출 구성 | `INPUT_PIE` | 종목, 기준일, 최대 5개 구성명·비율 | `s5076/5076` |
|
||||
| 성장성 지표 | `INPUT_GROW` | 매출·영업이익·순자산·순이익 증가율 각 4개와 분기 4개 | `s5079/5079` |
|
||||
| 매출액 | `INPUT_SELL` | 분기·금액 6쌍 | `s5080/5080` |
|
||||
| 영업이익 | `INPUT_PROFIT` | 분기·금액 6쌍 | `s5081/5081` |
|
||||
|
||||
자동 근거는 `LegacyManualFinancialScreenServiceTests.cs`, `OracleManualFinancialMutationExecutorTests.cs`, `manual-financial-workflow.test.cjs`, `manual-financial-bridge-integration.test.cjs`다.
|
||||
|
||||
### FSell — 수동 순매도 3종
|
||||
|
||||
원본: `Form/FSell.cs`, `Form/FSell.Designer.cs`
|
||||
|
||||
| 원본 이벤트·버튼 | 원본 입력·저장 경계 | 새 구현·bridge | 상태·검증 |
|
||||
|---|---|---|---|
|
||||
| `FSell_Load`, `data_load`, `ReadListFile` | 개인·외국인·기관별 로컬 파일에서 5행, 행마다 좌/우 종목·금액을 읽는다. | `src/MBN_STOCK_WEBVIEW.Infrastructure/Playout/S5025TrustedManualFileDataSource.cs`의 store/data source; native `request-manual-net-sell-data`; `Web/manual-lists-ui.js` | 화면 통합 완료 / 실환경 검증 대기 |
|
||||
| `btnok_Click`, `WriteListFile` | 정확히 5행을 저장하고 수동 순매도 항목 생성을 허용한다. | native `save-manual-net-sell-data`; `Web/manual-lists-workflow.js` | 화면 통합 완료. 검증된 versioned 데이터만 `s5025/5025`로 변환한다. 실제 파일 write는 자동 테스트 임시 디렉터리에서만 확인, 운영 데이터 write 없음. |
|
||||
| `btncn_Click` | 취소 시 placeholder 행을 유지하지 않는다. | workflow가 저장 성공·신뢰 데이터 없이는 항목을 만들지 않는다. | 화면 통합 완료. PGM 없음. |
|
||||
|
||||
자동 근거는 `TrustedManualSceneDataLoader5025Tests.cs`, `S5025TrustedManualFileDataSourceTests.cs`, `S5025TrustedManualFileStoreTests.cs`, `manual-lists-workflow.test.cjs`다.
|
||||
|
||||
### VIList — 수동 VI 목록
|
||||
|
||||
원본: `Form/VIList.cs`, `Form/VIList.Designer.cs`
|
||||
|
||||
| 원본 이벤트·버튼 | 원본 입력·저장 경계 | 새 구현·bridge | 상태·검증 |
|
||||
|---|---|---|---|
|
||||
| `VILIST_Load` | 로컬 VI 목록을 읽는다. | `src/MBN_STOCK_WEBVIEW.Infrastructure/Playout/ViTrustedManualFileStore.cs`; native `request-vi-manual-list`; `Web/manual-lists-ui.js` | 화면 통합 완료 / 실환경 검증 대기 |
|
||||
| 종목 검색, `listBox_DoubleClick` | 종목 master에서 코스피/코스닥 prefix와 종목명을 추가한다. | native `search-stocks`, `manual-lists-workflow.js`의 strict VI item | 화면 통합 완료. |
|
||||
| `btn1/2/3/4_Click` | 선택/전체 삭제와 위/아래 이동 | manual lists draft operations | 화면 통합 완료. |
|
||||
| `btnok_Click` | 목록을 저장하고 종목명을 comma로 결합해 항상 `5단 표그래프 / VI 발동(수동)`으로 추가하며 page를 5개 단위로 계산한다. | native `save-vi-manual-list`; trusted `s5074/5074`, 5개 단위 page entry | 화면 통합 완료. 자동 테스트는 `ViTrustedManualFileStoreTests.cs`, `manual-lists-workflow.test.cjs`, fixed workflow의 VI placeholder 테스트. 운영 write 없음. |
|
||||
| `btncancel_Click` | 저장·추가 없이 닫는다. | workflow 취소 시 mutation 없음 | 화면 통합 완료. 수동 VI dataset의 PGM 없음. 기존 generic `5074` PGM 검증을 VI 입력 검증으로 간주하지 않는다. |
|
||||
|
||||
### PList + AList — 이름 있는 DB 플레이리스트
|
||||
|
||||
원본: `Form/PList.cs`, `Form/AList.cs`와 각 Designer
|
||||
|
||||
| 원본 이벤트·버튼 | 원본 입력·저장 경계 | 새 구현·bridge | 상태·검증 |
|
||||
|---|---|---|---|
|
||||
| `PList_Load`, `Get_FileName_Folder` | `DC_LIST`의 프로그램 제목·코드를 조회한다. | native `request-named-playlist-list`; `named-playlist-workflow.js` | 화면 통합 완료 |
|
||||
| `AList_Load`, `GetNextCode`, `btnok_Click` | 다음 8자리 코드와 프로그램명을 생성해 `DC_LIST`에 추가한다. | native `create-named-playlist` | 화면 통합 완료. parameterized transaction 사용. |
|
||||
| `PList.data_save`, double-click/확인 | 기존 `PLAY_LIST`를 지운 뒤 enabled와 6개 표시 필드를 `LIST_TEXT` 및 ITEM_INDEX 순서로 저장한다. | native `replace-named-playlist`; trusted entry serialization | 화면/계약 구현. raw·untrusted entry는 저장 또는 PREPARE 불가. GraphE·FSell·VI 등 manual 항목의 실제 save→reload→fresh identity/version 보존은 미검증. |
|
||||
| `PList.data_load`, double-click/확인 | 프로그램 코드별 `PLAY_LIST`를 ITEM_INDEX 순으로 읽어 playlist를 교체한다. | native `request-named-playlist-load`; resolver 기반 materialization | 화면/계약 구현. 실제 2,213행의 한국어 `DC_TITLE`/7필드 `LIST_TEXT` round-trip과 typed persistence는 전부 일치했다. KRX·해외종목·GraphE를 포함한 통합 read-only 증거는 `selectionMapped=1,603`, 차단 610이며 raw DB row는 scene data/asset/page를 재검증할 때까지 PREPARE 불가다. |
|
||||
| `PList.button1_Click` | 프로그램 정의 삭제 | native `delete-named-playlist` | 화면 통합 완료. 실제 운영 DB-W 없음. |
|
||||
| 취소 | 아무 변경 없이 닫는다. | mutation 요청 없음 | 화면 통합 완료. |
|
||||
|
||||
구현은 `src/MBN_STOCK_WEBVIEW.Core/Data/NamedPlaylistPersistence.cs`, KRX/NXT/해외종목/manual 전용 restore, `src/MBN_STOCK_WEBVIEW.Infrastructure/Execution/OracleNamedPlaylistMutationExecutor.cs`, `MainWindow.NamedPlaylists.cs`, `Web/named-playlist-workflow.js`에 있다. 자동 근거는 named playlist Core/Infrastructure/Web/bridge 테스트이며 실제 기존 데이터의 read-only 대조는 `docs/NAMED_PLAYLIST_READ_AUDIT.md`에 있다. 실제 운영 DB write와 named playlist 전용 scene/asset/PREPARE/PGM 검증은 아직 없다.
|
||||
|
||||
### ThemeA — 테마 추가·편집
|
||||
|
||||
원본: `Form/ThemeA.cs`, `Form/ThemeA.Designer.cs`
|
||||
|
||||
| 원본 이벤트·버튼 | 원본 입력·저장 경계 | 새 구현·bridge | 상태·검증 |
|
||||
|---|---|---|---|
|
||||
| `Theme_Load` | KRX/NXT 선택, 기존 테마 구성 종목과 순서를 로드한다. | native `request-theme-catalog-list`, `request-operator-catalog-schema`; `operator-catalog-workflow.js`; `operator-catalog-ui.js` | 화면 통합 완료 / 실환경 검증 대기 |
|
||||
| `btnC_Click` | 시장별 테마명 중복을 확인한다. | native list/identity validation | 화면 통합 완료 |
|
||||
| 종목 검색·double-click | 같은 시장의 종목만 prefix가 포함된 구성 행으로 추가한다. | stock search + typed theme draft/preview | 화면 통합 완료 |
|
||||
| `btn1/2/3/4_Click`, Delete key | 선택/전체 삭제, 위/아래 이동 | operator catalog theme draft operations | 화면 통합 완료 |
|
||||
| `btnok_Click`, `GetNextCode` | 신규 코드를 만들거나 기존 `SB_LIST`/`SB_ITEM`을 교체하고 입력 순서를 저장한다. KRX와 NXT 저장소를 구분한다. | native `request-theme-catalog-next-code`, `create-theme-catalog`, `replace-theme-catalog`; atomic mutation executor | 화면 통합 완료. 실제 운영 DB-W/PGM 없음. |
|
||||
| UC4 테마 삭제 | `SB_ITEM` 후 `SB_LIST` 삭제 | native `delete-theme-catalog` | 화면 통합 완료. |
|
||||
|
||||
구현은 `src/MBN_STOCK_WEBVIEW.Core/Data/ThemeCatalogPersistence.cs`, `src/MBN_STOCK_WEBVIEW.Core/Data/OperatorCatalogSchemaValidation.cs`, `src/MBN_STOCK_WEBVIEW.Infrastructure/Execution/OperatorCatalogMutationExecutor.cs`, `MainWindow.OperatorCatalogs.cs`, `Web/operator-catalog-workflow.js`다. 자동 근거는 theme catalog, schema, executor, Web workflow 테스트다. expected identity와 응답 correlation 계약은 구현했지만 실제 운영 DB에서 KRX/NXT, 동명이름, 코드가 같은 선택 행을 정확히 replace/delete하는지는 검증 대기다.
|
||||
|
||||
### EList — 전문가 신규 생성
|
||||
|
||||
원본: `Form/EList.cs`, `Form/EList.Designer.cs`
|
||||
|
||||
| 원본 이벤트·버튼 | 원본 입력·저장 경계 | 새 구현·bridge | 상태·검증 |
|
||||
|---|---|---|---|
|
||||
| `EList_Load`, `GetNextCode` | `EXPERT_LIST`의 다음 4자리 코드를 제안한다. | native `request-expert-catalog-next-code`; `operator-catalog-ui.js` | 화면 통합 완료 / 실환경 검증 대기 |
|
||||
| `btnok_Click` | 이름을 검증하고 `EXPERT_LIST`에 신규 전문가를 저장한다. | native `create-expert-catalog` | 화면 통합 완료. 실제 운영 DB-W 없음. |
|
||||
| UC6 저장·삭제 | 전문가와 `RECOMMEND_LIST` 구성 종목·금액·PLAYINDEX를 교체 또는 삭제한다. | native `replace-expert-catalog`, `delete-expert-catalog` | 화면 통합 완료. 실제 PGM 없음. |
|
||||
| `btncn_Click` | 변경 없이 닫는다. | mutation 요청 없음 | 화면 통합 완료. |
|
||||
|
||||
구현은 `src/MBN_STOCK_WEBVIEW.Core/Data/ExpertCatalogPersistence.cs`, 공통 schema/executor/native partial, `Web/operator-catalog-workflow.js`다. 자동 근거는 expert catalog, schema, executor, Web workflow 테스트다. 전문가 코드·이름과 추천 행 identity 계약은 구현했지만 실제 운영 DB의 create/replace/delete read-back은 검증 대기다.
|
||||
|
||||
## bridge와 화면 로딩 상태 요약
|
||||
|
||||
| 기능군 | native request | workflow | 현재 `index.html`/`app.js` |
|
||||
|---|---|---|---|
|
||||
| 종목/고정/업종/비교/테마 조회/해외/전문가 조회/거래정지 | `search-stocks`, `request-industries`, `search-themes`, `request-theme-preview`, `search-world-stocks`, `search-overseas-industries`, `search-experts`, `request-expert-preview`, `search-trading-halts` | 각 전용 workflow | 로드·연결됨 |
|
||||
| 이름 있는 플레이리스트 | `request-named-playlist-list`, `request-named-playlist-load`, `create-named-playlist`, `replace-named-playlist`, `delete-named-playlist` | `named-playlist-workflow.js` | 로드·연결됨 |
|
||||
| GraphE | `request-manual-financial-list`, `request-manual-financial-load`, `request-manual-financial-selection`, `create-manual-financial-record`, `update-manual-financial-record`, `delete-manual-financial-record`, `delete-all-manual-financial-records` | `manual-financial-workflow.js`, `manual-financial-ui.js` | 로드·연결됨 |
|
||||
| FSell/VI | `request-manual-operator-data-status`, `request-manual-net-sell-data`, `save-manual-net-sell-data`, `request-vi-manual-list`, `save-vi-manual-list` | `manual-lists-workflow.js`, `manual-lists-ui.js` | 로드·연결됨 |
|
||||
| ThemeA/EList CRUD | `request-theme-catalog-list`, `request-expert-catalog-list`, `request-operator-catalog-schema`, theme/expert next-code, create/replace/delete requests | `operator-catalog-workflow.js`, `operator-catalog-ui.js` | 로드·연결됨 |
|
||||
|
||||
## 근거 파일 빠른 링크
|
||||
|
||||
| 영역 | 구현 | 자동 테스트 |
|
||||
| 원본 이벤트 | 원본 의미 | LegacyParityApp 동작 |
|
||||
|---|---|---|
|
||||
| 공통 화면·송출·배경 | [MainWindow](../MainWindow.xaml.cs), [Playout partial](../MainWindow.Playout.cs), [Background partial](../MainWindow.Background.cs), [Web app](../Web/app.js) | [playout safety](../tests/Web/playout-safety.test.cjs) |
|
||||
| 328 fixed / 44 industry / 31 stock / 9 comparison | [fixed workflow](../Web/legacy-fixed-workflow.js), [industry workflow](../Web/industry-workflow.js), [stock workflow](../Web/operator-workflow.js), [comparison workflow](../Web/comparison-workflow.js) | [fixed](../tests/Web/legacy-fixed-workflow.test.cjs), [industry](../tests/Web/industry-workflow.test.cjs), [stock](../tests/Web/operator-workflow.test.cjs), [comparison](../tests/Web/comparison-workflow.test.cjs) |
|
||||
| 테마 조회·해외·전문가 조회·거래정지 | [theme](../Web/theme-workflow.js), [overseas](../Web/overseas-workflow.js), [expert](../Web/expert-workflow.js), [trading halt](../Web/trading-halt-workflow.js) | [theme](../tests/Web/theme-workflow.test.cjs), [overseas](../tests/Web/overseas-workflow.test.cjs), [expert](../tests/Web/expert-workflow.test.cjs), [trading halt](../tests/Web/trading-halt-workflow.test.cjs) |
|
||||
| GraphE | [native partial](../MainWindow.ManualFinancial.cs), [Core service](../src/MBN_STOCK_WEBVIEW.Core/Data/ManualFinancialScreens.cs), [Oracle executor](../src/MBN_STOCK_WEBVIEW.Infrastructure/Execution/OracleManualFinancialMutationExecutor.cs), [workflow](../Web/manual-financial-workflow.js) | [Core](../tests/MBN_STOCK_WEBVIEW.Core.Tests/LegacyManualFinancialScreenServiceTests.cs), [Infrastructure](../tests/MBN_STOCK_WEBVIEW.Infrastructure.Tests/OracleManualFinancialMutationExecutorTests.cs), [Web](../tests/Web/manual-financial-workflow.test.cjs), [bridge](../tests/Web/manual-financial-bridge-integration.test.cjs) |
|
||||
| FSell / VI | [native partial](../MainWindow.ManualLists.cs), [FSell store/data source](../src/MBN_STOCK_WEBVIEW.Infrastructure/Playout/S5025TrustedManualFileDataSource.cs), [VI store](../src/MBN_STOCK_WEBVIEW.Infrastructure/Playout/ViTrustedManualFileStore.cs), [workflow](../Web/manual-lists-workflow.js) | [FSell store](../tests/MBN_STOCK_WEBVIEW.Infrastructure.Tests/S5025TrustedManualFileStoreTests.cs), [VI store](../tests/MBN_STOCK_WEBVIEW.Infrastructure.Tests/ViTrustedManualFileStoreTests.cs), [Web](../tests/Web/manual-lists-workflow.test.cjs) |
|
||||
| PList / AList | [native partial](../MainWindow.NamedPlaylists.cs), [Core service](../src/MBN_STOCK_WEBVIEW.Core/Data/NamedPlaylistPersistence.cs), [Oracle executor](../src/MBN_STOCK_WEBVIEW.Infrastructure/Execution/OracleNamedPlaylistMutationExecutor.cs), [workflow](../Web/named-playlist-workflow.js) | [Core integration](../tests/MBN_STOCK_WEBVIEW.Core.Tests/LegacyNamedPlaylistPersistenceIntegrationTests.cs), [Infrastructure](../tests/MBN_STOCK_WEBVIEW.Infrastructure.Tests/OracleNamedPlaylistMutationExecutorTests.cs), [Web](../tests/Web/named-playlist-workflow.test.cjs), [bridge](../tests/Web/named-playlist-bridge-integration.test.cjs) |
|
||||
| ThemeA / EList | [native partial](../MainWindow.OperatorCatalogs.cs), [theme persistence](../src/MBN_STOCK_WEBVIEW.Core/Data/ThemeCatalogPersistence.cs), [expert persistence](../src/MBN_STOCK_WEBVIEW.Core/Data/ExpertCatalogPersistence.cs), [executor](../src/MBN_STOCK_WEBVIEW.Infrastructure/Execution/OperatorCatalogMutationExecutor.cs), [workflow](../Web/operator-catalog-workflow.js) | [theme](../tests/MBN_STOCK_WEBVIEW.Core.Tests/LegacyThemeCatalogPersistenceServiceTests.cs), [expert](../tests/MBN_STOCK_WEBVIEW.Core.Tests/LegacyExpertCatalogPersistenceServiceTests.cs), [Infrastructure](../tests/MBN_STOCK_WEBVIEW.Infrastructure.Tests/OperatorCatalogMutationExecutorTests.cs), [Web](../tests/Web/operator-catalog-workflow.test.cjs) |
|
||||
| `Form1_Load` | 자산·DB·탭·Tornado 초기화 | WebView local UI와 C# composition root 초기화; 기본 `DryRun` |
|
||||
| `tabControl1_SelectedIndexChanged` | 탭별 최초 데이터 조회 | 10개 identity별 C# activation gate; 최초 진입 1회 load, 재진입 snapshot 유지 |
|
||||
| 탭 drag 이벤트 | 10개 탭 표시 순서 교환 | identity 기반 drag/키보드 이동; active tab·playlist 불변 |
|
||||
| 검색 버튼/Enter | 종목 검색 | 원본 KOSPI→KOSDAQ→NXT 순서와 마지막 이름 일치 규칙 유지 |
|
||||
| 컷 click/double-click/drag | 원본 31컷 선택·추가 | plain/Ctrl/Shift/Ctrl+Shift와 원본 double-click 판정, batch 추가 |
|
||||
| playlist click/key/buttons | 선택, 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 대체와 클릭 규칙
|
||||
|
||||
| 항목 | 결과 |
|
||||
|---|---|
|
||||
| 자동 테스트 | Core 1,383개, Infrastructure 173개, Playout 393개가 Debug/Release x64에서 각각 통과했고 Web workflow·bridge 415/415도 통과했다. 412 action의 폐쇄형 매트릭스, 계약·경계·복원 실패와 native fail-closed gate를 검증한다. |
|
||||
| 실제 운영 DB write | **미실행.** GraphE, named playlist, ThemeA, EList의 mutation executor는 fake/계약 테스트로 검증했으며 운영 DB에는 쓰지 않았다. FSell/VI는 테스트용 격리 저장소만 사용했다. |
|
||||
| 실제 Tornado2 PGM | 제한된 승인 회차에서 `5001`, `5074` page 1·2, TAKE OUT을 확인했다. 이는 전체 412 action, GraphE, FSell, VI, ThemeA, EList, 이름 있는 playlist 각각의 UI 동등성 검증이 아니다. |
|
||||
| PList legacy 복원 | 실제 `DC_TITLE`/`LIST_TEXT` 2,213행의 한국어·7필드 무손상과 typed persistence 일치를 확인했다. 기본 `selectionMapped=1,581`/차단 632와 별도 GraphE 22/14를 합친 결과는 1,603/610이다. selection 매핑은 scene data·asset·PREPARE·PGM 완료 증거가 아니다. |
|
||||
| manual named save | 현재 legacy PList manual 36행은 모두 GraphE이며 fresh proof로 22행을 복원하고 저장값 무효 9행·identity 중복 5행을 차단했다. FSell/VI PList행은 0건이고 production source read는 FSell 3×5행, VI 9항목·2 page로 전후 불변이다. 실제 Oracle save→reload와 PGM은 미검증이다. |
|
||||
| ThemeA/EList identity | expected identity·상관 응답 계약은 구현했다. 실제 운영 DB의 KRX/NXT·동명이름·코드 경계와 정확한 선택 행 replace/delete는 미검증이다. |
|
||||
| legacy file import | `종목비교.dat`는 signed package에서 8쌍 1회 실행·재시작 marker까지 확인했다. FSell·VI importer는 strict parser, source/hash 재검증, durable intent/marker와 격리 fixture를 통과했으며 현재 운영 profile은 기존 동일 파일 때문에 no-overwrite 차단된다. |
|
||||
| 남은 실환경 검증 | 다섯 전용 화면의 Web 연결과 signed `1.0.5.0` Release MSIX package-context 실행은 화면/계약 검증 범위에서 확인됐다. 1.0.5 package UI에서 실제 삼성전자 검색·현재가 5001 DryRun과 자동 refresh cap을 확인했으며, 기존 10개 업무 탭 상태·비교 8쌍 영속성 증거는 1.0.4 감사에 보존한다. 실제 입력→운영 DB write→재조회와 각 전용 scene PGM 검증은 별도 승인·테스트 데이터·rollback 회차에서만 수행한다. |
|
||||
FarPoint UI는 Web table/list로 표현한다. 다음 의미는 원본과 같이 유지한다.
|
||||
|
||||
- plain/Ctrl/Shift/Ctrl+Shift 선택과 현재 행
|
||||
- column header 정렬과 현재 표시 순서
|
||||
- 위/아래 이동, 선택/전체 삭제와 enabled 토글
|
||||
- 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를 추정하지 않는다.
|
||||
|
||||
## 원클릭 TAKE IN과 숨김 PREPARE
|
||||
|
||||
원본 운영 화면의 핵심 동작은 별도 PREPARE를 먼저 누르는 것이 아니라 F8/TAKE IN 한 번으로
|
||||
송출하는 것이다. 현재 보이는 TAKE IN은 idle에서도 활성 조건을 만족하면 다음을 실행한다.
|
||||
|
||||
1. playlist selection과 page를 C# snapshot으로 고정
|
||||
2. DB/신뢰 파일 DTO와 PageN을 fresh load
|
||||
3. 장면 mutation과 transaction 순서를 적용해 `Prepare` 정확히 1회
|
||||
4. 성공한 prepared scene에 `TakeIn` 정확히 1회
|
||||
|
||||
엔지니어링용 native PREPARE control과 explicit workflow는 테스트를 위해 유지하지만 Web UI에서
|
||||
`hidden`이다. TAKE IN 실패·timeout·`OutcomeUnknown`이면 자동 재시도하지 않으며 성공한
|
||||
prepared ownership을 추측해 지우지 않는다.
|
||||
|
||||
## 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` 존재
|
||||
- `s5006` `Video\큐브배경.vrv` 1개와 `s6001` 국가 영상 13개는 외부자산 미제공
|
||||
- sibling `배경\기본.vrv`도 외부자산 미제공
|
||||
|
||||
마지막 14개 영상과 `기본.vrv`의 부재는 UI/scene 코드 실패가 아니다. 해당 기능은 자산이
|
||||
제공될 때까지 COM 전에 차단하며 임의 파일로 대체하지 않는다.
|
||||
|
||||
## 구현·테스트 빠른 링크
|
||||
|
||||
| 영역 | 현재 실행 구현 | 주요 테스트/근거 |
|
||||
|---|---|---|
|
||||
| WinUI 3 + WebView2 shell | [MainWindow.xaml.cs](../src/MBN_STOCK_WEBVIEW.LegacyParityApp/MainWindow.xaml.cs), [Web app](../src/MBN_STOCK_WEBVIEW.LegacyParityApp/Web/app.js), [Web layout](../src/MBN_STOCK_WEBVIEW.LegacyParityApp/Web/index.html) | [thin client](../tests/LegacyParityWeb/thin-client.test.cjs) |
|
||||
| C# 업무 권위 | [LegacyOperatorController.cs](../src/MBN_STOCK_WEBVIEW.LegacyApplication/LegacyOperatorController.cs) | [LegacyApplication tests](../tests/MBN_STOCK_WEBVIEW.LegacyApplication.Tests) |
|
||||
| strict bridge | [LegacyBridgeProtocol.cs](../src/MBN_STOCK_WEBVIEW.LegacyBridge/LegacyBridgeProtocol.cs) | [LegacyBridge tests](../tests/MBN_STOCK_WEBVIEW.LegacyBridge.Tests) |
|
||||
| 송출 | [MainWindow.Playout.cs](../src/MBN_STOCK_WEBVIEW.LegacyParityApp/MainWindow.Playout.cs), [playout-ui.js](../src/MBN_STOCK_WEBVIEW.LegacyParityApp/Web/playout-ui.js), [LegacyPlayoutWorkflow.cs](../src/MBN_STOCK_WEBVIEW.Core/Playout/Scenes/LegacyPlayoutWorkflow.cs) | [LegacyPlayoutWorkflowTests.cs](../tests/MBN_STOCK_WEBVIEW.Core.Tests/LegacyPlayoutWorkflowTests.cs) |
|
||||
| 수동 목록 | [MainWindow.ManualLists.cs](../src/MBN_STOCK_WEBVIEW.LegacyParityApp/MainWindow.ManualLists.cs), [LegacyManualListsWorkflow.cs](../src/MBN_STOCK_WEBVIEW.LegacyApplication/ManualLists/LegacyManualListsWorkflow.cs) | [manual-list tests](../tests/MBN_STOCK_WEBVIEW.LegacyApplication.Tests/ManualLists) |
|
||||
| GraphE | [LegacyManualFinancialWorkflow.cs](../src/MBN_STOCK_WEBVIEW.LegacyApplication/ManualFinancial/LegacyManualFinancialWorkflow.cs) | [manual-financial tests](../tests/MBN_STOCK_WEBVIEW.LegacyApplication.Tests/ManualFinancial) |
|
||||
| PList/AList | [LegacyNamedPlaylistWorkflow.cs](../src/MBN_STOCK_WEBVIEW.LegacyApplication/NamedPlaylist/LegacyNamedPlaylistWorkflow.cs) | [named-playlist tests](../tests/MBN_STOCK_WEBVIEW.LegacyApplication.Tests/NamedPlaylist) |
|
||||
| ThemeA/EList | [LegacyOperatorCatalogWorkflow.cs](../src/MBN_STOCK_WEBVIEW.LegacyApplication/OperatorCatalog/LegacyOperatorCatalogWorkflow.cs) | [catalog tests](../tests/MBN_STOCK_WEBVIEW.LegacyApplication.Tests/OperatorCatalog) |
|
||||
| scene matrix | [SCENE_EQUIVALENCE.md](SCENE_EQUIVALENCE.md) | 35/35 builder, 45 alias, PageN matrix |
|
||||
| 2026-07-15 증적 | [LEGACY_PARITY_VALIDATION_20260715.md](LEGACY_PARITY_VALIDATION_20260715.md) | 최종 전체 회귀 기록란 포함 |
|
||||
|
||||
최종 Debug/Release 전체 테스트 개수, 새 MSIX SHA-256와 개발 PGM 결과는 병렬 변경을
|
||||
모두 합친 뒤 위 검증 문서에 기록했다. 이전 package 숫자나 hash를 현재 결과로
|
||||
재사용하지 않았다.
|
||||
|
||||
Reference in New Issue
Block a user