# 진단 — Steam Advisory 라이브 차트 데이터 불로딩 (2026-06-06)

## 증상

Steam Advisory 탭 → "라이브" 패널에서 "조회" 버튼 클릭 후 차트와 입력값이 표시되지 않음.

## 진단 결과

### 🔴 HIGH — `ToDictionaryAsync` 중복 tagname `ArgumentException`

**문제**: `SteamAdvisorController.Live`와 `TempProfile` API에서 `_ctx.RealtimePoints.Where(...).ToDictionaryAsync(r => r.TagName, ...)` 사용. `realtime_table`의 UNIQUE 제약이 `(controller_id, tagname)`이므로 같은 `tagname`이 여러 `controller_id`로 존재할 수 있음. `ToDictionaryAsync`는 중복 키에서 `ArgumentException`을 던짐.

**근거**:
- `SteamAdvisorController.cs:133-135` — `ToDictionaryAsync(r => r.TagName, r => r.LiveValue)`
- `SteamAdvisorController.cs:194-196` — 동일 패턴
- `Hc900Controllers.cs:279-281` — 동일 패턴
- `Hc900DbContext.cs:488-498` — UNIQUE 제약이 `(controller_id, tagname)`

**영향**: API 호출 시 `ArgumentException` 발생 → 500 Internal Server Error → JS `catch (_) {}`로 삼켜짐 → 사용자에게 아무 표시도 없음.

**수정**: `GroupBy(r => r.TagName).ToDictionaryAsync(g => g.Key, g => g.OrderByDescending(r => r.Timestamp).FirstOrDefault()?.LiveValue)`로 변경. 최신 타임스탬프의 값을 사용.

### 🟠 MED — JS `catch (_) {}`로 예외 삼킴

**문제**: `stLiveTick`의 `catch (_) {}`가 API 에러를 완전히 무시. `missing_tags` 응답도 `stUpdateLive`에 전달되어 `d.recOp`가 `undefined`인 상태로 UI 갱신 시도.

**근거**: `steam.js:163-169` — `catch (_) {}`

**영향**: API 에러가 발생해도 사용자에게 표시되지 않음. 디버깅 불가.

**수정**: `catch (e)`로 변경 후 `st-live-msg`에 에러 메시지 표시. `missing_tags` 응답은 별도 처리.

### 🟡 LOW — register-map에 없는 태그 존재

**문제**: `appsettings.json`의 SteamAdvisor 태그 중 9차, 9-2차, 10-1차, 10-2차 관련 태그가 `register-map.json`에 없음.

**근거**: `appsettings.json:76-80` — 9차/10차 태그 정의. `register-map.json`에 해당 태그 없음.

**영향**: 해당 컬럼 선택 시 `missing_tags` 응답. 6차(C3)는 정상 동작.

**수정**: register-map에 태그 추가 또는 appsettings에서 제거.

## 교차 검증

| 질문 | 확인 방법 | 결과 |
|------|-----------|------|
| Q1. 이미 수정된 문제인가? | 파일 현재 상태 확인 | ❌ 수정되지 않음 |
| Q2. 다른 레이어에서 처리되고 있는가? | 호출 계층 확인 | ❌ JS `catch (_) {}`로 삼킴 |
| Q3. 의도적 설계인가? | 문서·주석 확인 | ❌ 의도적 아님 |
| Q4. 실제 장애 시나리오가 있는가? | 재현 경로 구체화 | ✅ 같은 tagname이 여러 controller_id로 존재하면 500 Error |

## 수정 내역

| 파일 | 변경 내용 |
|------|-----------|
| `SteamAdvisorController.cs:133-136` | `ToDictionaryAsync` → `GroupBy` + `FirstOrDefault` |
| `SteamAdvisorController.cs:194-197` | 동일 패턴 수정 |
| `Hc900Controllers.cs:279-282` | 동일 패턴 수정 |
| `steam.js:163-176` | `catch (_) {}` → 에러 표시 + `missing_tags` 처리 |

---

## 재평가 (2026-06-06, 코드·DB 데이터 검증 후)

진단을 데이터로 검증한 결과, **#1의 "현재 장애 원인" 진술은 부정확하나, 위험의 본질은 설계상 유효**함. 정정·심화:

