# ExperionCrawler 프로젝트 분석 보고서 (Qwen 기반)

**작성일**: 2026-04-28  
**분석 도구**: Qwen3-Coder-Next  
**프로젝트 경로**: `/home/windpacer/projects/ExperionCrawler`

---

## 1. 개요

### 1.1 프로젝트 목표
Honeywell Experion OPC UA 서버를 위한 **웹 기반 데이터 수집 및 시계열 분석 도구**입니다.  
OPC UA 프로토콜을 통해 실시간 데이터 수집, CSV 저장, TimescaleDB 이력 관리, 자연어 질의 처리까지 통합적으로 제공합니다.

### 1.2 기술 스택

| 계층 | 기술 | 용도 |
|------|------|------|
| **Backend** | .NET 8 (C#) | 웹 API, 백그라운드 서비스 |
| **Frontend** | Vanilla JS + Bootstrap | UI 구현 |
| **Database** | PostgreSQL + TimescaleDB | 시계열 데이터 저장 |
| **OPC UA** | opcua-sharp (Opc.Ua) | 실시간/히스토리 데이터 수집 |
| **MCP** | Python + Qwen3-Coder-Next | 자연어 → SQL 변환 (LLM 기반) |

---

## 2. 아키텍처

### 2.1 소스 구조 (Clean Architecture)

```
ExperionCrawler/
├── src/
│   ├── Core/                    # 핵심 비즈니스 로직 (Domain, Application)
│   │   ├── Domain/
│   │   │   └── Entities/        # 엔티티 정의 (ExperionTag, ExperionRecord, RealtimePoint...)
│   │   └── Application/
│   │       ├── DTOs/            # 데이터 전송 객체
│   │       ├── Interfaces/      # 서비스 인터페이스 (DI 대상)
│   │       └── Services/        # 구현체 (TextToSqlService, KoreanTimeRangeExtractor...)
│   │
│   ├── Infrastructure/          # 기술적 구현 (OPC UA, DB, Mcp)
│   │   ├── Certificates/        # X.509 인증서 관리 (pki/ 디렉토리)
│   │   ├── Database/            # EF Core + TimescaleDB
│   │   ├── Csv/                 # CSV 읽기/쓰기 (CsvHelper)
│   │   ├── OpcUa/               # OPC UA 클라이언트/서버 구현
│   │   └── Mcp/                 # MCP 클라이언트 (Python 통신)
│   │
│   └── Web/                     # ASP.NET Core 웹 프로젝트
│       ├── Controllers/         # API 컨트롤러
│       ├── Program.cs           # DI, 미들웨어 구성
│       └── wwwroot/             # 정적 파일 (index.html, js/app.js, css/)
```

### 2.2 DI 컨테이너 등록 (`Program.cs`)

| Service | Lifetime | 구현체 |
|---------|----------|--------|
| `IExperionCertificateService` | Singleton | `ExperionCertificateService` |
| `IExperionStatusCodeService` | Singleton | `ExperionStatusCodeService` |
| `IOpcUaConfigProvider` | Singleton | `OpcUaConfigProvider` |
| `IExperionOpcClient` | Scoped | `ExperionOpcClient` |
| `IExperionCsvService` | Scoped | `ExperionCsvService` |
| `IExperionDbService` | Scoped | `ExperionDbService` |
| `ITextToSqlService` | Scoped | `TextToSqlService` |
| `IMcpService` | Singleton | `McpService` |
| `ExperionRealtimeService` | Singleton | 실시간 구독 (BackgroundService) |
| `ExperionHistoryService` | Singleton | 히스토리 구독 (BackgroundService) |
| `ExperionOpcServerService` | Singleton | OPC UA 서버 (BackgroundService) |

---

## 3. 핵심 기능

### 3.1 인증서 관리

| 기능 | 설명 |
|------|------|
| **생성** | `POST /api/certificate/create` → X.509 클라이언트 인증서 생성 |
| **상태 확인** | `GET /api/certificate/status?clientHostName=dbsvr` |
| **PKI 구조** | `pki/{own,trusted,issuers,rejected}/certs/` |

### 3.2 OPC UA 클라이언트

| 기능 | 설명 |
|------|------|
| **서버 접속** | `POST /api/connection/test` → 단일 태그 읽기, 노드 탐색 |
| **실시간 구독** | `ExperionRealtimeService` → Subscription 기반 콜백 |
| **히스토리 수집** | `ExperionHistoryService` → 주기적 Snapshot |
| **CSV 저장** | `ExperionCsvService` → `data/csv/` 디렉토리 |
| **DB 임포트** | `ExperionDbService` → `history_table` / `realtime_table` |

### 3.3 Text-to-SQL (자연어 → SQL)

| 기능 | 설명 |
|------|------|
| **자연어 파싱** | `POST /api/text-to-sql/parse` → `TextToSqlService.ParseNaturalLanguageAsync()` |
| **MCP 통합** | `POST /api/text-to-sql/query-nl` → LLM → SQL → 실행 |
| **도구 목록** | `GET /api/text-to-sql/tools` |
| **시계열 분석** | `POST /api/text-to-sql/analyze` → avg/max/min/추세 계산 |
| **간격 쿼리** | `POST /api/text-to-sql/query-history-interval` |

**시간 키워드 예시**:
- `"최근 1시간"`, `"최근 24시간"`, `"최근 7일"`, `"최근 1개월"`
- `"오늘"`, `"어제"`, `"오늘부터 ~ 까지"`, `"어제부터 ~ 까지"`
- `"오전 9시부터 오후 5시까지"`

### 3.4 MCP (Model Context Protocol)

| 기능 | 설명 |
|------|------|
| **Ping** | `GET /api/mcp/ping` → Python 서버 연결 확인 |
| **SQL 실행** | `POST /api/mcp/run-sql` → TimescaleDB 쿼리 |
| **PV 히스토리** | `POST /api/mcp/query-pv-history` → 태그명 + 시간 범위 |
| **태그 메타데이터** | `POST /api/mcp/get-tag-metadata` |
| **도면 목록** | `GET /api/mcp/list-drawings?unitNo=...` |
| **자연어 질의** | `POST /api/mcp/query-with-nl` |

### 3.5 OPC UA 서버

| 기능 | 설명 |
|------|------|
| **시작** | `POST /api/opcserver/start` → 자동 시작 플래그 저장 |
| **중지** | `POST /api/opcserver/stop` → 플래그 삭제 |
| **NodeManager** | `ExperionOpcServerNodeManager` → 커스텀 노드 매니저 |

---

## 4. 데이터베이스 스키마

### 4.1 테이블 정의

| 테이블명 | 용도 | 주요 컬럼 |
|----------|------|-----------|
| `raw_node_map` | 노드맵 원시 데이터 | `id, level, class, name, node_id, data_type` |
| `node_map_master` | 마스터 노드맵 | `id, level, class, name, node_id, data_type` |
| `realtime_table` | 실시간 포인트 | `id, tagname, node_id, livevalue, timestamp` |
| `history_table` | 시계열 이력 | `id, tagname, node_id, value, recorded_at` |

** TimescaleDB 확장 활성화: `CREATE EXTENSION IF NOT EXISTS timescaledb` **

---

## 5. API 엔드포인트

### 5.1 인증서

| 메서드 | 엔드포인트 | 기능 |
|--------|-----------|------|
| GET | `/api/certificate/status?clientHostName={name}` | 인증서 존재 여부 확인 |
| POST | `/api/certificate/create` | X.509 클라이언트 인증서 생성 |

### 5.2 연결

| 메서드 | 엔드포인트 | 기능 |
|--------|-----------|------|
| POST | `/api/connection/test` | 서버 접속 테스트, 단일 태그 읽기 |
| POST | `/api/connection/read` | nodeId 기반 읽기 |
| POST | `/api/connection/browse` | 노드 탐색 |

### 5.3 크롤링

| 메서드 | 엔드포인트 | 기능 |
|--------|-----------|------|
| POST | `/api/crawl/start` | 복수 노드 주기 수집 시작 |
| POST | `/api/crawl/stop` | 수집 중지 |
| POST | `/api/crawl/export` | CSV 다운로드 |

### 5.4 DB

| 메서드 | 엔드포인트 | 기능 |
|--------|-----------|------|
| GET | `/api/db/records?limit={n}&offset={m}` | 레코드 조회 |
| POST | `/api/db/import` | CSV 임포트 |
| POST | `/api/db/export` | CSV 다운로드 |

### 5.5 Text-to-SQL

| 메서드 | 엔드포인트 | 기능 |
|--------|-----------|------|
| POST | `/api/text-to-sql/parse` | 자연어 → SQL 변환 |
| POST | `/api/text-to-sql/execute` | SQL 실행 |
| POST | `/api/text-to-sql/suggest` | 쿼리 제안 |
| POST | `/api/text-to-sql/analyze` | 시계열 분석 |
| POST | `/api/text-to-sql/query-history-interval` | 사용자 지정 간격 조회 |
| POST | `/api/text-to-sql/query-nl` | MCP 통합 자연어 질의 |
| GET | `/api/text-to-sql/tools` | MCP 도구 목록 |

### 5.6 OPC UA 서버

| 메서드 | 엔드포인트 | 기능 |
|--------|-----------|------|
| POST | `/api/opcserver/start` | 서버 시작 |
| POST | `/api/opcserver/stop` | 서버 중지 |
| GET | `/api/opcserver/status` | 서버 상태 |

---

## 6. 주요 서비스 클래스

### 6.1 TextToSqlService

| 기능 | 메서드 |
|------|--------|
| 자연어 파싱 | `ParseNaturalLanguageAsync(string input)` |
| SQL 생성 | `BuildSqlFromNaturalLanguage(string input, out List<string> tagNames)` |
| 태그 매핑 | `GetMappingNodesAsync(List<string> tagNames)` |
| 시계열 분석 | `AnalyzeAsync(string sql)` |
| 시간 범위 추출 | `KoreanTimeRangeExtractor` 협업 |

### 6.2 ExperionOpcClient

| 기능 | 메서드 |
|------|--------|
| 단일 읽기 | `ReadAsync(string nodeId)` |
| 복수 읽기 | `ReadAsync(List<string> nodeIds)` |
| 노드 탐색 | `BrowseAsync(string nodeId)` |
| 연결 테스트 | `TestConnectionAsync(ExperionServerConfig cfg)` |

### 6.3 ExperionRealtimeService

| 기능 | 메서드 |
|------|--------|
| 시작 | `StartAsync(ExperionServerConfig cfg)` |
| 중지 | `StopAsync()` |
| 등록 | `SubscribeAsync(List<string> nodeIds)` |
| 해제 | `UnsubscribeAsync(List<string> nodeIds)` |

---

## 7. 설정 파일

### 7.1 appsettings.json

```json
{
  "ConnectionStrings": {
    "DefaultConnection": "Host=localhost;Port=5432;Database=iiot_platform;Username=postgres;Password=postgres",
    "ExperionDbConnection": "Host=localhost;Port=5432;Database=postgres;Username=postgres;Password=postgres;Trust Server Certificate=true"
  },
  "OpcUaServer": {
    "Port": 4841,
    "EnableSecurity": false,
    "AllowAnonymous": true,
    "AllowedUsernames": ["mngr"],
    "AllowedPasswords": ["mngr"]
  }
}
```

### 7.2 자동 시작 플래그

| 파일 | 용도 |
|------|------|
| `realtime_autostart.json` | 실시간 구독 자동 시작 |
| `opcserver_autostart.json` | OPC UA 서버 자동 시작 |

---

## 8. 개발/배포

### 8.1 로컬 실행

```bash
cd src/Web
dotnet run
# → http://localhost:5000
```

### 8.2 Ubuntu 배포

```bash
git clone <repo> ExperionCrawler
cd ExperionCrawler
sudo bash deploy.sh
```

### 8.3 systemctl 관리

```bash
sudo systemctl status experioncrawler
sudo systemctl restart experioncrawler
sudo systemctl stop experioncrawler
sudo journalctl -u experioncrawler -f  # 실시간 로그
```

---

## 9. 테스팅

### 9.1 단위 테스트 프로젝트

| 테스트 클래스 | 주요 테스트 항목 |
|---------------|----------------|
| `TextToSqlServiceTests.cs` | SQL 생성, 태그 매핑, 시간 범위 추출 |
| `SqlValidatorTests.cs` | SQL 인젝션 방지, 테이블 제한 |
| `KoreanTimeRangeExtractorTests.cs` | 한국어 시간 표현 파싱 |

### 9.2 테스트 명령어

```bash
dotnet test ExperionCrawler.Tests/
```

---

## 10. 주의사항

### 10.1 JSON 직렬화 정책

`Program.cs`에서 **PascalCase 유지** 설정:

```csharp
opt.JsonSerializerOptions.PropertyNamingPolicy = null;  // camelCase로 변환하지 않음
```

**프론트엔드 대응**: app.js의 모든 API 응답은 소문자 키로 접근 (`res.id`, `res.tagName`).

### 10.2 인증서 경로

```bash
pki/own/certs/{clientHostName}.pfx         # 클라이언트 인증서
pki/trusted/certs/                         # 신뢰 피어
pki/issuers/certs/                         # 신뢰 발급자 (필수)
pki/rejected/certs/                        # 거부 인증서
```

### 10.3 TimescaleDB

- `history_table`은 TimescaleDB의 **Hypercube**로 자동 관리됨
- `recorded_at` 컬럼은 `TIMESTAMPTZ` 타입

---

## 11. 다음 개선 방향

| 항목 | 설명 |
|------|------|
| **realtime_table indexing** | `node_id` 유니크 인덱스만 있고 `tagname` 인덱스 추가 필요 |
| **CSV import 성능** | AssetLoader의 binary COPY 대신 EF Core Bulk Insert 고려 |
| **MCP error handling** | Python 서버 장애 시 fallback 처리 강화 |
| **UI/UX** | Bootstrap 5 업그레이드, 모바일 반응형 개선 |
| **OPC UA Security** | 현재 `AutoAcceptUntrustedCertificates=true` → 프로덕션 시 변경 필요 |

---

## 12. 관련 문서

| 문서 | 경로 | 설명 |
|------|------|------|
| CLAUDE.md | `CLAUDE.md` | Claude 작업 규칙 |
| .roo.md | `.roo.md` | Roo 작업 규칙 |
| task_state.md | `task_state.md` | Text-to-SQL 개발 로그 |
| issues.md | `issues.md` | 이슈 추적 |
| REVIEW_REQUEST.md | `REVIEW_REQUEST.md` | 코드 리뷰 요청 |

---

**분석 완료일**: 2026-04-28 23:11 (KST)  
**분석 도구**: Qwen3-Coder-Next  
**프로젝트 상태**: ✅ 활발한 개발 중 (Text-to-SQL + MCP 통합 완료)
