Files
ExperionCrawler/plans/roo-nodemap-undefined-fix.md
windpacer 77bdcf1f7f feat: ExperionCrawler IIoT OPC UA Data Bridge Infrastructure
Major project initialization and feature implementation:

**Core Features:**
- OPC UA client for Honeywell Experion HS R530 integration
- Real-time data streaming and history data retrieval
- Text-to-SQL query engine with TimeScaleDB
- JSON-based node configuration system
- SQLite database with migration support

**Architecture:**
- Clean architecture with Domain, Application, Infrastructure layers
- ASP.NET Core Web API frontend
- Web UI with real-time visualization
- PKI-based OPC UA authentication (TLS)

**Infrastructure Components:**
- ExperionOpcClient: OPC UA connection management
- ExperionRealtimeService: Real-time data streaming
- ExperionHistoryService: Historical data queries
- TextToSqlService: Natural language to SQL queries
- SqlValidator: SQL injection prevention

**Database:**
- TimescaleDB integration (recommended) or SQLite fallback
- Entity Framework Core with Extenstion methods
- OPCTag, KeyValue tables for data storage

**Security:**
- Certificate-based OPC UA endpoint security
- SSL/TLS encryption for database connections
- Output param binding injection prevention

**Testing:**
- Unit tests for TextToSqlService and SqlValidator
- Integration tests for Korean time range extraction

See REVIEW_REQUEST.md for detailed code review information.
2026-04-26 19:28:56 +09:00

111 lines
2.8 KiB
Markdown

# Roo 작업 지시: 노드맵 대시보드 undefined 필드 수정
## 배경 및 원인
`src/Web/Program.cs` 에 다음 설정이 있음:
```csharp
opt.JsonSerializerOptions.PropertyNamingPolicy = null; // PascalCase 직렬화
```
이로 인해 C# 익명 객체 shorthand `new { x.Id, x.NodeId }` 등은 PascalCase로 직렬화됨.
프론트엔드(`app.js`)는 camelCase(`r.id`, `r.nodeId`)로 접근 → **모든 값이 `undefined`로 표시됨**.
같은 문제를 Browse 엔드포인트에서도 확인했으며, 명시적 camelCase 익명 객체로 수정 완료:
```csharp
// 수정 전
return Ok(new { success = r.Success, nodes = r.Nodes, error = r.ErrorMessage });
// 수정 후
return Ok(new {
success = r.Success,
error = r.ErrorMessage,
nodes = r.Nodes.Select(n => new {
nodeId = n.NodeId,
displayName = n.DisplayName,
nodeClass = n.NodeClass,
hasChildren = n.HasChildren
})
});
```
---
## 수정 대상 파일
**`src/Web/Controllers/ExperionControllers.cs`**
클래스: `ExperionNodeMapController`
메서드: `Query()` (약 571번째 줄)
---
## 현재 코드 (문제)
```csharp
return Ok(new
{
total = r.Total,
items = r.Items.Select(x => new
{
x.Id, x.Level, x.Class, x.Name, x.NodeId, x.DataType
})
});
```
`x.Id` → JSON `"Id"` (PascalCase) → JS `r.id` = undefined
---
## 수정 후 코드 (목표)
```csharp
return Ok(new
{
total = r.Total,
items = r.Items.Select(x => new
{
id = x.Id,
level = x.Level,
@class = x.Class,
name = x.Name,
nodeId = x.NodeId,
dataType = x.DataType
})
});
```
`@class` 는 C# 예약어 회피용이며, JSON 직렬화 시 `"class"` 로 정상 출력됨.
---
## 추가 확인 사항 (같은 패턴이 있는지 전수 검사)
`ExperionControllers.cs` 전체에서 `PropertyNamingPolicy = null` 환경에서 PascalCase로 직렬화될 수 있는 패턴을 모두 찾아 수정:
1. `new { x.PropertyName }` 형태의 shorthand 익명 객체
2. 직접 typed record/class 인스턴스를 `Ok(...)` 에 넣는 경우
단, 다음은 이미 lowercase이므로 수정 불필요:
- `new { success = ..., nodes = ... }` — 명시적 소문자 키
- `new { total = ..., names = ... }` — 명시적 소문자 키
---
## 빌드 검증
수정 후 반드시:
```bash
dotnet build src/Web/ExperionCrawler.csproj --no-restore -v q
```
- `Build succeeded` 확인
- 에러 0건 확인
---
## 클로드 코드 검수 항목
수정 완료 후 아래 내용을 검수 요청:
1. `ExperionNodeMapController.Query()` 응답 필드가 모두 camelCase인지
2. 같은 패턴(`{ x.Prop }`)이 다른 컨트롤러에도 있는지 확인 여부
3. 빌드 성공 여부
4. `@class` → JSON `"class"` 직렬화 정상 작동 여부