### #1 재평가 — "현재 ArgumentException 발생"은 거짓, 그러나 위험은 실재
- **현 데이터**: `realtime_table`에 `tagname` **단독 UNIQUE 인덱스**(`realtime_table_tagname_key`)가 존재 → tagname 전역 유일 → 현재 중복 0행 → `ToDictionaryAsync` **지금은 안 터짐**. 즉 **현재 불로딩의 원인이 아님**.
- **그러나(핵심)**: HC900은 컨트롤러 간 **peer 통신으로 동일 태그를 미러링**함. 설계상 같은 tagname이 **여러 `controller_id`로 존재 가능**하며, 올바른 제약은 `UNIQUE(controller_id, tagname)`(메모리·`idx_realtime_table_ctrl_tag_unique`와 일치).
- **진짜 버그**: 현재 공존하는 **`tagname` 단독 UNIQUE 제약이 설계 위반**. C4 online 시 peer 미러 태그의 두 번째 INSERT/upsert를 막아 **적재 자체가 깨짐**.
- **결론**: `ToDictionaryAsync → GroupBy` 수정은 **단독 UNIQUE 제거를 전제로 방어적으로 정당**. 단 "현재 500 에러 발생"은 아님(잠재 위험).

### 추가로 필요한 근본 수정 (진단보다 한 겹 깊음)
1. **`realtime_table_tagname_key`(tagname 단독 UNIQUE) 제거** — 멀티컨트롤러 peer 적재 정합. `(controller_id, tagname)`만 유지.
2. **`Live`/`TempProfile` 조회를 `controller_id`로 한정** — 컬럼→컨트롤러 매핑(6차=C3 등)으로 필터. tagname만으로 조회하면 peer 미러된 동명 태그의 **엉뚱한 컨트롤러 값**이 섞일 수 있음(GroupBy로 예외는 막아도 *어느 컨트롤러 값인지* 모호).

