Files
MBN_STOCK_WEBVIEW/docs/NAMED_PLAYLIST_READ_AUDIT.md

9.9 KiB

PList 기존 데이터 읽기 전용 동등성 감사

기준일: 2026-07-12

원본(읽기 전용): C:\Users\MD\source\repos\MBN_STOCK_N\MBN_STOCK_N\Form\PList.cs 감사 구현: LegacyPListReadAuditParser, NamedPlaylistReadOnlyDbAudit, NamedManualRestoreReadOnlyAudit

원본 읽기 의미

원본 PList.data_loadPLAY_LISTITEM_INDEX 순으로 읽고 LIST_TEXT^로 나눈 뒤 FarPoint의 0~6열에 넣는다. 첫 필드는 1일 때만 활성이고, 여섯 번째 필드인 PageN 문자열에서만 ASCII 공백을 제거한다. 다른 필드는 공백·한글·기호를 포함해 변경하지 않는다.

새 감사도 이 경계를 그대로 사용한다.

  • 정확히 7필드인지 검사한다.
  • 원문을 다시 ^로 결합했을 때 DB 문자열과 code-unit 단위로 같아야 한다.
  • 페이지 표시값 이외의 필드는 정규화하지 않는다.
  • 프로그램 코드는 8자리 숫자, ITEM_INDEX는 0부터 연속이어야 한다.
  • 제어문자, U+FFFD replacement 문자와 알려진 UTF-8/codepage 손상 신호를 거부한다.
  • 같은 프로그램을 현재 LegacyNamedPlaylistPersistenceService.LoadAsync로 다시 읽어 활성값, 5개 selection 필드와 페이지 표시값을 모두 대조한다.

감사 오류와 정상 출력에는 DC_TITLE, LIST_TEXT, 종목명, 테마명, 프로그램 코드 또는 인증정보를 포함하지 않는다.

실제 Oracle/MariaDB 결과

실제 운영 연결에 고정 SQL과 bind 변수로 SELECT만 실행했다. DB write, Tornado2 명령과 원본 저장소 변경은 수행하지 않았다.

항목 결과
활성 DC_LIST 정의 23
연결된 PLAY_LIST 2,213
정확히 7필드로 parse 2,213
원문 무손상 round-trip 2,213
replacement/mojibake 신호 0
비연속 ITEM_INDEX 프로그램 0
현재 persistence 결과 불일치 0
단일 route와 결과로 분류 2,213
기본 감사에서 selectionMapped 1,581
기본 감사에서 안전 차단 632
별도 GraphE fresh proof에서 추가 selectionMapped 22
별도 GraphE fresh proof에서 안전 차단 14
두 read-only 증거를 합친 selectionMapped 1,603
두 read-only 증거를 합친 안전 차단 610

단일 route와 결과로 분류는 모든 행이 송출 가능하다는 뜻이 아니다. 기본 NamedPlaylistReadOnlyDbAudit는 KRX·해외종목 복원까지 적용해 1,581행을 단일 current selection으로 매핑하고 632행을 차단한다. 이 632행에는 별도 production source 증거가 필요한 manual 36행이 들어 있다. NamedManualRestoreReadOnlyAudit가 그 36행을 독립적으로 다시 읽어 GraphE 22행을 fresh selection으로 만들고 14행을 차단하므로, 두 read-only 증거를 행별로 합친 결과는 1,603행 selectionMapped, 610행 차단이다.

selectionMapped는 raw 7필드에서 현재 typed selection을 하나 만들었다는 뜻만 갖는다. 각 장면의 시세·재무 데이터 결과, 컷·배경 자산, package context, PREPARE 성공과 Tornado2/PGM 출력은 포함하지 않으며 각각 별도 gate를 통과해야 한다.

route 집계

route 전체 selectionMapped 차단
기존 닫힌 매퍼 1,250 1,250 0
업종 fixed 닫힌 매퍼 24 24 0
비교 fresh identity 99 74 25
해외지수 candle fresh identity 61 61 0
NXT theme fresh identity 48 0 48
KRX theme fresh identity 587 94 493
해외종목 fresh identity 78 78 0
GraphE fresh materialization 36 22 14
unique closed mapper 없음 30 0 30
합계 2,213 1,603 610

기본 감사 수치 1,581은 기존 닫힌 매퍼 1,250행, 업종 fixed 24행, 비교 74행, 해외지수 candle 61행, KRX theme 94행 및 해외종목 78행의 합이다. GraphE 22행은 별도 fresh proof의 성공분이다. 현재 PList의 manual 36행은 모두 GraphE이고 FSell/VI 저장 행은 0건이다.

