중간저장 04.20

This commit is contained in:
2026-04-20 20:06:18 +09:00
parent 210b546130
commit 7cedeef5a9
180 changed files with 36496 additions and 918 deletions

View File

@@ -1,8 +1,24 @@
# 2026-04-17 현재 구현 상태 정리 # 2026-04-18 현재 구현 상태 정리
이 문서는 2026-04-17 기준으로 Tornado3 2026 Election 프로젝트에 실제 반영된 구현 사항과 검증 결과를 한 번에 확인하기 위한 현행화 문서다. 이 문서는 2026-04-18 기준으로 Tornado3 2026 Election 프로젝트에 실제 반영된 구현 사항과 운영 기준을 한 번에 확인하기 위한 현행화 문서다.
## 1. 데이터 화면 ## 1. 화면 구성
- 메인 네비게이션은 아래 페이지를 제공한다.
- `통합 스케줄`
- `노멀`
- `좌상단`
- `하단`
- `비디오월`
- `사전데이터`
- `데이터`
- `컷리스트`
- `설정`
- `로그`
- `사전데이터` 페이지는 저장형 역대 데이터 조회/수정/저장 전용 화면이다.
- `컷리스트` 페이지는 전체 컷 목록, 채널 필터, 컷 시간, 썸네일 현황을 관리하는 운영 화면이다.
## 2. 데이터 화면
- 데이터 화면에서 원본 JSON 형태로 보여주던 `데이터 시트`는 제거했다. - 데이터 화면에서 원본 JSON 형태로 보여주던 `데이터 시트`는 제거했다.
- 현재 데이터 화면에는 아래 두 시트만 남겨두었다. - 현재 데이터 화면에는 아래 두 시트만 남겨두었다.
@@ -12,158 +28,162 @@
- `전체보기` 선택 시, 현재 수신 가능한 지역 데이터를 작은 카드 형태로 한 번에 볼 수 있도록 바꿨다. - `전체보기` 선택 시, 현재 수신 가능한 지역 데이터를 작은 카드 형태로 한 번에 볼 수 있도록 바꿨다.
- 표시 형식: `지역명 - 개표율` - 표시 형식: `지역명 - 개표율`
- 목적: 전체 데이터 수신 상태를 빠르게 확인하기 위한 개요 화면 - 목적: 전체 데이터 수신 상태를 빠르게 확인하기 위한 개요 화면
- 데이터 탭에는 `선거구명`, `지역 코드` 외에 `시도명`, `송출 선거구명`, `StatusText`를 함께 표시한다.
## 2. 후보/개표 데이터 처리 규칙 ## 3. SBS API 실연동 범위
- 현재 실연동 대상 선거 종류는 아래와 같다.
- `광역단체장`
- `교육감`
- `기초단체장`
- `광역단체장`
- 개표: 지원
- 사전 투표율: 지원
- `교육감`
- 개표: 지원
- 사전 투표율: SBS API 미지원
- `기초단체장`
- 개표: 지원
- 사전 투표율: SBS API 미지원
- `교육감`, `기초단체장`의 사전 방송 데이터는 저장형 사전 데이터(JSON)로 보완한다.
## 4. 저장형 사전데이터
- 저장형 사전데이터는 `Tornado3_2026Election/Assets/Data/pre_election_history.json`에 보관한다.
- 2026-04-18 기준 저장 건수는 아래와 같다.
- `광역단체장`: 전국 17개 시도
- `교육감`: 전국 17개 시도
- `기초단체장`: 전국 262개 선거구
- 커버리지 요약:
- `광역단체장` 당선자: 1995~2022
- `광역단체장` 투표율: 2002~2022
- `교육감`: 직선제 기준 2010~2022
- `기초단체장`: 공식 API 기준 2002~2022
- `사전데이터` 페이지에서 아래 기능을 제공한다.
- 선거 종류별 레코드 선택
- 지역별 역대 투표율 카드 보기
- 지역별 역대 당선자 카드 보기
- 엑셀형 편집 그리드에서 직접 수정
- `사전데이터 저장` 버튼으로 JSON 반영
- `사전_역대당선자`, `사전_역대당선자_교육감`, `사전_역대당선자_기초단체장` 편집 행에는 `ColorParty` 콤보박스가 있다.
- 표기 정당명은 그대로 두고
- 송출용 색상 포맷 정당만 별도로 지정할 수 있다.
- 예: 표기 `한나라당`, 색상 포맷 `국민의힘`
- 사전데이터 편집 화면에서는 개별 항목의 `SourceUrl`을 노출하지 않는다.
## 5. 사전 컷 연동 규칙
- `사전_역대당선자*` 계열은 `사전``개표` 양쪽 단계에서 모두 사용할 수 있다.
- `사전_역대투표율*` 계열은 `사전` 단계 전용이다.
- 사전 컷 송출 시에는 저장형 사전데이터의 `TurnoutHistory`, `WinnerHistory`를 사용한다.
- 역사 정당명은 색상 포맷 선택과 별개로 아래 별칭을 함께 처리한다.
- `민주자유당`, `신한국당`, `한나라당`, `새누리당`, `자유한국당` -> `국민의힘`
- `민주당`, `새천년민주당`, `열린우리당`, `새정치민주연합` -> `더불어민주당`
- `무소속` -> `무기타`, `무소속기타`
## 6. 후보/개표 데이터 처리 규칙
- 후보가 나오는 데이터는 개표 데이터 기준으로 처리한다. - 후보가 나오는 데이터는 개표 데이터 기준으로 처리한다.
- 개표 데이터에는 반드시 `개표율`도 함께 표시되도록 맞췄다. - 개표 데이터에는 반드시 `개표율`도 함께 표시되도록 맞췄다.
- 개표율 텍스트는 단순 숫자가 아니라 아래 형식으로 송출되도록 수정했다. - 개표율 텍스트는 아래 형식으로 송출다.
- 예: `개표 98.7%` - 예: `개표 98.7%`
- 광역단체장 컷에서는 지역명 대신 실제 직함이 나오도록 보정했다. - 수동 판정값이 있으면 수동 판정을 우선 적용하고, 없으면 API 판정값을 사용한다.
- 예: `부산광역시` -> `부산시장` - SBS API 판정 코드는 아래 기준으로 반영한다.
- 예: `경기도` -> `경기도지사`
## 3. 당선/유력/확정 코드 반영
SBS API 판정 코드는 아래 기준으로 반영되어 있다.
- `40`: 유력 - `40`: 유력
- `50`: 확정 - `50`: 확정
- `60`: 개표중 당선 - `60`: 개표중 당선
- `80`: 무투표 당선 - `80`: 무투표 당선
- `90`: 개표마감 당선 - `90`: 개표마감 당선
추가 반영 사항: ## 7. 직함 표기 규칙
- `80` 무투표 당선 케이스도 실제 발생 가능한 값으로 보고 정상 처리하도록 반영했다. - `광역단체장`, `교육감`, `기초단체장`은 전 컷 공통으로 지역명 대신 직함 기준 표기를 사용한다.
- 수동 판정값이 있으면 수동 판정을 우선 적용하고, 없으면 API 판정값을 사용한다. - 적용 대상은 컷 종류를 가리지 않고 executive 계열 전체다.
- 예시:
- `서울특별시` -> `서울시장`
- `전라남도` -> `전라남도지사`
- `부산광역시` -> `부산광역시교육감`
- `경상남도 창원시` -> `창원시장`
- 송출 시 `선거구명`, `시도명`, `RegionName`, `ElectionDistrictName`, `DistrictName` 계열 값에 같은 규칙을 적용한다.
## 4. 유확당 변수 처리 규칙 ## 8. 유확당 변수 처리 규칙
`유확당` 계열 변수는 전 컷 공통으로 아래 순서로 처리하도록 정리했다. `유확당` 계열 변수는 전 컷 공통으로 아래 순서로 처리다.
1. 해당 컷에 존재하는 `유확당*` 변수들을 먼저 모두 `visible=false`로 숨긴다. 1. 해당 컷에 존재하는 `유확당*` 변수 먼저 모두 `visible=false`로 숨긴다.
2. 실제 판정 이미지가 필요한 후보에게만 `SetValue(...)`를 넣는다. 2. 실제 판정 이미지가 필요한 후보에게만 `SetValue(...)`를 넣는다.
3. 값이 들어간 변수만 다시 `visible=true`로 켠다. 3. 값이 들어간 변수만 다시 `visible=true`로 켠다.
적용 범위: 적용 범위:
- 1위/2위/3위 후보 슬롯 공통 - `1-2위`
- `1-2위`, `1-3위`, `당선`, `모든후보`, `접전`, `실시간` 계열 포함 - `1-3위`
- `당선`
- `모든후보`
- `접전`
- `초접전`
- `이시각1위`
검증 결과: ## 9. RGB / 정당 색상 매핑
- 최신 전체 스캔 기준 `유확당` 변수를 사용하는 컷은 총 69개
- 전체 변수 스캔 리포트: `TSCN_VARIABLE_DISCOVERY_ELECT2026_NORMAL.md`
- 라이브 검증 리포트: `LIVE_VALIDATE_1-2위_ani_광역단체장_judgement_visibility.md`
## 5. RGB / 정당 색상 매핑
정당 색상은 아래 경로의 RGB 텍스트 기준으로 매핑한다.
- `E:\김의연\지역민방\T3_Cut\Elect2026_Normal_민방\RGB\`
현재 적용 규칙:
- 정당 색상은 RGB txt 기준으로 매핑한다.
- RGB txt에 `style > ... > color` 지시가 있으면 이미지 교체보다 `SetStyleColor(...)`를 우선 사용한다. - RGB txt에 `style > ... > color` 지시가 있으면 이미지 교체보다 `SetStyleColor(...)`를 우선 사용한다.
- style color 지시가 없는 항목만 기존 asset 기반 `SetValue(...)` 경로를 사용한다. - style color 지시가 없는 항목만 기존 asset 기반 `SetValue(...)` 경로를 사용한다.
- `사전_역대당선자*` 계열도 `ColorParty` 또는 정당 별칭을 기준으로 동일한 색상 자산 해석을 사용한다.
- 상세 매핑은 `RGB_SPEC_CUT_MAPPING.md`를 기준으로 관리한다.
특히 `1-2위_ani_광역단체장.txt` 기준으로 아래 항목을 실제 style color 변경 방식으로 맞췄다. ## 10. 컷 카탈로그 / 실제 디자인 매칭
- 정당판: `style > face > color` - 컷 목록은 폴더 스캔이 아니라 `FormatCatalogService`의 하드코딩 카탈로그 기준으로 관리한다.
- 득표율: `style > edge > color` - 2026-04-18 기준 현재 카탈로그는 실제 `.tscn` 파일 기준으로 정리되어 있다.
- 정당바: `style > face > color` - `Elect2026_Bottom_민방`: 15컷
- `Elect2026_Normal_민방`: 63컷
- `Elect2026_Top_민방`: 11컷
- 총 89컷
- 실제 `.tscn` 파일이 없는 오래된 별칭(`_L`, `_END`, `_7680` 등)은 현재 카탈로그에서 제거했다.
- 다만 기존 저장 상태/큐 복원을 위해 예전 `FormatId`는 내부 별칭 맵으로 현재 실제 컷 이름에 자동 연결한다.
- 2026-04-18 전수 감사 기준:
- 카탈로그 등록 컷 중 실제 씬 누락 `0건`
관련 구현 사항: ## 11. 썸네일 운영
- RGB txt에서 style color 대상을 파싱하도록 `PartyColorCatalog`를 보완했다. - 썸네일 저장 위치는 `Assets/Thumbnail/<template.Id>.png` 규칙을 사용한다.
- Karisma 적용 단계에서 `IKAStyle.SetStyleColor`를 호출하도록 처리 경로를 추가했다. - 콤보박스에서 컷을 선택하면 해당 썸네일을 즉시 프리뷰에 표시한다.
- style color 대상에는 잘못된 이미지 값이 들어가지 않도록 분기 처리했다. - 로딩 우선순위:
1. 프로젝트 내부 `Assets/Thumbnail`
2. 번들된 앱 내부 `Assets/Thumbnail`
3. 기본 앱 아이콘
- `컷리스트` 페이지에서는 전체 컷 썸네일 보유 현황과 채널별 컷 목록을 함께 확인할 수 있다.
- `썸네일 생성` 기능은 Karisma에 씬을 로드한 뒤 320x180 PNG를 저장한다.
검증 문서: ## 12. 설정 저장 및 복원
- 컷별 RGB 매핑 문서: `RGB_SPEC_CUT_MAPPING.md`
- style color 검증 리포트: `LIVE_VALIDATE_1-2위_ani_광역단체장_style.md`
## 6. 후보 슬롯 수 처리
- 실제 컷에 존재하는 후보 슬롯 수만큼만 값을 넣도록 보정했다.
- 예를 들어 후보가 2명까지 있는 컷에서는 `후보명03`, `유확당03` 같은 존재하지 않는 변수를 억지로 쓰지 않도록 수정했다.
- 최신 변수 카탈로그를 우선 참조해서 컷별 실제 변수 개수를 판단한다.
우선순위:
1. `TSCN_VARIABLE_DISCOVERY_ELECT2026_NORMAL.md`
2. `TSCN_VARIABLE_DISCOVERY_E_DRIVE.md`
3. 기타 `TSCN_VARIABLE_DISCOVERY*.md`
추가 사항:
- 변수 카탈로그 파일들은 실행 출력 폴더와 `AppX` 배포 폴더에도 함께 복사되도록 정리했다.
## 7. 설정 저장 및 API 주기
- API 자동 갱신 기본값을 `60초`로 변경했다.
- 설정 변경 시 별도 저장 버튼 없이 자동 저장되도록 반영했다.
자동 저장 대상에는 아래 항목들이 포함된다.
- 설정은 변경 즉시 자동 저장한다.
- 자동 저장 대상에는 아래 항목들이 포함된다.
- API 자동 갱신 ON/OFF - API 자동 갱신 ON/OFF
- API 갱신 주기 - API 갱신 주기
- 데이터 관련 주요 선택 상태 - 데이터 관련 주요 선택 상태
- 앱 재시작 후 복원이 필요한 주요 설정 - 채널별 큐 상태
- 컷별 재생 시간
- 방송사 지역 필터
- 저장된 큐에 예전 컷 ID가 들어 있어도 복원 시 현재 canonical 컷으로 자동 정규화한다.
관련 구현 파일: ## 13. 검증 상태
- 상태 저장 모델: `Tornado3_2026Election/Persistence/AppState.cs` - `dotnet build .\Tornado3_2026Election\Tornado3_2026Election.csproj` 통과
- 자동 저장 루프: `Tornado3_2026Election/ViewModels/MainViewModel.cs` - 2026-04-18 컷 감사 결과 `MISSING_SCENES=0`
- 현재 남아 있는 빌드 경고는 아래와 같다.
- `WindowsBase` 참조 충돌 경고
- MSIX 인증서 경고
- `MockTornado3Adapter.ConnectionChanged` 미사용 경고
## 8. 데이터/라벨 관련 라이브 검증 결과 ## 14. 참고 문서
- `LIVE_VALIDATE_1-2위_ani_광역단체장.md` - `SYSTEM_SPEC.md`
- 기본 변수 송출 검증 - `INTEGRATION_NOTES_2026-04-15.md`
- `LIVE_VALIDATE_1-2위_ani_광역단체장_labels.md` - `RGB_SPEC_CUT_MAPPING.md`
- `개표 99.9%` 형식, 광역장 직함 표기 검증
- `LIVE_VALIDATE_1-2위_ani_광역단체장_style.md`
- style color 적용 검증
- `LIVE_VALIDATE_1-2위_ani_광역단체장_judgement_visibility.md`
- `유확당` visible 처리 검증
- `TSCN_VARIABLE_DISCOVERY_ELECT2026_NORMAL.md` - `TSCN_VARIABLE_DISCOVERY_ELECT2026_NORMAL.md`
- Elect2026_Normal_민방 전체 컷 변수 스캔 결과 - `LIVE_VALIDATE_1-2위_ani_광역단체장.md`
- `LIVE_VALIDATE_1-2위_ani_광역단체장_labels.md`
## 9. 스케줄 지역 선택 송출 - `LIVE_VALIDATE_1-2위_ani_광역단체장_style.md`
- `LIVE_VALIDATE_1-2위_ani_광역단체장_judgement_visibility.md`
- 스케줄 추가 시 컷만 고르는 방식에서 `컷 + 지역 범위`를 함께 고르는 방식으로 확장했다.
- 스케줄 제어 패널에 지역 선택 콤보박스를 추가했다.
- 지역 콤보박스 구성은 아래와 같다.
- 첫 번째: `전체`
- 두 번째: `선택권역`
- 이후: 개별 지역 목록
- 개별 지역 선택 시 해당 지역 데이터만 송출한다.
- `전체` 선택 시 현재 포맷에 대응되는 지역 데이터 전체를 순서대로 송출한다.
- `선택권역` 선택 시 방송사 설정에 잡혀 있는 권역만 순서대로 송출한다.
- `전체``선택권역`은 데이터 순서에 따라 송출 가능한 지역만 순차적으로 처리한다.
- 스케줄 큐 항목에는 지역 범위 정보가 함께 저장되며, 앱 상태 저장/복원에도 포함된다.
- 송출 중에는 큐 카드와 현재/다음 표시에서 실제 송출 중인 지역명이 함께 보이도록 반영했다.
구현 범위:
- 스케줄 UI 콤보박스 추가
- 스케줄 아이템에 지역 범위 저장
- 스케줄 상태 저장/복원 반영
- 송출 시 지역별 API 재조회 후 순차 송출
## 10. 현재 기준 운영 요약
- 데이터 화면은 `개표율 시트 + 후보 시트 + 전체보기 카드` 기준으로 동작한다.
- 후보 데이터는 개표 데이터 기준으로 처리되고, 개표율이 함께 표시된다.
- 개표율은 항상 `개표 x.x%` 형식으로 송출한다.
- 광역단체장 컷의 지역 표기는 지역명이 아니라 실제 직함 기준으로 보정한다.
- 정당 색상은 RGB txt 기준으로 매핑하며, style color 지시가 있으면 이미지보다 style color를 우선 적용한다.
- `유확당` 변수는 전 컷 공통으로 먼저 숨긴 뒤 필요한 경우에만 값을 넣고 다시 보이게 한다.
- 설정은 변경 즉시 자동 저장되며, API 기본 갱신 주기는 60초다.
- 스케줄은 `전체 / 선택권역 / 개별 지역` 기준으로 지역별 순차 송출이 가능하다.
## 11. 참고
이 문서는 완료된 구현 사항 위주로 정리했다. 이후 추가 변경이 생기면 같은 문서를 기준으로 계속 현행화한다.

View File

@@ -1,112 +1,114 @@
# 2026-04-15 통합 작업 메모 # 2026-04-15 통합 작업 메모
이 문서는 2026-04-15에 시작한 연동 점검 메모를 2026-04-18 기준으로 현행화한 기록이다.
## 0. 작업 요약 ## 0. 작업 요약
- SBS 선거 API 기준으로 `광역단체장(3)`, `기초단체장(4)`, `교육감(11)` 실연동을 정리했다. - SBS 선거 API 기준 실연동 범위를 `광역단체장(3)`, `기초단체장(4)`, `교육감(11)`으로 정리했다.
- 선거 종류 변경 시 실제 SBS `선거구` 코드 기준으로 지역 목록과 요청 코드가 맞물리도록 보정했다. - 선거 종류 변경 시 실제 SBS `선거구` 코드 기준으로 지역 목록과 요청 코드가 맞물리도록 보정했다.
- `기초단체장` 지역 옵션 교체 중 발생하던 `ArgumentNullException (key)`를 ViewModel 방어 로직으로 정했다. - `기초단체장` 지역 옵션 교체 중 발생하던 `ArgumentNullException (key)``ObservableCollection during CollectionChanged` 계열 UI 전환 이슈를 방어 로직으로 정했다.
- 콤보박스 변경 후 값이 그대로 남아 있던 문제를 선택 변경 즉시 재조회 흐름으로 바꿨다. - `교육감`, `기초단체장` 사전 방송용 데이터 공백은 저장형 사전데이터(JSON)로 보완했다.
- `System.Text.Json.JsonException`은 region API 응답이 최상위 배열(`[...]`)인 경우를 허용하도록 수정했다. - 사전데이터는 `사전데이터` 페이지에서 조회/수정/저장 가능하게 만들었다.
- 미지원 조합에서는 이전 값이 계속 보이지 않도록 상태 문구와 보드 표시 규칙을 보정했다. - `사전_역대당선자*` 계열은 표기 정당과 색상 포맷 정당을 분리해 저장할 수 있게 했다.
- 직함 표기는 광역단체장만이 아니라 `광역단체장`, `교육감`, `기초단체장` 전 컷으로 확대했다.
- 컷 카탈로그는 실제 `.tscn` 이름 기준으로 정리했고, 과거 별칭은 호환용 alias로만 유지한다.
- 컷 썸네일은 `Assets/Thumbnail`에 저장하고, 제어 패널과 컷리스트에서 프리뷰를 표시하도록 정리했다.
## 1. 선거종류(Sunger Types)별 점검 결과 ## 1. 선거 종류별 연동 결과
### `3` 시도지사 / 광역단체장 ### `3` 광역단체장
- 선거구 목록: `sungerInfo/region?type=선거구&sungerType=3` - 선거구 목록: `sungerInfo/region?type=선거구&sungerType=3`
- 개표 요청: `gaepyo/3/sungergus?ids=<선거구 id>` - 개표 요청: `gaepyo/3/sungergus?ids=<선거구 id>`
- 사전 투표율 요청: `tupyo/3/sidos?ids=<시도 id>` - 사전 투표율 요청: `tupyo/3/sidos?ids=<시도 id>`
- 부산 기준 확인:
- 개표 코드: `3260000`
- 사전 코드: `26`
- 결론: - 결론:
- 개표 연동 정상 - 개표 연동 정상
- 사전 투표율 연동 정상 - 사전 투표율 연동 정상
### `4` 시군구장 / 기초단체장 ### `4` 기초단체장
- 선거구 목록: `sungerInfo/region?type=선거구&sungerType=4` - 선거구 목록: `sungerInfo/region?type=선거구&sungerType=4`
- 개표 요청: `gaepyo/4/sungergus?ids=<선거구 id>` - 개표 요청: `gaepyo/4/sungergus?ids=<선거구 id>`
- 부산 해운대구 기준 확인:
- 개표 코드: `4260900`
- 결론: - 결론:
- 개표 연동 정상 - 개표 연동 정상
- 사전 투표율은 SBS API가 `400` 반환으로 미지원 - SBS API 사전 투표율은 미지원
- 사전 방송용 역사 데이터는 저장형 사전데이터에서 제공
### `11` 교육감 ### `11` 교육감
- 선거구 목록: `sungerInfo/region?type=선거구&sungerType=11` - 선거구 목록: `sungerInfo/region?type=선거구&sungerType=11`
- 개표 요청: `gaepyo/11/sungergus?ids=<선거구 id>` - 개표 요청: `gaepyo/11/sungergus?ids=<선거구 id>`
- 부산 기준 확인:
- 개표 코드: `11260000`
- 결론: - 결론:
- 개표 연동 정상 - 개표 연동 정상
- 사전 투표율은 SBS API가 `400` 반환으로 미지원 - SBS API 사전 투표율은 미지원
- 사전 방송용 역사 데이터는 저장형 사전데이터에서 제공
## 2. SBS 선거 API 실연동 ## 2. SBS API 제한 사항
- 기준 문서: `https://documenter.getpostman.com/view/39408930/2sBXqCNNyS`
- 기준 호스트: `http://202.31.153.141:8421/`
- API 응답 헤더에 charset이 없어서, 본문은 UTF-8 바이트 기준으로 직접 디코딩한다.
### 현재 지원 범위
- `광역단체장`
- 사전 투표율: 지원
- 개표: 지원
- `교육감`
- 사전 투표율: 미지원
- 개표: 지원
- `기초단체장`
- 사전 투표율: 미지원
- 개표: 지원
### 확인된 API 제한사항
- `tupyo/4/...` 계열은 현재 `기초단체장`에 대해 `400`을 반환한다. - `tupyo/4/...` 계열은 현재 `기초단체장`에 대해 `400`을 반환한다.
- `tupyo/11/...` 계열은 현재 `교육감`에 대해 `400`을 반환한다. - `tupyo/11/...` 계열은 현재 `교육감`에 대해 `400`을 반환한다.
- 위 두 경우는 앱 오류가 아니라 API 제공 범위 밖으로 본다. - 위 두 경우는 앱 오류가 아니라 API 제공 범위 밖으로 본다.
- 실제 운영에서는 저장형 사전데이터로 공백을 메운다.
## 3. 기초단체장 지역 선택 규칙 ## 3. 저장형 사전데이터 운영
- 기초단체장 목록은 `시도 + 시군구/기초단체장명` 결합 문자열로 표시한다. - 저장 위치: `Tornado3_2026Election/Assets/Data/pre_election_history.json`
- 예: `부산광역시 해운대구` - 2026-04-18 기준 저장 건수:
- 실제 개표 요청은 SBS API의 `선거구 id`를 사용한다. - `광역단체장` 17건
- 예: `부산광역시 해운대구 -> 4260900` - `교육감` 17건
- 데이터 탭의 `지역 코드`는 현재 선택된 요청 코드(시도 코드 또는 선거구 코드)를 표시한다. - `기초단체장` 262건
- 기초단체장 선택 목록은 API `sungerInfo/region?type=선거구&sungerType=4` 기준으로 구성한다. - 페이지 기능:
- 선거 종류/지역 선택
- 역대 투표율 카드
- 역대 당선자 카드
- 엑셀형 편집
- 저장
- `사전_역대당선자`, `사전_역대당선자_교육감`, `사전_역대당선자_기초단체장``ColorParty`를 저장할 수 있다.
## 4. 데이터 탭 표시 기준 ## 4. 지역 선택 / 전환 안정화
- 데이터 탭에는 기존 `선거구명`, `지역 코드` 외에 다음 정보를 함께 표시한다.
- `시도명`
- `송출 선거구명`
- 데이터 탭은 원본 JSON 전문을 그대로 보여주는 용도는 아니다.
- API 응답을 ViewModel용 값으로 정규화한 뒤 표시한다.
- `DistrictName`은 선택/저장용 원본 문자열로 유지한다.
- 송출용 분리 값은 별도 필드로 관리한다.
- `RegionName`
- `ElectionDistrictName`
- Karisma/Tornado3 송출 변수 `시도명NN`, `선거구명NN`은 위 분리 값을 직접 사용한다.
## 5. Null-Key 예외 원인과 처리
- 원인:
- `기초단체장`으로 전환하면서 `DistrictOptions`를 교체하는 순간,
`ComboBox.SelectedValue`가 일시적으로 `null`을 보낼 수 있었다.
- 이 값이 그대로 `Dictionary.TryGetValue(null, ...)` 경로로 들어가며
`System.ArgumentNullException (Parameter 'key')`가 발생했다.
### 적용한 방어 규칙
- `DistrictName`, `DistrictCode`, `ElectionType`는 내부적으로 null이 아닌 문자열 상태를 유지한다. - `DistrictName`, `DistrictCode`, `ElectionType`는 내부적으로 null이 아닌 문자열 상태를 유지한다.
- 지역 옵션 교체 중에는 `_isUpdatingDistrictOptions` 플래그로 transient UI 값을 무시한다. - 지역 옵션 교체 중에는 `_isUpdatingDistrictOptions` 플래그로 transient UI 값을 무시한다.
- 옵션 교체 전의 `preferredName`, `preferredCode`를 캡처해 마지막 유효 선택값 기준으로 복원한다. - 사전데이터용 지역 콤보박스도 별도 `_isUpdatingPreElectionHistoryDistrictOptions` 플래그로 같은 문제를 피한다.
- 선택 변경 뒤에는 debounce 후 자동 재조회한다.
## 6. 변수 매핑 관련 기록 ## 5. 직함 표기 확대
- Karisma 장면 변수 지원 범위 로깅을 추가했다. - 현재 직함 표기는 `광역단체장`, `교육감`, `기초단체장` 전부에 적용한다.
- 현재 컷에 없는 변수명은 경고로 확인 가능하다. - 적용 대상 변수:
- `선거구명`
- `시도명`
- `RegionName`
- `ElectionDistrictName`
- `DistrictName`
- 예:
- `서울특별시` -> `서울시장`
- `전라남도` -> `전라남도지사`
- `부산광역시` -> `부산광역시교육감`
- `창원시` -> `창원시장`
## 6. 컷 카탈로그 / 실제 씬 파일
- 현재 카탈로그는 실제 존재하는 `.tscn` 파일명 기준으로 정리했다.
- 2026-04-18 기준 canonical 카탈로그:
- Bottom 15컷
- Normal 63컷
- Top 11컷
- 총 89컷
- 기존에 사용되던 `_L`, `_END`, `_7680` 계열은 현재 카탈로그에서 제거했다.
- 다만 저장된 큐/상태 복원을 위해 legacy format alias는 유지한다.
- 전수 감사 기준 실제 씬 누락은 `0건`이다.
## 7. 썸네일
- 저장 위치: `Assets/Thumbnail/<template.Id>.png`
- 제어 패널의 컷 선택 콤보박스에서 프리뷰 표시
- `컷리스트` 페이지에서 전체 현황 확인
- `썸네일 생성` 기능은 Karisma에 씬을 로드해 320x180 PNG로 저장
## 8. 변수 매핑 관련 기록
- Karisma 장면 변수 지원 범위 로깅을 유지한다.
- 다음 alias 매핑을 보강했다. - 다음 alias 매핑을 보강했다.
- `기준시`, `기준시01`, `기준시02` - `기준시`, `기준시01`, `기준시02`
- `유권자수`, `유권자수01` - `유권자수`, `유권자수01`
@@ -114,64 +116,25 @@
- `득표수바NN` - `득표수바NN`
- `정당원NN` - `정당원NN`
- `정당색NN` - `정당색NN`
- 역사 당선자 컷은 저장형 사전데이터의 `ColorParty`와 정당 별칭 정규화를 함께 사용한다.
### 아직 주의가 필요한 변수군
- 의석수 계열
- 판세 지도/그래프 계열
- 경력/공약 계열
- 일부 사진/차트 전용 컷 변수
## 7. 콤보박스 변경 즉시 갱신
- `선거 종류`, `선거구명`, `지역 코드`, `방송 단계`가 바뀌면 짧은 debounce 후 자동으로 `RefreshAsync(...)`를 다시 호출한다.
- 지역 옵션 재구성 중(`_isUpdatingDistrictOptions`)이나 API 결과 반영 중(`_isApplyingRefreshResult`)에는
내부 setter 연쇄로 인한 재조회는 막는다.
- 이 변경으로 콤보박스만 바뀌고 `후보현황`, `정보값`이 이전 조회 결과에 머물러 있던 문제를 줄인다.
## 8. JSON 응답 파싱 보정
- `sungerInfo/region...` 계열은 `{ value: [...] }`가 아니라 최상위 배열(`[...]`)로 오는 경우가 있다.
- 클라이언트는 이제 `value` envelope와 최상위 배열을 모두 허용한다.
- 광역단체장/교육감/기초단체장 지역 옵션도 SBS `선거구` 목록 기준으로 읽어서 실제 요청 코드와 맞춘다.
- 선택 변경 debounce는 `CancellationTokenSource` 취소 대신 revision 비교로 바꿔
디버그 출력에 반복 `TaskCanceledException`이 쌓이는 흐름을 줄였다.
- 선거 종류 전환/상태 복원 도중 비동기 선거구 목록 갱신이 늦게 끝나더라도,
마지막 현재 선택값 기준으로 다시 매칭하도록 보정했다.
- 미지원 단계나 API 오류가 나면 `StatusText`에 마지막 경고 메시지를 남겨
화면에서 이유를 바로 확인할 수 있게 했다.
- 데이터 탭 상단에 `StatusText`를 노출해 마지막 갱신 상태/미지원 사유를 바로 볼 수 있게 했다.
- 미지원 조합에서는 투표율/후보 보드를 숨기고 상태 문구만 보이도록 해
이전 수신값을 현재 데이터로 오해하는 흐름을 줄였다.
## 9. 주요 변경 파일 ## 9. 주요 변경 파일
- `Tornado3_2026Election/Services/SbsElectionApiClient.cs` - `Tornado3_2026Election/Services/SbsElectionApiClient.cs`
- SBS API 요청/응답 처리
- 선거 종류별 지역 옵션 구성
- JSON 파싱 보정
- `Tornado3_2026Election/ViewModels/DataViewModel.cs` - `Tornado3_2026Election/ViewModels/DataViewModel.cs`
- null-safe 지역 선택 처리 - `Tornado3_2026Election/ViewModels/MainViewModel.cs`
- 선택 변경 즉시 갱신 - `Tornado3_2026Election/ViewModels/ChannelScheduleViewModel.cs`
- 상태 문구 및 미지원 조합 표시 - `Tornado3_2026Election/Services/PreElectionHistoryService.cs`
- `Tornado3_2026Election/MainWindow.xaml` - `Tornado3_2026Election/Services/CutThumbnailAssetCatalog.cs`
- 데이터 탭에 `시도명`, `송출 선거구명`, `StatusText` 표시 - `Tornado3_2026Election/Services/KarismaThumbnailGeneratorService.cs`
- `Tornado3_2026Election/Services/FormatCatalogService.cs`
- `Tornado3_2026Election/Services/KarismaTornado3Adapter.cs` - `Tornado3_2026Election/Services/KarismaTornado3Adapter.cs`
- `RegionName`, `ElectionDistrictName` 기반 송출 변수 매핑
- `Tornado3_2026Election/Domain/ElectionDataSnapshot.cs`
- 송출용 분리 지역 필드 추가
- `Tornado3_2026Election/Persistence/AppState.cs`
- 실제 연동 기준 기본값 정리
## 10. 현재 상태 및 남은 체크포인트 ## 10. 현재 체크 결과
- `dotnet build Tornado3_2026Election.slnx` 통과 - `dotnet build .\\Tornado3_2026Election\\Tornado3_2026Election.csproj` 통과
- 기존 경고는 유지 - 기존 경고는 유지
- `WindowsBase` 참조 충돌 경고 - `WindowsBase` 참조 충돌
- MSIX 인증서 경고 - MSIX 인증서 경고
- `MockTornado3Adapter.ConnectionChanged` 미사용 경고 - `MockTornado3Adapter.ConnectionChanged` 미사용 경고
- 코드상 연동/파싱/표시 흐름은 정리 완료 - 컷 카탈로그 감사 결과 실제 씬 누락 `0건`
- 남은 실운영 체크:
- 앱 실행 상태에서 `광역단체장 -> 교육감 -> 기초단체장` 전환 확인
- `개표 <-> 사전` 전환 시 상태 문구와 보드 표시 확인
- 실제 송출 컷에서 `시도명NN`, `선거구명NN` 반영 확인