### 실제 현재 불로딩 원인
- 6차(`C-6111`) 필수 태그(`FICQ-6101.PV`·`FICQ-6118.PV`·`TI-6111C`)는 realtime에 **모두 존재**(C3) → 6차 선택 시 정상 동작해야 함.
- 표면 트리거 = **기본 선택 컬럼이 `configured` 알파벳순 첫(`C-10111`=10차)** → 조회 시 C4 태그 부재 → `missing_tags` → `catch(_){}`(#2)로 삼켜져 무표시.
- **해법**: `DefaultColumn=C-6111`로 변경 + **UI `stLoadColumns`에서 기본 선택을 `C-6111`으로 강제**(작업플랜 4-3) + `missing_tags` 사용자 표시(#2 수정으로 반영됨). #3(9·10차 태그 부재)은 사실.

### 종합
| 항목 | 진단 주장 | 재평가 |
|---|---|---|
| #1 ToDictionaryAsync | HIGH·현재 500 | **현재는 미발생**(tagname 단독 UNIQUE)이나 **peer 설계상 잠재 실재** → GroupBy 정당 + **단독 UNIQUE 제거·controller_id 한정 조회**가 근본 |
| #2 catch 삼킴 | MED | 타당(표면 원인을 가림) |
| #3 9·10차 태그 부재 | LOW | 타당 + **기본 컬럼이 10차라 첫 화면 불로딩 트리거** |

---

## 추가 진단 — 컬럼명칭 혼재 문제 (2026-06-06)

### 🟠 MED — `c6111`/`c61` 중복 + 3가지 네이밍 체계 혼재

**문제**: `appsettings.json`에서 `c6111`과 `c61`이 같은 태그 매핑을 가리킴. `ListModels` API는 둘 다 반환 → UI에서 중복 선택 가능. 또한 컬럼키(`c6111`), 파일prefix(`c6111`/`c61`), TempProfile route param(`61`)이 각각 다른 규칙 사용.

**근거**: `appsettings.json:73-74` — `c6111`과 `c61` 중복. `SteamAdvisorController.cs:184` — `c{col}_tempref.json` (col = numeric suffix). `steam.js:74` — `ST_TEMP_COLS`가 numeric suffix 사용.

**영향**: UI 컬럼 선택에서 중복, TempProfile API 호출 시 컬럼키 ↔ numeric suffix 변환 누락 가능성.

**해결**: `작업플랜-스팀컬럼명칭통일.md` 참조. 컬럼키·파일prefix 모두 `C-6111` 형식으로 통일.

### 🟡 LOW — TagsFor numeric suffix 불일치

**문제**: `TagsFor`가 `"61"`, `"62"` 등 2자리 numeric suffix를 기대하지만, 컬럼명칭 통일 후 `"6111"`, `"6211"` 등 4자리로 변경됨. 템플릿 `$"TICA-{p}11A.PV"` → `$"TICA-{p}A.PV"`로 변경 필요.

**근거**: `SteamAdvisorController.cs:249-268` — `TagsFor` 메서드.

**영향**: 컬럼명칭 통일 후 수정하지 않으면 태그명 생성 실패 → `missing_tags` 응답.

**해결**: `작업플랜-스팀컬럼명칭통일.md` 작업 2-5 참조.

---

## 통일 작업플랜 보완 진단 (2026-06-06)

> `작업플랜-스팀컬럼명칭통일.md`에 명칭 통일 방향은 타당하나, **원래 증상(라이브 불로딩) 해결과 Python 영향 범위가 과소평가**됨.

### 🔴 누락 A — 통일해도 라이브 불로딩 재현 (UI 기본선택 강제 누락)

- 통일 후에도 `OrderBy(x)` 알파벳순 첫은 **`C-10111`**(`1 < 6`) → `st-col` 첫 옵션 자동선택 = 데이터 없는 10차 → `missing_tags` → **불로딩 그대로 재현**.
- `DefaultColumn=C-6111`(작업1)은 **Live API의 `col` 누락 시 fallback**일 뿐, **UI `select` 기본선택과 별개**(`steam.js stLoadColumns`가 첫 옵션 선택).
- **해결**: `stLoadColumns`에서 기본 선택을 `C-6111`으로 강제 (작업플랜 4-3).

### 🟠 누락 B — Python `c{prefix}` 접두 패턴 (작업5 과소평가)

- `run_column.py:51` — `f"c{prefix}_data.pkl"`, `gen_temp_profiles.py:38` — `f"c{prefix}_data.pkl"`, `gen_temp_profiles.py:71` — `f"c{prefix}"`, `gen_temp_profiles.py:75` — `f"c{prefix}_tempref.json"`.
- prefix=`C-6111`이면 `"cC-6111_data.pkl"`로 **깨짐**.
- **해결**: 스크립트 내부 `c{prefix}` → `{prefix}` 패턴 변경 + `gen_temp_profiles.py:71`의 `"column": f"c{prefix}"` → `"column": prefix`.

---

## 추가 진단 — 컬럼명칭 혼재 문제 (2026-06-06)

### 🟠 MED — `c6111`/`c61` 중복 + 3가지 네이밍 체계 혼재

**문제**: `appsettings.json`에서 `c6111`과 `c61`이 같은 태그 매핑을 가리킴. `ListModels` API는 둘 다 반환 → UI에서 중복 선택 가능. 또한 컬럼키(`c6111`), 파일prefix(`c6111`/`c61`), TempProfile route param(`61`)이 각각 다른 규칙 사용.

**근거**: `appsettings.json:73-74` — `c6111`과 `c61` 중복. `SteamAdvisorController.cs:184` — `c{col}_tempref.json` (col = numeric suffix). `steam.js:74` — `ST_TEMP_COLS`가 numeric suffix 사용.

**영향**: UI 컬럼 선택에서 중복, TempProfile API 호출 시 컬럼키 ↔ numeric suffix 변환 누락 가능성.

**해결**: `작업플랜-스팀컬럼명칭통일.md` 참조. 컬럼키·파일prefix 모두 `C-6111` 형식으로 통일.

### 🟡 LOW — TagsFor numeric suffix 불일치

**문제**: `TagsFor`가 `"61"`, `"62"` 등 2자리 numeric suffix를 기대하지만, 컬럼명칭 통일 후 `"6111"`, `"6211"` 등 4자리로 변경됨. 템플릿 `$"TICA-{p}11A.PV"` → `$"TICA-{p}A.PV"`로 변경 필요.

**근거**: `SteamAdvisorController.cs:249-268` — `TagsFor` 메서드.

**영향**: 컬럼명칭 통일 후 수정하지 않으면 태그명 생성 실패 → `missing_tags` 응답.

**해결**: `작업플랜-스팀컬럼명칭통일.md` 작업 2-5 참조.
