# 원본 WinForms Designer 이벤트 동등성 감사 감사 기준일은 2026-07-15이다. 원본 `C:\Users\MD\source\repos\MBN_STOCK_N`의 커밋 `309286309eaf20b58172247263f575753a860700`을 읽기 전용으로 조사했다. 대상은 실제로 Designer가 구독하는 이벤트이며, 선언만 있고 구독되지 않은 메서드는 수에 넣지 않았다. Designer 구독은 186개이고, 같은 핸들러를 여러 컨트롤에 연결한 구독을 화면별로 합치면 171개다. 이 문서의 상세 목록은 그 171개를 한 번씩 모두 분류한다. ## 분류 기준 - `IMPL`: 원본의 활성 업무 동작이 C# 소유 상태/워크플로, 엄격한 WebView 브리지 intent, 또는 네이티브 playout 경로로 이관됐다. - `DEAD`: 원본 메서드가 비어 있거나 전부 주석이거나 즉시 반환한다. 새 명령을 만들지 않는 것이 원본과 같다. - `VISUAL`: WinForms 전용 paint/draw/focus/resize 동작이며 DOM, CSS, 브라우저 기본 동작으로 대체됐다. - `DRAG`: 원본 drag/mouse 순서를 Web drag-and-drop과 C# 소유 순서 변경으로 이관한 묶음이다. - `SAFETY`: 원본과 의도적으로 다르다. 기존 송출 안전 정책 때문에 자동 `StopAll`을 넣지 않는다. ## 집계 | 화면 | Designer 구독 | 고유 핸들러 | IMPL | DEAD | VISUAL | DRAG | SAFETY | |---|---:|---:|---:|---:|---:|---:|---:| | UC1 | 9 | 9 | 3 | 2 | 4 | 0 | 0 | | UC2 | 4 | 4 | 3 | 1 | 0 | 0 | 0 | | UC3 | 18 | 18 | 14 | 4 | 0 | 0 | 0 | | UC4 | 15 | 15 | 13 | 2 | 0 | 0 | 0 | | UC5 | 20 | 9 | 6 | 3 | 0 | 0 | 0 | | UC6 | 13 | 13 | 12 | 1 | 0 | 0 | 0 | | UC7 | 7 | 6 | 5 | 1 | 0 | 0 | 0 | | AList | 3 | 3 | 3 | 0 | 0 | 0 | 0 | | EList | 3 | 3 | 3 | 0 | 0 | 0 | 0 | | FSell | 3 | 3 | 3 | 0 | 0 | 0 | 0 | | GraphE | 15 | 15 | 14 | 1 | 0 | 0 | 0 | | PList | 7 | 7 | 6 | 1 | 0 | 0 | 0 | | ThemeA | 13 | 13 | 12 | 1 | 0 | 0 | 0 | | VIList | 10 | 10 | 10 | 0 | 0 | 0 | 0 | | MainForm | 46 | 43 | 24 | 3 | 6 | 9 | 1 | | **합계** | **186** | **171** | **131** | **20** | **10** | **9** | **1** | UC5는 20개 구독이 9개 핸들러를, UC7은 7개 구독이 6개 핸들러를, MainForm은 46개 구독이 43개 핸들러를 공유한다. 따라서 구독 수와 고유 핸들러 수가 다르다. ## 171개 핸들러 상세 증거 `@숫자`는 각 원본 `*.Designer.cs`의 이벤트 구독 줄이다. ### UC1 — `Control/UC1.Designer.cs` - `IMPL (3)`: `treeView_DoubleClick@51`, `chE_CheckedChanged@73`, `UC1_Load@84` - `DEAD (2)`: `treeView_BeforeSelect@49`, `treeView_AfterSelect@50` - `VISUAL (4)`: `treeView_BeforeCollapse@46`, `treeView_BeforeExpand@47`, `treeView_DrawNode@48`, `treeView_MouseDown@52` ### UC2 — `Control/UC2.Designer.cs` - `IMPL (3)`: `btnswap_Click@75`, `listbox_DoubleClick@106`, `UC2_Load@126` - `DEAD (1)`: `listbox_SelectedIndexChanged@105` ### UC3 — `Control/UC3.Designer.cs` - `IMPL (14)`: `btnswap_Click@154`, `txtSearch_KeyPress@220`, `listBox_DoubleClick@232`, `btnsearch_Click@249`, `txtj_KeyPress@314`, `btnsearch2_Click@328`, `FpSpread2_CellDoubleClick@396`, `btnAdd_Click@470`, `btn4_Click@531`, `btn3_Click@545`, `btn2_Click@559`, `btn1_Click@573`, `listBox1_DoubleClick@720`, `UC3_Load@741` - `DEAD (4)`: `txtSearch_KeyDown@219`, `listBox_SelectedIndexChanged@231`, `FpSpread2_CellClick_1@395`, `FpSpread_CellClick@641` ### UC4 — `Control/UC4.Designer.cs` - `IMPL (13)`: `btn1_Click@288`, `txts_KeyDown@297`, `btn2_Click@311`, `btn3_Click@325`, `btn4_Click@339`, `FpSpread1_CellClick@406`, `FpSpread1_CellDoubleClick@407`, `btnpre_Click@483`, `btnnow_Click@497`, `timer1_Tick@659`, `btnnow_6_Click@673`, `btnnow_12_Click@687`, `UC4_Load@752` - `DEAD (2)`: `FpSpread1_DoubleClick@408`, `FpSpread2_CellClick@564` ### UC5 — `Control/UC5.Designer.cs` - `IMPL (6)`: `btn1_Click@497`, `btnsearch2_Click@571`, `txtj_KeyPress@581`, `btnsearch1_Click@747`, `txts_KeyPress@757`, `UC5_Load@1007` - `DEAD (3)`: `txtj_TextChanged@580`, `txts_TextChanged@756`, `FpSpread_CellClick@939` ### UC6 — `Control/UC6.Designer.cs` - `IMPL (12)`: `btlistadd_Click@263`, `txtSearch_KeyPress@284`, `listBox_DoubleClick@295`, `btnsearch_Click@312`, `btnsave_Click@365`, `btndel_Click@379`, `FpSpread1_CellClick@456`, `FpSpread2_CellClick@570`, `FpSpread2_KeyDown@571`, `btnDelete_Click@648`, `btnAdd_Click@662`, `UC6_Load@673` - `DEAD (1)`: `tabp6_1_Paint@331` - 이벤트 구독과 별도로 원본 `FpSpread2.AllowDragDrop = true`가 `Control/UC6.Designer.cs:519`에 있다. 이는 `DRAG` 경로의 전문가 추천행 재정렬로 이관한다. ### UC7 — `Control/UC7.Designer.cs` - `IMPL (5)`: `btnsearch_Click@185`, `txts_KeyPress@194`, `btn4_Click@219`, `FpSpread1_CellClick@301`, `UC7_Load@369` - `DEAD (1)`: `groupBox3_Enter@161` ### AList — `Form/AList.Designer.cs` - `IMPL (3)`: `btnok_Click@64`, `btncn_Click@88`, `AList_Load@124` ### EList — `Form/EList.Designer.cs` - `IMPL (3)`: `btncn_Click@56`, `btnok_Click@82`, `EList_Load@137` ### FSell — `Form/FSell.Designer.cs` - `IMPL (3)`: `btncn_Click@109`, `btnok_Click@125`, `FSell_Load@337` ### GraphE — `Form/GraphE.Designer.cs` - `IMPL (14)`: `upSearch_Click@220`, `txt1_KeyPress@229`, `downSearch_Click@244`, `FpSpread1_CellClick@312`, `FpSpread1_CellDoubleClick@313`, `btndel_Click@402`, `btnall_Click@417`, `btnAdd_Click@432`, `txtSearch_KeyPress@453`, `listBox_DoubleClick@464`, `btnsearch_Click@481`, `btadd_Click@531`, `btnok_Click@1274`, `GraphE_Load@1318` - `DEAD (1)`: `GraphE_KeyPress@1319` ### PList — `Form/PList.Designer.cs` - `IMPL (6)`: `btnadd_Click@55`, `listBox_DoubleClick@79`, `btncn_Click@95`, `button3_Click@111`, `button1_Click@127`, `PList_Load@139` - `DEAD (1)`: `listBox_SelectedIndexChanged@78` ### ThemeA — `Form/ThemeA.Designer.cs` - `IMPL (12)`: `btncancel_Click@246`, `btnok_Click@260`, `FpSpread_KeyDown@353`, `btn4_Click@422`, `btn3_Click@436`, `btn2_Click@450`, `btn1_Click@464`, `btnC_Click@479`, `listBox_DoubleClick@521`, `txtSearch_KeyPress@530`, `btnsearch_Click@547`, `Theme_Load@569` - `DEAD (1)`: `listBox_SelectedIndexChanged@520` ### VIList — `Form/VIList.Designer.cs` - `IMPL (10)`: `txtSearch_KeyPress@188`, `btnok_Click@202`, `btn4_Click@391`, `btn3_Click@405`, `btn2_Click@419`, `btn1_Click@433`, `listBox_DoubleClick@456`, `btnsearch_Click@473`, `btncancel_Click@487`, `VILIST_Load@528` ### MainForm — `MainForm.Designer.cs` - `IMPL (24)`: `tabControl1_SelectedIndexChanged@1062`, `btnPrepare_Click@1465`, `PlayList_CellClick@1723`, `PlayList_KeyPress@1726`, `ckback_CheckedChanged@1818`, `btnback_Click@1854`, `btntdelete_Click@1869`, `btndelete_Click@1884`, `btnload_Click@1899`, `button5_Click@1914`, `btndown_Click@1930`, `btnup_Click@1946`, `checkBox1_CheckedChanged@1986`, `btnTake_Click@2018`, `txtSearch_KeyPress@2047`, `b1_Click@2108`, `button9_Click@2207`, `btnNext_Click@2410`, `btnTakeout_Click@2428`, `timer1_Tick@2433`, `timer3_Tick@2443`, `Form1_Load@2470`, `MainForm_KeyDown@2472`, `MainForm_KeyUp@2473` - `DEAD (3)`: `PlayList_LeaveCell@1722`, `GridView1_RowLeave@2081`, `FpSpread2_CellDoubleClick@2280` - `VISUAL (6)`: `panel6_Paint@1426`, `GridView1_CellEnter@2080`, `listBox_DrawItem@2189`, `timer2_Tick@2438`, `MainForm_Paint@2471`, `MainForm_Resize@2474` - `DRAG (9)`: `tab_DragOver@1063`, `tab_MouseDown@1064`, `tab_MouseMove@1065`, `tab_MouseUp@1066`, `PlayList_DragDrop@1724`, `PlayList_DragOver@1725`, `listView1_MouseDown@2349`, `listView1_MouseMove@2350`, `listView1_MouseUp@2351` - `SAFETY (1)`: `MainForm_FormClosing@2469` ## ThemeA `중복확인` 동등성 원본 `Form/ThemeA.cs:84-109`는 입력 이름과 현재 KRX/NXT 선택을 기준으로 정확히 같은 테마명을 조회하고 `이미 사용중인 테마명입니다.` 또는 `사용 가능한 테마명입니다.`를 표시한다. 새 구현은 다음 경로를 사용한다. 1. `Web/index.html`의 `operator-catalog-name-check`가 원본 `중복확인` 버튼이다. 2. Web은 이름만 담은 `check-operator-catalog-theme-name` intent를 보낸다. market, code, table 이름 같은 DB 권한 정보는 보내지 못한다. 3. `LegacyBridgeProtocol`이 닫힌 payload와 128자 canonical text를 검증한다. 4. `LegacyOperatorController.CheckOperatorCatalogThemeNameAsync`가 `LegacyOperatorCatalogWorkflowController.CheckThemeNameAvailabilityAsync`를 호출한다. 5. 워크플로는 C#이 소유한 현재 KRX/NXT 대상과 exact title을 `IThemeCatalogPersistenceService.ThemeNameExistsAsync`에 전달한다. 구현은 선택한 provider의 `SB_LIST`에 parameterized `COUNT(*)` SELECT를 정확히 한 번 실행하며 mutation executor를 호출하지 않는다. 128자 테마명도 잘림 없는 exact query로 처리한다. 6. 조회 결과를 확정할 수 없으면 `Indeterminate`로 표시하며 DB 쓰기를 하지 않는다. 원본 쿼리는 edit 중인 현재 `SB_CODE`를 제외하지 않는다. 따라서 제목을 바꾸지 않은 기존 테마에서 `중복확인`을 누르면 자기 행 때문에 `이미 사용중인 테마명입니다.`가 표시된다. 새 구현도 고객의 원본 우선 요구에 따라 KRX/NXT 모두 이 동작을 그대로 보존한다. 검증은 `LegacyThemeCatalogPersistenceServiceTests`, `LegacyOperatorCatalogWorkflowTests`, `LegacyOperatorCatalogBridgeTests`, `LegacyOperatorCatalogParityWebTests`에 있다. KRX와 NXT의 같은 이름을 서로 혼동하지 않는지, create의 중복/사용 가능, edit의 자기 행 중복, 128자 parameter binding, 결과 불명 상태, 엄격한 payload, Web 버튼 및 native dispatch를 확인한다. ## 의도적인 종료 안전 차이 원본 `MainForm.cs:751-780`은 종료 확인 뒤 `KAScenePlayer.StopAll()`, `KAScene.Unload()`, 500ms 대기, 현재 프로세스 강제 종료를 수행한다. 새 앱은 이를 그대로 이관하지 않는다. - `MainWindow.CloseConfirmation.cs:88-119`는 PREPARED/PROGRAM, pending callback, `OutcomeUnknown`을 사용자에게 명시하고 종료 과정에서 TAKE OUT을 자동 실행하지 않는다. - `MainWindow.Playout.cs:778-823`은 on-air 장면, pending callback, quarantine, `OutcomeUnknown`이 없을 때만 bounded disconnect를 시도한다. - 이 차이는 기존 안전 게이트의 `결과 불명 시 반복 금지`와 `명시적 송출 명령` 정책을 지키기 위한 활성 안전 요구사항이다. 동등성 작업에서 완화하거나 자동 `StopAll`로 바꾸지 않는다. ## 기타 감사 메모 - 원본 `MainForm.Designer.cs:1399`에서 PREPARE가 들어 있는 `panel3.Visible = false`다. 새 Web UI도 PREPARE를 일반 조작 화면에서는 숨기고 네이티브/승인된 검증 경로는 유지한다. - 원본의 단일 인스턴스 기대는 `LegacyParityApp/App.xaml.cs`의 `AppInstance.FindOrRegisterForKey`로 구현됐다. - 원본 `timer3_Tick`의 Tornado 상태 감시는 `IPlayoutEngine` 뒤의 `TornadoPlayoutEngine` monitor/reconnect 상태 기계로 격리돼 있다. - 이벤트 이름이 여러 화면에서 같더라도 화면별 메서드는 별개로 셌다. 예를 들어 `btn1_Click`, `btnsearch_Click`, `listBox_DoubleClick`은 전역 이름 하나가 아니라 각 화면의 서로 다른 바인딩이다.