13 KiB
원본 WinForms Designer 이벤트 동등성 감사
감사 기준일은 2026-07-15이다. 원본 C:\Users\MD\source\repos\MBN_STOCK_N의 커밋
309286309eaf20b58172247263f575753a860700을 읽기 전용으로 조사했다. 대상은 실제로
Designer가 구독하는 이벤트이며, 선언만 있고 구독되지 않은 메서드는 수에 넣지 않았다.
Designer 구독은 186개이고, 같은 핸들러를 여러 컨트롤에 연결한 구독을 화면별로 합치면 171개다. 이 문서의 상세 목록은 그 171개를 한 번씩 모두 분류한다.
원본 timerAliveChecker는 Designer 구독이 아니다. MainForm 생성자 코드가 10초 interval의
WinForms Timer를 만들고 timerAliveChecker_Tick을 동적으로 구독한다. 따라서 186/171 집계에
억지로 더하지 않고 별도 활성 이벤트로 감사한다. 이 보충 경로도 현재 LegacyParityApp에
연결됐으므로, 현행 판정에는 미이관 Designer 이벤트나 미이관 동적 활성 이벤트가 없다.
분류 기준
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@84DEAD (2):treeView_BeforeSelect@49,treeView_AfterSelect@50VISUAL (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@126DEAD (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@741DEAD (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@752DEAD (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@1007DEAD (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@673DEAD (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@369DEAD (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@1318DEAD (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@139DEAD (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@569DEAD (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@2473DEAD (3):PlayList_LeaveCell@1722,GridView1_RowLeave@2081,FpSpread2_CellDoubleClick@2280VISUAL (6):panel6_Paint@1426,GridView1_CellEnter@2080,listBox_DrawItem@2189,timer2_Tick@2438,MainForm_Paint@2471,MainForm_Resize@2474DRAG (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@2351SAFETY (1):MainForm_FormClosing@2469
원본 PROGRAM 중 enabled 변경에는 입력 경로별 차이가 있다. PlayList_CellClick은 m_TakeIn이면
checkbox 클릭을 거부하지만, PlayList_KeyPress의 Space와 checkBox1_CheckedChanged의 전체
변경은 같은 guard가 없어 다음 송출 행을 건너뛰는 데 쓰일 수 있다. 새 앱은 PROGRAM 중 이미
진행한 prefix와 현재 on-air 행을 고정하고 미래 행만 Space/전체 enabled 변경을 허용한다.
원본의 운영 편의는 보존하면서 재생 이력과 현재 행을 바꾸지 않는 안전 적응이며 자동 계약 검증은
완료됐다. 이 변경을 포함한 최신 등록 AppX에서도 실제 키보드 입력으로 PROGRAM 중 현재 행 고정과
미래 행 enabled 변경을 최종 재검증했다.
Designer 밖 동적 활성 이벤트
- 원본
MainForm.cs:82-89의timerAliveChecker는 10초마다 DB 연결 끊김·복구를 확인하고 상태 전이 때 로그를 남긴다. Designer 파일에 없다는 이유로 이전 186/171 집계에서 제외된 것이며, 비활성 코드가 아니다. - 현재
MainWindow.xaml.cs는 창 초기화 뒤StartDatabaseHealthMonitor()를 호출한다.MainWindow.DatabaseHealth.cs는 같은 10초 첫 지연/주기를 사용해 Oracle/MariaDB를 점검하고 전이 때만 native operator log를 남긴다. - 운영자 DB 입력·송출·자동 갱신이 시작되면 active health check를 취소하고 공용 DB activity gate를 양보하며, 앱 종료 때 lifetime token과 active check를 모두 취소한다.
- 실제 DB 서버를 강제로 끊는 장애 주입은 하지 않았다. 수명주기·전이 중복 억제·priority gate· cancellation은 자동 회귀로 고정한다.
ThemeA 중복확인 동등성
원본 Form/ThemeA.cs:84-109는 입력 이름과 현재 KRX/NXT 선택을 기준으로 정확히 같은
테마명을 조회하고 이미 사용중인 테마명입니다. 또는 사용 가능한 테마명입니다.를 표시한다.
새 구현은 다음 경로를 사용한다.
Web/index.html의operator-catalog-name-check가 원본중복확인버튼이다.- Web은 이름만 담은
check-operator-catalog-theme-nameintent를 보낸다. market, code, table 이름 같은 DB 권한 정보는 보내지 못한다. LegacyBridgeProtocol이 닫힌 payload와 128자 canonical text를 검증한다.LegacyOperatorController.CheckOperatorCatalogThemeNameAsync가LegacyOperatorCatalogWorkflowController.CheckThemeNameAvailabilityAsync를 호출한다.- 워크플로는 C#이 소유한 현재 KRX/NXT 대상과 exact title을
IThemeCatalogPersistenceService.ThemeNameExistsAsync에 전달한다. 구현은 선택한 provider의SB_LIST에 parameterizedCOUNT(*)SELECT를 정확히 한 번 실행하며 mutation executor를 호출하지 않는다. 128자 테마명도 잘림 없는 exact query로 처리한다. - 조회 결과를 확정할 수 없으면
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뒤의TornadoPlayoutEnginemonitor/reconnect 상태 기계로 격리돼 있다. - 이벤트 이름이 여러 화면에서 같더라도 화면별 메서드는 별개로 셌다. 예를 들어
btn1_Click,btnsearch_Click,listBox_DoubleClick은 전역 이름 하나가 아니라 각 화면의 서로 다른 바인딩이다.