# 작업플랜 — SteamAdvisor 컬럼명칭 통일 (2026-06-06)

## 목표

1. `appsettings.json`에서 `c6111`/`c61` 중복 제거
2. 컬럼키·파일prefix 모두 `C-6111`, `C-6211`, `C-8111`, `C-9111`, `C-9211`, `C-10111`, `C-10211`으로 통일
3. 컬럼키 = 파일prefix → 매핑 헬퍼 불필요

## 통일 매핑

| 컬럼키 (UI/API) | 파일 prefix | 설명 |
|-----------------|-------------|------|
| `C-6111` | `C-6111` | 6-1차 |
| `C-6211` | `C-6211` | 6-2차 |
| `C-8111` | `C-8111` | 8차 |
| `C-9111` | `C-9111` | 9-1차 |
| `C-9211` | `C-9211` | 9-2차 |
| `C-10111` | `C-10111` | 10-1차 |
| `C-10211` | `C-10211` | 10-2차 |

**TagsFor numeric suffix**: 컬럼키에서 `"C-"` prefix 제거 → `"6111"`, `"6211"`, `"8111"` 등

---

## 작업 0 — 파일 리네임 [선행 필수]

`scripts/analysis/` 내 데이터 파일 일괄 리네임:

```bash
cd scripts/analysis

# Model files
mv c6111_model.json C-6111_model.json
mv c61_model.json C-6111_model.json.bak  # 중복, 제거
mv c62_model.json C-6211_model.json
mv c81_model.json C-8111_model.json
mv c91_model.json C-9111_model.json
mv c92_model.json C-9211_model.json
mv c101_model.json C-10111_model.json
mv c102_model.json C-10211_model.json

# Tempref files
mv c61_tempref.json C-6111_tempref.json
mv c62_tempref.json C-6211_tempref.json
mv c81_tempref.json C-8111_tempref.json
mv c91_tempref.json C-9111_tempref.json
mv c92_tempref.json C-9211_tempref.json
mv c101_tempref.json C-10111_tempref.json
mv c102_tempref.json C-10211_tempref.json

# Plotdata files
mv c6111_plotdata.json C-6111_plotdata.json
mv c61_plotdata.json C-6111_plotdata.json.bak  # 중복, 제거
mv c62_plotdata.json C-6211_plotdata.json
mv c81_plotdata.json C-8111_plotdata.json
mv c91_plotdata.json C-9111_plotdata.json
mv c92_plotdata.json C-9211_plotdata.json
mv c101_plotdata.json C-10111_plotdata.json
mv c102_plotdata.json C-10211_plotdata.json
```

**동시 수정**: 각 JSON 파일 내부의 `"column"` / `"prefix"` 필드 값도 `C-6111` 등으로 변경.

```bash
# JSON 내부 필드 수정 (python one-liner)
for f in C-*_model.json; do
  python3 -c "
import json, sys
d = json.load(open('$f'))
d['column'] = '$f'.replace('_model.json','')
json.dump(d, open('$f','w'), indent=2)
"
done

for f in C-*_plotdata.json; do
  python3 -c "
import json, sys
d = json.load(open('$f'))
d['prefix'] = '$f'.replace('_plotdata.json','')
json.dump(d, open('$f','w'), indent=2)
"
done

for f in C-*_tempref.json; do
  python3 -c "
import json, sys
d = json.load(open('$f'))
d['column'] = '$f'.replace('_tempref.json','')
json.dump(d, open('$f','w'), indent=2)
"
done
```

---

## 작업 1 — appsettings.json 수정