View File

@@ -1,24 +1,22 @@
# RGB Spec Cut Mapping # RGB Spec Cut Mapping
- Updated: 2026-04-16 - Updated: 2026-04-19
- Source root: - Source root: `E:\김의연\지역민방\T3_Cut`
`E:\김의연\지역민방\T3_Cut` - 이 문서는 현재 화면에 노출되는 canonical 컷 이름 기준으로 정리한다.
- Runtime priority: - 과거 `_L`, `_END`, `_7680` 계열 이름은 저장 호환용 alias만 유지하며, 아래 표에는 적지 않는다.
1. explicit cut-to-RGB mapping
2. same-name RGB txt
3. heuristic fallback by similar file name
## Runtime Rule ## Runtime Rule
- RGB txt header가 `style > ... > color` 또는 `> ... > color` 형태면 해당 오브젝트는 이미지 `SetValue` 대신 `SetStyleColor`를 우선 적용 - RGB txt header가 `style > ... > color` 또는 `> ... > color` 형태면 해당 오브젝트는 이미지 `SetValue` 대신 `SetStyleColor`를 우선 적용한다.
- `face 2번째`처럼 순서가 명시된 경우 `SetStyleColor(..., order=1, ...)`로 적용 - `face 2번째`처럼 순서가 명시된 경우 `SetStyleColor(..., order=1, ...)`로 적용한다.
- `정당바`: RGB spec section `정당바` 우선 - `정당바` RGB spec section `정당바` 우선
- `정당판`: RGB spec section `정당판` 우선 - `정당판` RGB spec section `정당판` 우선
- `정당원`: RGB spec section `정당원` 우선, 없으면 `정당색`, `정당판`, `정당바` - `정당원` RGB spec section `정당원` 우선, 없으면 `정당색`, `정당판`, `정당바`
- `정당색`: RGB spec section `정당색` 우선, 없으면 `정당바`, `정당판` - `정당색` RGB spec section `정당색` 우선, 없으면 `정당바`, `정당판`
- `그룹`: `그룹`이 없으면 `정당바`, `정당판` - `그룹` `그룹`이 없으면 `정당바`, `정당판`
- 기존 png 자산이 있으면 그대로 사용 - 기존 png 자산이 있으면 그대로 사용한다.
- 기존 png가 없으면 `%LOCALAPPDATA%\Tornado3_2026Election\GeneratedPartyAssets` 아래에 RGB 기반 fallback png 생성 - 기존 png가 없으면 `%LOCALAPPDATA%\Tornado3_2026Election\GeneratedPartyAssets` 아래에 RGB 기반 fallback png 생성한다.
- `사전_역대당선자*` 계열은 `ColorParty` 값이 있으면 이를 우선 사용하고, 없으면 표기 정당명과 역사 정당 별칭을 기준으로 색상 자산을 찾는다.
## Verified ## Verified
@@ -30,84 +28,70 @@
## Implemented Mapping ## Implemented Mapping
### Normal ### Normal - 1~2위 / 1~3위 계열
| Template | RGB spec file | Note | | Canonical template | RGB spec file | Note |
| --- | --- | --- | | --- | --- | --- |
| `1-2위_ani_광역단체장` | `1-2위_ani_광역단체장.txt` | exact | | `1-2위_ani_광역단체장` | `1-2위_ani_광역단체장.txt` | exact |
| `1-2위_ani_기초단체장` | `1-2위_ani_기초단체장.txt` | exact | | `1-2위_ani_기초단체장` | `1-2위_ani_기초단체장.txt` | exact |
| `1-2위_ani_기초단체장_5760` | `1-2위_ani_기초단체장.txt` | shared | | `1-2위_ani_기초단체장_5760` | `1-2위_ani_기초단체장.txt` | shared |
| `1-2위_ani_기초단체장_L` | `1-2위_ani_기초단체장.txt` | shared |
| `1-2위_광역단체장` | `1-2위_광역단체장, 보궐.txt` | shared | | `1-2위_광역단체장` | `1-2위_광역단체장, 보궐.txt` | shared |
| `1-2위_광역단체장_5760` | `1-2위_광역단체장, 보궐.txt` | shared | | `1-2위_광역단체장_5760` | `1-2위_광역단체장, 보궐.txt` | shared |
| `1-2위_광역단체장_L` | `1-2위_광역단체장, 보궐.txt` | shared | | `1-2위_광역단체장_시도별영상` | `1-2위_광역단체장,기초단체장_시도별영상.txt` | family |
| `1-2위_광역단체장_시도별영상` | `1-2위_광역단체장,기초단체장_시도별영상.txt` | exact family |
| `1-2위_교육감` | `1-2위_교육감.txt` | exact | | `1-2위_교육감` | `1-2위_교육감.txt` | exact |
| `1-2위_기초단체장` | `1-2위_기초단체장.txt` | exact | | `1-2위_기초단체장` | `1-2위_기초단체장.txt` | exact |
| `1-2위_기초단체장_시도별영상` | `1-2위_광역단체장,기초단체장_시도별영상.txt` | exact family | | `1-2위_기초단체장_시도별영상` | `1-2위_광역단체장,기초단체장_시도별영상.txt` | family |
| `1-2위_보궐선거` | `1-2위_광역단체장, 보궐.txt` | inferred | | `1-2위_보궐선거` | `1-2위_광역단체장, 보궐.txt` | inferred |
| `1-3위_ani_광역단체장` | `1-3위_ani_광역단체장,보궐.txt` | exact family | | `1-3위_ani_광역단체장` | `1-3위_ani_광역단체장,보궐.txt` | family |
| `1-3위_보궐선거` | `1-3위_ani_광역단체장,보궐.txt` | inferred | | `1-3위_ani_기초단체장` | `1-3위_ani_기초단체장(5760동일).txt` | family |
| `1-3위_ani_기초단체장` | `1-3위_ani_기초단체장(5760동일).txt` | exact family |
| `1-3위_기초단체장_5760` | `1-3위_ani_기초단체장(5760동일).txt` | shared | | `1-3위_기초단체장_5760` | `1-3위_ani_기초단체장(5760동일).txt` | shared |
| `1-3위_기초단체장_L` | `1-3위_ani_기초단체장(5760동일).txt` | shared | | `1-3위_보궐선거` | `1-3위_ani_광역단체장,보궐.txt` | inferred |
| `1-3위_기초단체장_L_1` | `1-3위_ani_기초단체장(5760동일).txt` | shared |
### Normal - 경력 / 당선 / 이시각1위 / 접전
| Canonical template | RGB spec file | Note |
| --- | --- | --- |
| `경력_광역단체장_in` | `경력.txt` | shared | | `경력_광역단체장_in` | `경력.txt` | shared |
| `경력_기초단체장_in` | `경력.txt` | shared | | `경력_기초단체장_in` | `경력.txt` | shared |
| `당선_광역단체장` | `당선.txt` | shared | | `당선_광역단체장` | `당선.txt` | shared |
| `당선_광역단체장_HD` | `당선.txt` | shared | | `당선_광역단체장_HD` | `당선.txt` | shared |
| `당선_광역단체장_L` | `당선.txt` | shared |
| `당선_광역의원` | `당선.txt` | shared | | `당선_광역의원` | `당선.txt` | shared |
| `당선_광역의원_HD` | `당선.txt` | shared | | `당선_광역의원_HD` | `당선.txt` | shared |
| `당선_광역의원_L` | `당선.txt` | shared |
| `당선_교육감` | `당선_교육감.txt` | exact | | `당선_교육감` | `당선_교육감.txt` | exact |
| `당선_교육감_HD` | `당선_교육감.txt` | shared | | `당선_교육감_HD` | `당선_교육감.txt` | shared |
| `당선_교육감_L` | `당선_교육감.txt` | shared |
| `당선_기초단체장` | `당선.txt` | shared | | `당선_기초단체장` | `당선.txt` | shared |
| `당선_기초단체장_HD` | `당선.txt` | shared | | `당선_기초단체장_HD` | `당선.txt` | shared |
| `당선_기초단체장_L` | `당선.txt` | shared |
| `당선_기초의원` | `당선.txt` | shared | | `당선_기초의원` | `당선.txt` | shared |
| `당선_기초의원_HD` | `당선.txt` | shared | | `당선_기초의원_HD` | `당선.txt` | shared |
| `당선_기초의원_L` | `당선.txt` | shared |
| `모든후보_광역단체장` | `모든후보.txt` | shared |
| `모든후보_광역단체장_5760` | `모든후보.txt` | shared |
| `모든후보_광역단체장_5760_END` | `모든후보.txt` | shared |
| `모든후보_광역단체장_END` | `모든후보.txt` | shared |
| `모든후보_광역단체장_L` | `모든후보.txt` | shared |
| `모든후보_광역단체장_L_END` | `모든후보.txt` | shared |
| `모든후보_교육감` | `모든후보_교육감.txt` | exact |
| `모든후보_교육감_5760` | `모든후보_교육감.txt` | shared |
| `모든후보_교육감_5760_END` | `모든후보_교육감.txt` | shared |
| `모든후보_교육감_END` | `모든후보_교육감.txt` | shared |
| `모든후보_교육감_L` | `모든후보_교육감.txt` | shared |
| `모든후보_교육감_L_END` | `모든후보_교육감.txt` | shared |
| `모든후보_기초단체장` | `모든후보.txt` | shared |
| `모든후보_기초단체장_5760` | `모든후보.txt` | shared |
| `모든후보_기초단체장_5760_END` | `모든후보.txt` | shared |
| `모든후보_기초단체장_END` | `모든후보.txt` | shared |
| `모든후보_기초단체장_L` | `모든후보.txt` | shared |
| `모든후보_기초단체장_L_END` | `모든후보.txt` | shared |
| `사전_역대당선자` | `사전_역대당선.txt` | shared |
| `사전_역대당선자_기초단체장` | `사전_역대당선.txt` | shared |
| `사전_역대당선자_교육감` | `사전_역대당선_교육감.txt` | exact family |
| `이시각1위_광역단체장` | `이시각1위_광역단체장.txt` | exact | | `이시각1위_광역단체장` | `이시각1위_광역단체장.txt` | exact |
| `이시각1위_광역단체장_HD` | `이시각1위_광역단체장.txt` | shared | | `이시각1위_광역단체장_HD` | `이시각1위_광역단체장.txt` | shared |
| `이시각1위_광역단체장_L` | `이시각1위_광역단체장.txt` | shared | | `이시각1위_기초단체장` | `이시각1위_기초단체장(5760동일).txt` | family |
| `이시각1위_기초단체장` | `이시각1위_기초단체장(5760동일).txt` | exact family |
| `이시각1위_기초단체장_HD` | `이시각1위_기초단체장(5760동일).txt` | shared | | `이시각1위_기초단체장_HD` | `이시각1위_기초단체장(5760동일).txt` | shared |
| `이시각1위_기초단체장_L` | `이시각1위_기초단체장(5760동일).txt` | shared |
| `접전_광역단체장` | `접전,초접전.txt` | shared | | `접전_광역단체장` | `접전,초접전.txt` | shared |
| `접전_기초단체장` | `접전,초접전.txt` | shared | | `접전_기초단체장` | `접전,초접전.txt` | shared |
| `초접전_광역단체장` | `접전,초접전.txt` | shared | | `초접전_광역단체장` | `접전,초접전.txt` | shared |
| `초접전_기초단체장` | `접전,초접전.txt` | shared | | `초접전_기초단체장` | `접전,초접전.txt` | shared |
### Normal - 모든후보 / 판세 / 역사 컷
| Canonical template | RGB spec file | Note |
| --- | --- | --- |
| `모든후보_광역단체장` | `모든후보.txt` | shared |
| `모든후보_광역단체장_5760` | `모든후보.txt` | shared |
| `모든후보_교육감` | `모든후보_교육감.txt` | exact |
| `모든후보_교육감_5760` | `모든후보_교육감.txt` | shared |
| `모든후보_기초단체장` | `모든후보.txt` | shared |
| `모든후보_기초단체장_5760` | `모든후보.txt` | shared |
| `사전_역대당선자` | `사전_역대당선.txt` | historical |
| `사전_역대당선자_교육감` | `사전_역대당선_교육감.txt` | historical |
| `사전_역대당선자_기초단체장` | `사전_역대당선.txt` | historical |
| `판세_광역단체장` | `판세_광역단체장.txt` | exact | | `판세_광역단체장` | `판세_광역단체장.txt` | exact |
| `판세_기초단체장` | `판세_광역단체장.txt` | inferred | | `판세_기초단체장` | `판세_광역단체장.txt` | inferred |
| `판세_기초단체장_5760` | `판세_광역단체장.txt` | inferred | | `판세_기초단체장_5760` | `판세_광역단체장.txt` | inferred |
| `판세_기초단체장_7680` | `판세_광역단체장.txt` | inferred |
### Bottom ### Bottom
| Template | RGB spec file | Note | | Canonical template | RGB spec file | Note |
| --- | --- | --- | | --- | --- | --- |
| `1-2위_광역단체장` | `1-2위, 1-3위, 이시각1위.txt` | shared | | `1-2위_광역단체장` | `1-2위, 1-3위, 이시각1위.txt` | shared |
| `1-2위_기초단체장` | `1-2위, 1-3위, 이시각1위.txt` | shared | | `1-2위_기초단체장` | `1-2위, 1-3위, 이시각1위.txt` | shared |
@@ -120,12 +104,12 @@
| `당선_기초단체장` | `당선.txt` | shared | | `당선_기초단체장` | `당선.txt` | shared |
| `당선_기초의원` | `당선.txt` | shared | | `당선_기초의원` | `당선.txt` | shared |
| `전후보_광역단체장` | `모든후보.txt` | naming bridge | | `전후보_광역단체장` | `모든후보.txt` | naming bridge |
| `전후보_기초단체장` | `모든후보.txt` | naming bridge |
| `전후보_교육감` | `모든후보_교육감.txt` | naming bridge | | `전후보_교육감` | `모든후보_교육감.txt` | naming bridge |
| `전후보_기초단체장` | `모든후보.txt` | naming bridge |
### Top ### Top
| Template | RGB spec file | Note | | Canonical template | RGB spec file | Note |
| --- | --- | --- | | --- | --- | --- |
| `광역단체장_2인` | `1-2위_사진.txt` | photo layout | | `광역단체장_2인` | `1-2위_사진.txt` | photo layout |
| `기초단체장_2인` | `1-2위_사진.txt` | photo layout | | `기초단체장_2인` | `1-2위_사진.txt` | photo layout |
@@ -138,12 +122,8 @@
- `광역의원표` - `광역의원표`
- `광역의원표_HD` - `광역의원표_HD`
- `광역의원표_L`
- `광역의원표_L_1`
- `기초의원표` - `기초의원표`
- `기초의원표_HD` - `기초의원표_HD`
- `기초의원표_L`
- `기초의원표_L_1`
- `역대시도판세_광역단체장` - `역대시도판세_광역단체장`
- `역대시도판세_기초단체장` - `역대시도판세_기초단체장`
@@ -165,12 +145,28 @@
- `투표율_선거구별` - `투표율_선거구별`
- `투표율_선거구별 사전` - `투표율_선거구별 사전`
- `투표율_시도별` - `투표율_시도별`
- `투표율_시도별_L`
- `투표율_영상` - `투표율_영상`
## Compatibility Note
- 예전 문서나 저장 상태에 남아 있는 아래 이름들은 현재 canonical 이름으로 내부 매핑한다.
- `_L` 계열
- `_END` 계열
- `판세_기초단체장_7680`
- RGB spec 운영 문서는 앞으로 canonical 이름만 기준으로 관리한다.
## 2026-04-19 Runtime Note
- style color binding이 있는 오브젝트는 후보 슬롯 clear 단계에서 들어간 빈 `SetValue("")`를 최종 values payload에서 제거해야 한다.
-`SetValue`가 남아 있으면 `Path Shape` face color가 적용되지 않은 것처럼 보일 수 있다.
- `1-2위_ani_광역단체장``정당바`는 RGB spec의 `정당바` 값을 그대로 사용해 `SetStyleColor(face, order=0, ...)`로 적용한다.
- `더불어민주당 = 0,30,84`
- `국민의힘 = 95,0,15`
- `1-2위_ani_광역단체장_loop``1-2위_광역단체장`도 동일하게 `정당바``Path Shape > Appearance > Face Color` 직접 변경 기준으로 운용한다.
- `정당판`은 probe로는 직접 style color 적용이 가능하지만, 현재 runtime에서는 운영 요구에 맞춰 asset alias 경로를 유지한다.
## Notes ## Notes
- `판세_기초단체장*`은 현재 `판세_광역단체장.txt`로 연결다. - `판세_기초단체장*`은 현재 `판세_광역단체장.txt`로 연결다.
전용 RGB txt가 추가되면 그쪽으로 분리하는 것이 맞다. - `보궐선거` 계열은 가장 가까운 광역/보궐 계열 spec로 연결한다.
- `보궐선거` 계열은 현재 가장 가까운 광역/보궐 계열 spec로 연결했다. - 역사 당선자 컷은 `ColorParty`와 정당 별칭 정규화를 함께 사용한다.
- `정당색`, `정당원`은 RGB txt 전용 색이 있으면 기존 png보다 우선해서 fallback 생성 자산을 사용한다.

