# 저장 PList의 `테마_NXT` 복원

기준일: 2026-07-12
원본(읽기 전용): `C:\Users\MD\source\repos\MBN_STOCK_N`
현재 구현: `LegacyNamedNxtThemeRestoreService`, `MainWindow.NamedNxtThemeRestore.cs`, `named-nxt-theme-restore-workflow.js`

## 원본 동작

원본 `Control/UC4.cs`는 NXT 테마를 `테마_NXT ^ 제목(NXT) ^ 컷 ^ 테마-현재가(정렬) ^ PageN ^ code`로 저장한다. 그러나 `Scene/s5074.cs`, `s5077.cs`, `s5088.cs`는 저장 code를 그대로 사용하지 않고 장면 생성 때 MariaDB `SB_LIST`에서 `SB_TITLE`과 `SB_MARKET='NXT'`로 code를 다시 찾는다.

- NXT는 현재가만 지원한다. 예상체결가는 UC4에서 즉시 거부한다.
- 지원 컷은 5단(`s5074`, 5개), 6종목(`s5077`, 6개), 12종목(`s5088`, 12개)이다.
- 정렬은 입력순, 상승률순, 하락률순이다. 정렬 표기가 없으면 원본 최종 분기와 같이 하락률순이다.
- 장면 생성 시 로컬 시간이 00:00~12:59이면 PRE, 13:00~23:59이면 AFTER badge를 사용한다.
- 페이지 수는 현재 종목 수를 5/6/12로 나눈 올림값이며 현재 구현의 공통 안전 상한은 20페이지다.

## 현재 닫힌 복원 경계

Oracle named load의 `테마_NXT` 행은 기존 Web 문자열 매퍼를 통과하지 않는다. 모든 해당 행을 한 correlated native batch로 보내고 다음을 모두 만족할 때만 playable entry로 바꾼다.

1. 원본 7필드 grammar와 지원 컷·현재가·정렬이 정확하다.
2. MariaDB에서 suffix를 제거한 제목이 `SB_MARKET='NXT'` 안에서 정확히 한 행이다.
3. 그 현재 code도 NXT `SB_LIST` 안에서 유일하다.
4. `LegacyThemeSelectionService` preview가 schema, code/title/market, item code/index와 순서를 검증한다.
5. 정지되지 않았고 현재가가 0이 아닌 live item이 한 개 이상이다.

성공하면 송출 selection은 `PAGED_NXT_THEME`와 현재 code를 사용한다. 실패한 행은 화면에 남지만 미신뢰 상태이며 PREPARE가 차단된다. batch pending 동안 편집, DB mutation, 페이지 preflight와 PREPARE도 잠긴다. native 오류와 각 행 실패는 `retryable=false`이고 자동 재시도하지 않는다.

## 과거 code와 저장 왕복 정책

제목이 exact unique인 경우에만 과거 code를 현재 code로 매핑한다. 이 매핑은 “비슷한 제목”이나 code 숫자 거리 추정이 아니라 원본 장면이 사용한 exact-title lookup을 재현한 것이다.

- 실행·페이지 조회: 현재 MariaDB code만 사용한다.
- 같은 named list를 다시 저장: native 검증을 통과한 현재 세션에서만 원본 group/title/cut/subtype/과거 code를 그대로 보존하고, PageN만 fresh live 결과로 다시 계산한다.
- 로컬 저장 후 재시작 또는 검증 표식이 없는 객체: 과거 code 왕복 저장 권한이 없다. named list를 다시 load해 fresh 검증해야 한다.
- PREPARE 직전 PRE/AFTER session을 로컬 시각으로 다시 계산하되, code/title 검증 proof와 원본 7필드 identity는 유지한다.

이 정책은 원본 PList와의 상호운용성을 유지하면서도 stale code를 Tornado 장면 조회에 사용하지 않게 한다.

## 실제 read-only 감사 결과

2026-07-12 실제 Oracle/MariaDB에 DB write 없이 실행한 결과다.

| 항목 | 결과 |
|---|---:|
| 활성 `테마_NXT` 행 | 48 |
| 서로 다른 저장 제목 | 24 |
| raw grammar 변형 | 1 |
| grammar | `5단 표그래프 / 테마-현재가(입력순)` 48행 |
| exact unique 현재 title/code로 매핑 | 48 |
| 저장 code와 현재 code가 다름 | 48 |
| validated preview가 비어 있음 | 48 |
| 현재 playable 복원 | 0 |

따라서 구현은 48행을 안전하게 식별하고 현재 code까지 찾지만, 현재 MariaDB의 해당 24개 테마에 preview item이 없으므로 모두 `PreviewEmpty`로 차단하는 것이 정상 결과다. 운영자가 `SB_ITEM`과 `v_all_stock` live 연계를 복구하기 전에는 이 행들을 PREPARE해서는 안 된다.

KRX `테마` 행은 이 batch의 후보가 아니며 이번 변경에서 자동 복원 정책을 바꾸지 않았다.

## 검증

- Core focused: 30/30 통과
- Web workflow/app integration focused: 12/12 통과
- actual DB audit: Oracle/MariaDB healthy, NXT 48행 전부 exact identity mapped, 48행 `PreviewEmpty`
- 전체 DB smoke는 같은 실행에서 비교 장면용 현재 종목 데이터가 변해 `s5026/s5029/s5087` 세 장면이 실패했다. NXT audit 자체 실패가 아니며 전체 smoke 성공으로 기록하지 않는다.

실제 Tornado 명령, 운영 DB write, 원본 파일 수정은 수행하지 않았다.