```json
"SteamAdvisor": {
  "ModelPath": "/home/windpacer/projects/hc900_ax/scripts/analysis/C-6111_model.json",
  "PlotDataDir": "/home/windpacer/projects/hc900_ax/scripts/analysis",
  "ModelDir": "/home/windpacer/projects/hc900_ax/scripts/analysis",
  "DefaultColumn": "C-6111",
  "Columns": {
    "C-6111":  { "Feed": "FICQ-6101.PV", "Product": "FICQ-6118.PV", "TC": "TI-6111C",     "SteamOp": "TICA-6111A.OP", "SteamFlow": "FIQ-6115" },
    "C-6211":  { "Feed": "FICQ-6201.PV", "Product": "FICQ-6218.PV", "TC": "TI-6211C",     "SteamOp": "TICA-6211A.OP", "SteamFlow": "FIQ-6215" },
    "C-8111":  { "Feed": "FICQ-8101.PV",  "Product": "FICQ-8118.PV", "TC": "TI-8111C",     "SteamOp": "TICA-8111A.OP", "SteamFlow": "FIQ-8115" },
    "C-9111":  { "Feed": "FICQ-9101.PV",  "Product": "FICQ-9118.PV", "TC": "TI-9111C",     "SteamOp": "TICA-9111A.OP", "SteamFlow": "FIQ-9115" },
    "C-9211":  { "Feed": "FICQ-9201.PV",  "Product": "FICQ-9218.PV", "TC": "TI-9211C",     "SteamOp": "TICA-9211A.OP", "SteamFlow": "FIQ-9215" },
    "C-10111": { "Feed": "FICQ-10101.PV", "Product": "FICQ-10118.PV", "TC": "TI-10111C",   "SteamOp": "TICA-10111A.OP", "SteamFlow": "FIQ-10115" },
    "C-10211": { "Feed": "FICQ-10201.PV", "Product": "FICQ-10218.PV", "TC": "TI-10211C",   "SteamOp": "TICA-10211A.OP", "SteamFlow": "FIQ-10215" }
  }
}
```

---

## 작업 2 — SteamAdvisorController.cs 수정

### 2-1. 헬퍼 메서드 추가

```csharp
// 컬럼키 "C-6111" → 파일prefix "C-6111" (동일)
// 컬럼키 "C-6111" → TagsFor numeric suffix "6111"
private static string ToSuffix(string col) => col.StartsWith("C-") ? col[2..] : col;
```

### 2-2. `ListModels` — 파일명에서 컬럼키 추출 (변경 불필요)

`*_model.json` 파일명의 `_model.json` 제거 → `C-6111` 반환. 컬럼키 = 파일prefix이므로 추가 변환 불필요.

```csharp
// 변경 전
.Select(n => n!.Replace("_model", ""))  // "c6111"

// 변경 후 — 그대로 "C-6111" 반환
.Select(n => n!.Replace("_model", ""))
```

### 2-3. `Backtest` — 컬럼키 그대로 파일prefix 사용

```csharp
// 변경 전
var path = Path.Combine(plotDir, $"{col}_plotdata.json");

// 변경 후 — 컬럼키 = 파일prefix
var path = Path.Combine(plotDir, $"{col}_plotdata.json");  // 동일!
```

### 2-4. `TempProfile` — 컬럼키 = 파일prefix, TagsFor는 numeric suffix

```csharp
// 변경 전
var path = Path.Combine(dir, $"c{col}_tempref.json");  // col = "61"
var tagMap = TagsFor(col);

// 변경 후
var path = Path.Combine(dir, $"{col}_tempref.json");  // col = "C-6111"
var tagMap = TagsFor(ToSuffix(col));  // "6111"
```

### 2-5. `TagsFor` — numeric suffix 기반 태그명 생성

```csharp
// 변경 전 — col = "61", "62", "81" 등
private static Dictionary<string, string> TagsFor(string p)
{
    var m = new Dictionary<string, string>
    {
        ["reb_temp"] = $"TICA-{p}11A.PV",
        ["T_B"] = $"TI-{p}11B.PV",
        ["T_C"] = $"TI-{p}11C.PV",
        ["T_D"] = $"TI-{p}11D.PV",
        ["vacuum"] = $"PICA-{p}11.PV",
    };
    switch (p) {
        case "51": m["T_C"] = "TI-5111B.PV"; break;
        case "81": m["reb_temp"] = "TICA-8111.PV"; m["vacuum"] = "PICA-8111A.PV"; break;
        // ...
    }
    return m;
}

// 변경 후 — p = "6111", "6211", "8111" 등
private static Dictionary<string, string> TagsFor(string p)
{
    var m = new Dictionary<string, string>
    {
        ["reb_temp"] = $"TICA-{p}A.PV",
        ["T_B"] = $"TI-{p}B.PV",
        ["T_C"] = $"TI-{p}C.PV",
        ["T_D"] = $"TI-{p}D.PV",
        ["vacuum"] = $"PICA-{p}.PV",
    };
    switch (p) {
        case "8111": m["reb_temp"] = "TICA-8111.PV"; m["vacuum"] = "PICA-8111A.PV"; break;
        case "9111": m["vacuum"] = "PICA-9111A.PV"; break;
        case "9211": m["vacuum"] = "PICA-9211A.PV"; break;
        case "10111": m["vacuum"] = "PICA-10111A.PV"; break;
        case "10211": m["vacuum"] = "PICA-10211A.PV"; break;
    }
    return m;
}
```

### 2-6. `Live` — 컬럼키 fallback 변경