기존 fixed 매퍼는 임의 문자열 허용으로 판정하지 않았다. 원본 runtime 해외.ini·환율.ini·지수.ini를 각각 현재 Web 계약에 고정된 SHA-256으로 검증하고, 원본 MainForm과 같이 각 줄을 trim한 뒤 현재 fixedLegacySignature의 exact 5필드 규칙으로 변환했다. 원본 328 action에서 현재 명시적으로 unavailable인 수동 6개를 제외하고, 같은 raw 서명으로 겹치는 후보는 unique로 인정하지 않았다. 이 exact whitelist와 일치하지 않은 30행은 가까운 s5001 등을 추정하지 않고 차단했다.

fresh restore 결과와 차단 사유

사유 의미
comparison market identity 유실 25 원본 2열판이 KOSPI/KOSDAQ/NXT/해외 구분을 저장하지 않았다.
KRX theme identity 없음 271 원본 제목 exact lookup으로 현재 유일한 KRX identity를 찾을 수 없다.
KRX theme 저장 행 무효 16 닫힌 5·6·12행 문법과 필수 field를 만족하지 않는다.
KRX theme preview empty 193 현재 제목/code는 찾았지만 validated live item이 없다.
KRX theme 지원 불가 action 13 현재 5·6·12행 장면 계약으로 유일하게 해석할 수 없다.
GraphE 현재 저장값 무효 9 production GraphE parser가 현재 INPUT_* 저장값을 typed record로 만들 수 없다.
GraphE 이름 identity 중복 5 원본 이름-only identity로 현재 저장 행 하나를 결정할 수 없다.
NXT theme preview empty 48 현재 title/code 매핑은 성공했으나 MariaDB validated preview가 비어 있다.
unique closed mapper 없음 30 hash가 고정된 328-action runtime 계약의 어느 단일 서명과도 일치하지 않아 nearest scene을 추정하지 않는다.

KRX theme 587행 중 94행은 제목 exact lookup, 현재 code 및 validated preview를 같은 correlated batch에서 확인해 모두 현재 code로 remap했다(codeRemapped=94). 나머지 493행은 위 네 KRX 사유로 차단했다. 해외종목 78행은 현재 Oracle의 exact 입력명에서 유일한 symbol/nation을 얻고 original action 문법을 보존해 78/78을 매핑했다. 두 경로 모두 timeout/불명확 응답을 재시도하지 않으며 raw 이름·code를 감사 출력에 기록하지 않는다.

GraphE 36행의 별도 감사는 같은 identity를 두 번 읽어 64자리 row version이 변하지 않는지 확인하고 현재 종목 master의 유일한 identity와 cut 계약을 함께 검증했다. 22행만 materialize했고 저장값 무효 9행과 identity 중복 5행은 첫 후보를 고르지 않고 차단했다. 상세 경계는 manual read-only 감사에 있다.

FSell/VI는 현재 PList 후보가 0행이지만 운영 production source를 opened-handle로 별도 읽었다. FSell은 3 audience마다 5행, VI는 9항목·2 page였고 두 번째 VI read의 version도 같았다. 네 운영 파일의 이름·크기·시각·SHA-256은 감사 전후 동일했고 import marker/intent를 만들거나 파일을 쓰지 않았다.

구현과 자동 근거

반복 실행

전체 실제 DB smoke에 이 감사가 포함된다.

dotnet run --project .\tools\MBN_STOCK_WEBVIEW.DbSmoke\MBN_STOCK_WEBVIEW.DbSmoke.csproj `
  -c Release --no-restore

감사는 실패 시 원문을 출력하지 않고 boundary 이름만 남긴다. timeout 또는 불명확 결과를 재시도하거나 반대 명령으로 보정하지 않는다.

자동 검증

LegacyPListReadAuditParserTests는 다음을 고정한다.

  • 한글·공백·빈 필드를 포함한 7필드 무손상 round-trip
  • 페이지 필드에만 적용되는 원본 ASCII 공백 제거
  • field 수, enabled 값, 프로그램 코드, item index 경계
  • replacement와 codepage 손상 신호 차단
  • typed persistence 대조와 연속 index
  • comparison/업종/해외지수/NXT·KRX theme/해외종목/manual route 분리
  • KRX current-code correlated remap과 5·6·12행 문법, 해외종목 symbol/nation correlation, GraphE double-read row-version·identity 중복 fail-closed
  • exact stock label과 nearest-label 거부, 알 수 없는 행의 Unmapped fail-closed

실제 기본 감사, 별도 manual fresh 감사와 전체 DB smoke는 REAL_DB_SMOKE: PASS로 끝났다. DB write와 Tornado2/PGM 명령은 0건이며 raw 값이나 인증정보도 출력하지 않았다.