View File

@@ -1,345 +1,230 @@
# 선거방송 송출 프로그램 요구사항 정의 (v0.1) # 선거방송 송출 프로그램 요구사항 정의 (v0.2)
---
## 1. 시스템 개요 ## 1. 시스템 개요
### 1.1 목적 ### 1.1 목적
- Tornado3를 통해 선거 방송 자막 송출
- 포맷(디자인)에 데이터 매핑 후 송출
--- - Tornado3 / Karisma를 통해 선거 방송 자막 컷을 송출한다.
- 디자인(포맷)과 데이터를 매핑한 뒤 준비/송출/아웃 흐름을 운영한다.
### 1.2 기본 구조
- 방송 채널: `노멀`, `좌상단`, `하단`, `비디오월`
- 데이터 모드: `사전`, `개표`
- 저장형 보조 데이터: `사전데이터(JSON)`
- 운영 페이지: `통합 스케줄`, 채널별 페이지, `사전데이터`, `데이터`, `컷리스트`, `설정`, `로그`
## 2. 데이터 처리 ## 2. 데이터 처리
### 2.1 데이터 수신 ### 2.1 데이터 수신
- Polling 기반 - Polling 기반
- 수동 수신 가능 - 수동 수신 가능
### 2.2 수동 수신 정책
- 수동 수신 시 polling 주기 초기화 - 수동 수신 시 polling 주기 초기화
- 3초 이내 재요청 금지
### 2.3 갱신 vs 송출 ### 2.2 지원 선거 범위
- 갱신 중 송출 요청 시 → 갱신 완료 후 송출
- 실연동 지원:
- `광역단체장`
- `교육감`
- `기초단체장`
- `광역단체장`은 개표/사전 투표율을 모두 SBS API로 받는다.
- `교육감`, `기초단체장`은 개표만 SBS API로 받고, 사전 방송용 역사 데이터는 저장형 사전데이터로 보완한다.
### 2.3 데이터 기준
### 2.4 데이터 기준
- 득표율: 소수점 1자리 반올림 - 득표율: 소수점 1자리 반올림
- 득표수: 3자리 콤마 - 득표수: 3자리 콤마
- 개표율 표기: `개표 x.x%`
### 2.4 데이터 유효성
### 2.5 데이터 유효성
- 필수 필드 누락 시 송출 금지 - 필수 필드 누락 시 송출 금지
- 사진 필수 포맷에서 이미지 없으면 송출 금지 - 사진 필수 포맷에서 이미지 없으면 송출 금지
- 단계에 맞지 않는 컷은 목록에서 숨기고 송출 직전 검증에서도 차단
--- ## 3. 포맷 / 컷 카탈로그
## 3. 포맷
### 3.1 정의 ### 3.1 정의
- 디자인 템플릿
- 데이터 매핑 구조
### 3.2 구조 - 포맷은 하드코딩된 템플릿 카탈로그로 관리한다.
- 포맷 → 컷 → (준비 → 송출) - 포맷은 1개 이상의 컷을 가진다.
- 실제 Karisma 씬 파일은 `.tscn` 기준이다.
### 3.3 루프 ### 3.2 현재 canonical 카탈로그
- 하위 범주 반복 (예: 시도별 17개)
### 3.4 데이터 반영 - `Elect2026_Bottom_민방`: 15컷
- 현재 컷 반영 금지 - `Elect2026_Normal_민방`: 63컷
- 다음 컷부터 반영 - `Elect2026_Top_민방`: 11컷
- 총 89컷
### 3.5 송출 시간 ### 3.3 실제 디자인명 기준
- 포맷별 설정 가능
- 변경 시 다음 컷부터 적용
--- - 현재 카탈로그는 실제 존재하는 `.tscn` 파일명 기준으로 맞춘다.
- 과거에 사용하던 `_L`, `_END`, `_7680` 계열 별칭은 현재 카탈로그에 노출하지 않는다.
- 저장된 큐/상태 복원을 위해 예전 컷 ID는 내부 별칭 맵으로 canonical 이름에 자동 연결한다.
### 3.4 루프 컷 규칙
- 같은 컷 이름에 `_loop.tscn` 파일이 있으면 반복 송출 컷으로 사용한다.
- 최초 송출 시에는 기본 `.tscn`을 우선 사용한다.
- 이미 송출 중인 상태에서 같은 컷을 다시 호출할 때 `_loop.tscn`이 있으면 우선 사용한다.
- `_loop.tscn`이 없으면 기본 `.tscn` 파일로 폴백한다.
### 3.5 단계별 사용 규칙
- `사전_역대당선자*` 계열은 `사전`, `개표` 양쪽 단계에서 사용 가능
- `사전_역대투표율*` 계열은 `사전` 단계 전용
- 일반 후보/판세/당선/접전 계열은 주로 `개표` 단계에서 사용
## 4. 스케줄 ## 4. 스케줄
### 4.1 구조 ### 4.1 구조
- 큐 기반
### 4.2 상태 - 채널별 독립 큐 기반
- 현재 송출: 빨간색 - `현재`, `다음`, `대기` 상태를 구분한다.
- 다음 송출: 주황색
### 4.2 지역 범위
- 큐 추가 시 컷만이 아니라 지역 범위도 함께 지정한다.
- 지원 범위:
- `전체`
- `선택권역`
- `개별 지역`
### 4.3 제어 ### 4.3 제어
- 다음 포맷 변경 가능
- 현재 포맷 강제 중지 후 전환 가능
- 순서 변경 가능
### 4.4 삭제 정책 - `다음` 지정
- 대기 포맷 삭제 가능 - `위/아래` 순서 변경
- 송출 중 포맷 삭제 불가 - `제거`
- `반복`
- 빈 스케줄 시 `즉시 아웃` 또는 `마지막 화면 유지`
### 4.5 루프 ### 4.4 복원
- 전체 루프 가능
- 첫 포맷부터 재시작
### 4.6 빈 스케줄 - 저장 상태 복원 시 채널별 큐와 지역 범위를 함께 복원한다.
- 설정에 따라: - 예전 컷 ID가 저장돼 있어도 복원 시 canonical 컷 ID로 정규화한다.
- 즉시 Out
- 마지막 화면 유지
### 4.7 종료 ## 5. 사전데이터
- 수동 종료 시:
- 스케줄 종료
- 해당 Layer Out
--- ### 5.1 저장 위치
## 5. 방송 영역 - `Tornado3_2026Election/Assets/Data/pre_election_history.json`
- 노멀 ### 5.2 지원 범위
- 좌상단
- 하단
- VideoWall
특징: - `광역단체장`: 전국 17개 시도
- 독립 스케줄 - `교육감`: 전국 17개 시도
- 동시 송출 가능 - `기초단체장`: 전국 262개 선거구
--- ### 5.3 기능
## 6. 방송사 설정 - 선거 종류/지역 선택
- 역대 투표율 카드 표시
- 역대 당선자 카드 표시
- 엑셀형 편집 그리드
- JSON 저장
### 대상 ### 5.4 색상 포맷 분리
- KNN, TBC, KBC, G1, TJB, JTV
### 특징 - `사전_역대당선자*` 편집 행에는 `ColorParty` 콤보박스를 제공한다.
- 동일 구조 - 표기 정당명과 송출 색상 정당을 분리할 수 있다.
- 지역 필터만 다름 - 송출 시에는 `ColorParty`가 있으면 이를 우선 사용하고, 없으면 표기 정당명을 사용한다.
### 지역 필터 ## 6. 썸네일 / 컷리스트
- 기본값 제공
- 사용자 수정 가능
--- ### 6.1 저장 규칙
## 7. 유력/확실/당선 - 썸네일은 `Assets/Thumbnail/<template.Id>.png` 규칙으로 저장한다.
### 기준 ### 6.2 프리뷰
- 후보 단위
### 수동 입력 - 채널 제어 패널의 컷 선택 콤보박스는 선택한 컷의 썸네일 프리뷰를 표시한다.
- 콤보박스 선택 - 프로젝트 썸네일이 없으면 번들 썸네일, 그것도 없으면 기본 아이콘을 사용한다.
- 자동 판정보다 우선
### 자동 판정 ### 6.3 생성
- 수동 지정 없는 경우만 적용
- 당선 조건:
- (1위 - 2위) > 남은 개표수
### 초기화 - `썸네일 생성` 기능은 Karisma에 씬을 로드하고 320x180 PNG를 저장한다.
- 전체 초기화 가능 - 씬은 `KarismaSceneResolver`가 실제 `.tscn`/`_loop.tscn` 규칙으로 해석한다.
### 저장 ### 6.4 컷리스트 페이지
- 방송사 + 선거종류 + 선거구 + 후보 기준
--- - 전체 컷 목록
- 채널 필터
- 컷별 재생 시간 편집
- 썸네일 현황 요약
## 8. Tornado3 연동 ## 7. 직함 표기
### 방식 - `광역단체장`, `교육감`, `기초단체장`은 전 컷 공통으로 지역명 대신 직함을 사용한다.
- TCP + DLL - 예:
- `서울특별시` -> `서울시장`
- `전라남도` -> `전라남도지사`
- `부산광역시` -> `부산광역시교육감`
- `창원시` -> `창원시장`
### 기능 ## 8. RGB / 정당 색상
- 이미지 변경
- 텍스트 변경
- 준비
- 송출
### 응답 처리 - RGB txt 기준으로 정당색을 해석한다.
- ACK 대기 없음 - RGB txt에 style color 지정이 있으면 `SetStyleColor(...)`를 우선 사용한다.
- 5초 내 응답 없으면 실패 - style color가 없을 때만 이미지 자산 경로를 사용한다.
- 역사 정당명은 `한나라당 -> 국민의힘`, `열린우리당 -> 더불어민주당` 등 별칭 정규화를 함께 지원한다.
### 상태 ## 9. Tornado3 / Karisma 연동
- IDLE
- READY (송출 가능 상태)
- SENDING
- ON_AIR
- ERROR
### 연결 ### 9.1 방식
- 끊김 시 재연결
- 재연결 후 사용자 확인 후 재개
--- - TCP + `Interop.KAsyncEngineLib.dll`
## 9. 상태 흐름 ### 9.2 기본 연결
IDLE → READY → SENDING → ON_AIR → NEXT - 기본 대상: `127.0.0.1:30001`
- 환경변수:
ERROR - `TORNADO_KARISMA_HOST`
- `TORNADO_KARISMA_PORT`
--- ### 9.3 채널 바인딩
## 10. 복원 - 기본:
- `노멀=0:0`
- `좌상단=0:1`
- `하단=0:2`
- `비디오월=1:0`
- 환경변수:
- `TORNADO_KARISMA_BIND_NORMAL`
- `TORNADO_KARISMA_BIND_TOPLEFT`
- `TORNADO_KARISMA_BIND_BOTTOM`
- `TORNADO_KARISMA_BIND_VIDEOWALL`
### 대상 ### 9.4 T3_Cut 탐색
- 스케줄
- 방송사
- 상태값
### 방식 - `TORNADO_T3CUT_PATH`
- 통합 대화상자 - `문서\\Tornado3 Data\\T3_Cut\\T3_Cut`
- 체크박스 선택 - `문서\\Tornado3 Data\\T3_Cut`
- `다운로드\\T3_Cut`
--- ### 9.5 폴백
## 11. UI 구조 - 시작 시 `T3_Cut` 경로가 유효하지 않으면 Mock Adapter로 폴백한다.
### 네비게이션 ## 10. 상태 저장 / 자동 저장
- 통합 스케줄
- 노멀
- 좌상단
- 하단
- VideoWall
- 데이터
- 설정
- 로그
### 표시 - 주요 설정 변경 시 자동 저장
- 빨강: 현재 송출 - 저장 대상:
- 주황: 다음 송출 - API 자동 갱신 여부
- API 갱신 주기
- 데이터 선택 상태
- 채널 큐
- 컷별 재생 시간
- 방송사 지역 필터
--- ## 11. 인코딩 규칙
## 12. 이미지
경로 규칙:
{선택경로}/{선거종류}/{지역코드}/{후보코드}.png
---
## 13. 예외 처리
- API 실패 → 사용자 알림
- Tornado 실패 → ERROR 상태
---
## 14. 핵심 개념
- 포맷 기반
- 컷 단위 송출
- 스케줄 큐 구조
- 상태 머신 기반 제어
---
## 15. 인코딩 검증 규칙
- 한글 문자열이 포함된 파일을 수정한 뒤에는 반드시 인코딩 깨짐 여부를 다시 확인한다.
- UI 문구, 로그 문구, 기본값 문자열은 저장 직후 한글이 정상 표시되는지 우선 점검한다.
- `?`, `U+FFFD(치환 문자)`, 비정상 한자 형태의 모지바케가 보이면 즉시 수정 대상으로 간주한다.
- 텍스트 파일은 UTF-8 기준으로 관리한다. - 텍스트 파일은 UTF-8 기준으로 관리한다.
--- - 한글 문자열 수정 후에는 `?`, `U+FFFD`, 모지바케가 없는지 반드시 확인한다.
## 16. Karisma / Tornado3 연동 기준 ## 12. 현재 확인된 빌드 경고
- CG 연동 라이브러리는 `Interop.KAsyncEngineLib.dll`을 사용한다. - `WindowsBase` 참조 충돌 경고
- `Interop.KAsyncEngineLib.dll``AMD64` 기준이므로 앱 실행 대상도 `x64`를 기준으로 운영한다. - MSIX 인증서 경고
- 기본 접속 대상은 `127.0.0.1:30001`이다. - `MockTornado3Adapter.ConnectionChanged` 미사용 경고
- `TORNADO_KARISMA_HOST`가 있으면 기본 호스트 대신 사용한다.
- `TORNADO_KARISMA_PORT`가 있으면 기본 포트 대신 사용한다.
- 앱은 시작 시 공유 Karisma 어댑터 1개를 만들고 `127.0.0.1:30001` 연결을 즉시 시도한다.
- 노멀, 좌상단, 하단, 비디오월 채널은 같은 TCP 연결을 공유하고, 채널별 `output/layer` 바인딩만 다르게 사용한다.
- 앱 시작 시 `T3_Cut 경로`가 유효하지 않으면 실CG 대신 Mock Adapter로 폴백한다.
- 현재 구현 기준으로는 시작 시 Mock으로 결정된 경우, 설정 변경만으로 실CG 어댑터로 승격되지 않으므로 앱 재시작이 필요할 수 있다.
- 채널 기본 바인딩은 `노멀=0:0`, `좌상단=0:1`, `하단=0:2`, `비디오월=1:0`이다.
- 환경변수 `TORNADO_KARISMA_BIND_NORMAL`, `TORNADO_KARISMA_BIND_TOPLEFT`, `TORNADO_KARISMA_BIND_BOTTOM`, `TORNADO_KARISMA_BIND_VIDEOWALL`로 채널 바인딩을 덮어쓸 수 있다.
## 17. T3_Cut 운영 규칙
- 사용자 설정 명칭은 `이미지 루트 경로`가 아니라 `T3_Cut 경로`로 표기한다.
- 송출에 사용하는 컷 파일 확장자는 `.tscn`이다.
- 컷 파일은 `T3_Cut` 루트 아래의 고정된 포맷 구조를 기준으로 사용한다.
- 기본 `T3_Cut` 탐색 순서는 `TORNADO_T3CUT_PATH` 환경변수, `문서\Tornado3 Data\T3_Cut\T3_Cut`, `문서\Tornado3 Data\T3_Cut`, `다운로드\T3_Cut` 순서다.
- 사용자가 상위 폴더를 선택했더라도 그 아래의 `T3_Cut` 하위 폴더에서 `.tscn` 파일이 확인되면 해당 하위 폴더를 실제 송출 루트로 정규화한다.
- 포맷 목록은 폴더 스캔으로 동적 생성하지 않고 하드코딩된 목록으로 관리한다.
- 같은 컷 이름에 `_loop.tscn` 파일이 있으면 반복 송출 컷으로 사용한다.
- 최초 송출 시에는 기본 컷 파일을 사용한다.
- 이미 송출 중인 상태에서 같은 컷을 다시 사용할 때는 `_loop.tscn`이 있으면 우선 사용한다.
- `_loop.tscn`이 없으면 기본 `.tscn` 파일로 폴백한다.
- 예시: `1-2위_광역단체장.tscn`은 최초 송출용, `1-2위_광역단체장_loop.tscn`은 반복 송출용으로 간주한다.
## 18. CG 연동 상태 UI 표기 기준
- 메인 화면 상단에는 `CG 연동 상태`를 표시한다.
- 사용자는 UI에서 현재 어댑터가 `실CG`인지 `Mock`인지 즉시 식별할 수 있어야 한다.
- 상단 상태 영역에는 실CG 연동 여부, 연결 대상, 채널 정상 상태 요약을 함께 표시한다.
- 채널 패널별로도 해당 채널이 어떤 백엔드를 사용하는지 표시한다.
- 실제 Karisma 사용 시 연결 대상 예시는 `127.0.0.1:30001` 형식으로 표시한다.
## 19. CG Return Value / Callback 로그 정책
- CG 시스템으로부터 오는 Return Value 관련 결과는 `로그` 탭에서 확인할 수 있어야 한다.
- 즉시 반환되는 값과 비동기 콜백 결과를 모두 로그로 남긴다.
- `Connect()` 호출 직후의 반환값은 즉시 로그로 기록한다.
- `LoadScene()``LoadSceneForce()` 호출 결과도 즉시 로그로 기록한다.
- `KAEventHandler` 기반 콜백 결과를 `LogService`를 통해 공용 로그에 남긴다.
- 로그에는 콜백 이름, 결과 enum 이름, 숫자 코드, 추가 정보(scene, object, output, layer 등)를 함께 남긴다.
- `OnConnect(int ErrorCode)``0`을 성공으로 간주하고, `0`이 아닌 값은 실패로 기록한다.
- `eKResult.RESULT_SUCCESS`는 정보 로그로 남기고, 그 외 결과는 경고 로그로 남긴다.
- 현재 로깅 대상에는 `OnConnect`, `OnClose`, `OnLogMessage`, `OnMessageNo`, `OnLoadScene`, `OnLoadSceneForce`, `OnBeginTransaction`, `OnEndTransaction`, `OnHeartBeat`, `OnSetValue`, `OnSetCounterNumberKey`, `OnScenePrepare`, `OnScenePrepareEx`, `OnPlay`, `OnPlayOut`, `OnPause`, `OnResume`, `OnStop`, `OnStopAll`, `OnCutIn`, `OnCutOut`, `OnTrigger`, `OnTriggerObject`, `OnQueryIsOnAir`, `OnQueryLayerCount`, `OnScenePlayingStarted`, `OnScenePlayed`, `OnSceneAnimationPlayed`, `OnScenePaused`를 포함한다.
- CG 콜백 로그와 앱 내부 로그는 같은 로그 시스템에 합쳐서 표시한다.
## 2026-04-09 운영 규칙 업데이트
### 스케줄 조작 규칙
- `다음`은 목록 순서를 바꾸지 않고 `다음 예약`만 이동한다.
- `다음`으로 지정된 항목은 대기 목록에서 노란색으로 표시한다.
- `다음 컷 즉시 송출`은 현재 송출 중인 컷을 끝내고, 노란색으로 예약된 다음 컷을 즉시 송출한다.
- `위``아래`는 대기 목록의 순서 변경 전용이다.
- `제거`는 대기 항목만 삭제하며, 송출 중이거나 준비 중인 항목은 제거하지 않는다.
### 대기 목록 상태 표시 규칙
- 빨강: 현재 송출 중인 항목
- 노랑: 다음 송출 예정 항목
- 회색: 그 외 항목
- 완료 상태 항목은 카드 전체를 흐리게 표시한다.
### 단계별 컷 사용 규칙
- `투표율` 계열 컷은 `사전` 단계에서 사용한다.
- 득표수, 순위, 판세 등 나머지 대부분의 컷은 `개표` 단계에서 사용한다.
- `사전_역대당선자*` 계열은 `사전``개표` 양쪽 단계에서 모두 사용할 수 있다.
- 단계에 맞지 않는 컷은 목록에서 숨기고, 송출 직전 검증에서도 차단한다.
### MediaWall 분류 규칙
- `포멧 정리.xlsx``사전`, `개표`, `판세` 시트에서 `MediaWall`로 표기된 컷은 `비디오월 모드 전용`으로 분류한다.
- 현재 비디오월 전용 컷은 다음과 같다.
- `민방_타이틀`
- `사전_역대투표율_loop`
- `투표율_시도별`
- `1-2위_광역단체장_5760`
- `1-3위_기초단체장_5760`
- `모든후보_광역단체장_5760`
- `이시각1위_광역단체장`
- `판세_기초단체장_5760`
- `광역의원표`
### T3_Cut 및 루프 컷 규칙
- `T3_Cut 경로` 아래의 `.tscn` 파일을 송출 컷으로 사용한다.
- 같은 이름의 `_loop.tscn` 파일이 있으면, 이미 송출 중인 상태에서 재호출할 때 loop 컷을 우선 사용한다.
### CG 연결 상태 표시 규칙
- CG 상태는 Karisma 어댑터 존재 여부가 아니라 공유 TCP `30001` 연결 성공 여부를 기준으로 표시한다.
- `Connected / Disconnected` 표시는 `OnConnect``OnClose` 콜백 기준으로 갱신한다.
- 공유 연결 상태는 해당 연결을 사용하는 모든 채널 패널에 동일하게 반영한다.
- TCP 연결이 끊기면 5초 간격으로 자동 재접속을 시도한다.
## 2026-04-14 TCP / SetValue 디버깅 업데이트
- 앱 실행 직후 `30001`과의 TCP 연결을 바로 시도하고, 이후 각 채널은 그 단일 연결을 공유한다.
- Karisma SDK 콜백 수신을 위해 전용 STA 스레드에서 메시지 펌프를 유지한다.
- `SetValue` 검증을 위해 후보 이름 키를 기존 `Candidate1Name`, `Candidate2Name` 외에 `후보명01`, `후보명02`로도 함께 전달한다.
- 현재 테스트 빌드 기준 `후보명01=김후보`, `후보명02=이후보`를 함께 송신해 실제 장면 변수 반영 여부를 확인한다.
- `.tscn`별 실제 변수 목록은 사전 스캔 리포트(`TSCN_VARIABLE_DISCOVERY_E_DRIVE.md`)를 기준으로 읽고, 현재 장면에 존재하는 변수에만 `SetValue`/`SetCounterNumberKey`를 보낸다.
- `ElectionDataSnapshot`에서 `후보명NN`, `정당명NN`, `득표수NN`, `득표율NN`, `순위NN`, `표차NN`, `득표차NN`, `선거구명NN`, `시도명NN`, `개표율NN`, `투표율01`, `전국투표율01`을 장면 변수로 자동 매핑한다.
- `ani`가 포함된 컷은 `득표율NN` 텍스트 값과 별도로 `IKACounter.SetCounterNumberKey(1, voteRate)`를 함께 전송한다.
- `유확당NN``유력.vrv`, `확정.vrv` 또는 `확실.vrv`, `당선.vrv`로 해석하고, `후보사진NN`, `정당바NN`, `정당판NN`, `정당심볼NN`, `그룹NN``T3_Cut` 아래 로컬 에셋 폴더에서 해석한다.
- 대표 검증 컷 `1-2위_ani_광역단체장`에 대해서는 실경로 기준 라이브 검증에서 `Success Count: 21`, `Failure Count: 0`을 확인했다(`LIVE_VALIDATE_1-2위_ani_광역단체장.md`).
### 인코딩 확인 원칙
- 터미널 출력이 깨져 보이는 것과 파일 자체 인코딩 손상을 구분해서 판단한다.
- 한글 문자열 상태 판단은 편집기 화면 또는 `UTF-8` 파일 직접 읽기 기준으로 확인한다.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 16 KiB

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1 @@

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Some files were not shown because too many files have changed in this diff Show More