```csharp
// 변경 전
col ??= _config.GetValue<string>("SteamAdvisor:DefaultColumn") ?? "c6111";

// 변경 후
col ??= _config.GetValue<string>("SteamAdvisor:DefaultColumn") ?? "C-6111";
```

---

## 작업 3 — SteamAdvisor.cs 수정

```csharp
// 변경 전
_modelPath = config.GetValue<string>("SteamAdvisor:ModelPath")
             ?? "/home/windpacer/projects/hc900_ax/scripts/analysis/c6111_model.json";

// 변경 후
_modelPath = config.GetValue<string>("SteamAdvisor:ModelPath")
             ?? "/home/windpacer/projects/hc900_ax/scripts/analysis/C-6111_model.json";
```

---

## 작업 4 — UI (steam.js) 수정

### 4-1. `ST_TEMP_COLS` — 컬럼키 사용

```javascript
// 변경 전
const ST_TEMP_COLS = [['61','6-1차'],['62','6-2차'],['81','8차'],['91','9-1차'],['92','9-2차'],['101','10-1차'],['102','10-2차']];

// 변경 후
const ST_TEMP_COLS = [
  ['C-6111','6-1차'],['C-6211','6-2차'],['C-8111','8차'],
  ['C-9111','9-1차'],['C-9211','9-2차'],['C-10111','10-1차'],['C-10211','10-2차']
];
```

### 4-2. API 호출 — 컬럼키 그대로 전달

`stLiveTick`, `stTempTick` 모두 컬럼키를 API에 전달. 백엔드가 컬럼키를 직접 사용하므로 추가 변환 불필요.

### 4-3. `stLoadColumns` — 기본 선택 컬럼 강제 [★ 누락 A 해결]

알파벳순 정렬 시 `C-10111`이 `C-6111`보다 앞서 첫 옵션 자동 선택됨 → 데이터 없는 10차 → `missing_tags` → 불로딩 재현.

```javascript
// 변경 전
[sel1, sel2].forEach(sel => {
  sel.innerHTML = cols.map(c => `<option value="${c}">${c}</option>`).join('');
});

// 변경 후 — DefaultColumn(C-6111)을 기본 선택으로 강제
const defaultCol = 'C-6111';  // 또는 d.defaultColumn에서 동적 획득
[sel1, sel2].forEach(sel => {
  sel.innerHTML = cols.map(c => `<option value="${c}" ${c===defaultCol?'selected':''}>${c}</option>`).join('');
});
```

### 4-3. `stLoadColumns` — 기본 선택 컬럼 강제 [★ 누락 A 해결]

알파벳순 정렬 시 `C-10111`이 `C-6111`보다 앞서 첫 옵션 자동 선택됨 → 데이터 없는 10차 → `missing_tags` → 불로딩 재현.

```javascript
// 변경 전
[sel1, sel2].forEach(sel => {
  sel.innerHTML = cols.map(c => `<option value="${c}">${c}</option>`).join('');
});

// 변경 후 — DefaultColumn(C-6111)을 기본 선택으로 강제
const defaultCol = 'C-6111';  // 또는 d.defaultColumn에서 동적 획득
[sel1, sel2].forEach(sel => {
  sel.innerHTML = cols.map(c => `<option value="${c}" ${c===defaultCol?'selected':''}>${c}</option>`).join('');
});
```

---

## 작업 5 — Python 스크립트 출력 파일명 수정

Python 스크립트명 변경 불가 (`-` 포함 모듈 import 불가)이나, **출력 파일명**은 `C-6111` 형식으로 변경.

수정 대상 (각 스크립트의 `--prefix` 기본값 + 출력 파일명):

| 스크립트 | 변경 항목 |
|----------|----------|
| `c6111_extract.py` | `--data` 기본값: `c6111_data.pkl` → `C-6111_data.pkl` |
| `c6111_prodmap.py` | `--prefix` 기본값: `c6111` → `C-6111` |
| `c6111_shadow.py` | `--prefix` 기본값: `c6111` → `C-6111` |
| `c6111_rolling.py` | `--prefix` 기본값: `c6111` → `C-6111` |
| `c6111_startup.py` | `--prefix` 기본값: `c6111` → `C-6111` |
| `c6111_shutdown.py` | `--prefix` 기본값: `c6111` → `C-6111` |
| `c6111_operator_assist.py` | `--prefix` 기본값: `c6111` → `C-6111` |
| `c6111_export_model.py` | `--prefix` 기본값: `c6111` → `C-6111` |
| `gen_temp_profiles.py` | `--prefix` 기본값: `c61` → `C-6111` |
| `export_plotdata.py` | `--prefix` 기본값: `c6111` → `C-6111` |
| `run_column.py` | 스크립트 목록 + 데이터 파일명 |

**주의**: `c6111_data.pkl` → `C-6111_data.pkl`로 리네임 필요.

---

## 권장 순서

```
작업0 (파일 리네임) → 작업1 (appsettings) → 작업2 (Controller) → 작업3 (SteamAdvisor) → 작업4 (UI) → 작업5 (Python)
```

## 리스크

| 항목 | 수준 | 대응 |
|------|------|------|
| 파일 리네임 누락 | 중 | `ls scripts/analysis/`로 확인 |
| `TagsFor` switch case 누락 | 중 | 기존 6개 case → 5개 case로 변경 (51 제거) |
| Python 스크립트 `--prefix` 기본값 누락 | 낮 | 스크립트 11개 확인 |

## 검증

1. `ls scripts/analysis/` — 모든 데이터 파일이 `C-6111_` 등으로 시작
2. `dotnet build` 성공
3. `/api/steam/models` → `["C-6111", "C-6211", ...]` 반환 (중복 없음)
4. `/api/steam/live?col=C-6111` → 정상 응답
5. `/api/steam/tempprofile/C-6111` → 정상 응답
6. `/api/steam/backtest/C-6111` → 정상 응답
7. UI 컬럼 선택 → 차트 데이터 표시

---

## 재진단 보완 (2026-06-06) — 통일 작업플랜의 누락 2건

> 명칭 통일 방향은 타당하나, 이 작업만으로 **원래 증상(라이브 불로딩)이 해결되지 않으며** Python 영향 범위가 과소평가됨.

### 🔴 누락 A — 통일해도 라이브 불로딩이 재현 (기본선택 강제 누락)
- 이 문서 본진단의 증상 = "라이브 차트 불로딩", 실제 원인 = **UI 기본 선택 컬럼이 알파벳순 첫 = 10차**(재평가 §실제 원인).
- 통일 후에도 `OrderBy(x)` 알파벳순 첫은 **`C-10111`**(`1 < 6`) → `st-col` 첫 옵션 자동선택 = 데이터 없는 10차 → `missing_tags` → **불로딩 그대로 재현**.
- `DefaultColumn=C-6111`(작업1)은 **Live API의 `col` 누락 시 fallback**일 뿐, **UI `select` 기본선택과 별개**(`steam.js stLoadColumns`가 첫 옵션 선택).
- **추가 작업 필요(작업4에 포함)**: `stLoadColumns`에서 **기본 선택을 `DefaultColumn`(C-6111)으로 강제** + `missing_tags` 사용자 표시. ★명칭 통일이 불로딩을 고친다는 착시 주의 — 별개 수정.

### 🟠 누락 B — Python `c{prefix}` 접두 패턴 (작업5 과소평가)
- 분석 스크립트·`gen_temp_profiles.py`가 파일명을 `f"c{prefix}_data.pkl"`처럼 **코드에 접두 `c`를 박아** 생성. prefix=`C-6111`이면 `"cC-6111_data.pkl"`로 **깨짐**.
- 작업5는 "`--prefix` 기본값 + 출력 파일명"만 명시 — 실제론 **각 스크립트 내부 `c{prefix}` → `{prefix}` 문자열 패턴**을 모두 바꿔야 함.
- `gen_temp_profiles.build()`의 6-1 fallback(`if prefix=="61"`)·`c6111_data.pkl` 경로 등도 함께 수정 대상.
- **작업5 보강**: "출력 파일명 변경" = 스크립트 내부 `c{prefix}` 접두 제거 + prefix 비교 리터럴(`"61"`,`"81"` 등) 갱신.

### 🟡 경미 (인지됨)
- 모듈명(`c6111_extract.py`, `-` import 불가로 변경 불가) vs 출력 prefix(`C-6111`) **영구 불일치** — 동작엔 무해하나 혼란 잔존. 헤더 주석으로 명시 권장.

### 종합
| 항목 | 통일 작업플랜 | 보완 |
|---|---|---|
| 명칭 혼재(c6111/c61 중복·3규약) | ✅ 해결 | 타당 |
| **라이브 불로딩(원래 증상)** | ❌ **미해결** | 작업4에 **기본선택=C-6111 강제 + missing_tags 표시** 추가 필수 |
| Python 파일명 | ⚠️ 과소평가 | 작업5를 **내부 `c{prefix}`→`{prefix}` 패턴 변경**으로 확장 |
