11 KiB
11 KiB
ExperionCrawler 프로젝트 문제점 진단 보고서
분석일: 2026-05-10 | 도구: MiniMax-M2.5-Free | 프로젝트: ExperionCrawler
🔴 심각 (Critical) — 즉시 수정 필요
1. P&ID 컨트롤러 중복 충돌
- 위치:
src/Web/Controllers/ExperionControllers.cs(줄 981~1158) +src/Web/Controllers/PidController.cs(226줄) - 문제: 두 개의 컨트롤러가 동일한 라우트
/api/pid를 공유 - 영향: 의도치 않은 동작,,哪个 컨트롤러가 처리될지 불확실
- 해결:
ExperionPidController또는PidController중 하나 제거, Program.cs의ExcludedControllersFeatureProvider로직 재검토
2. SQL 인젝션 취약점 (EF1002)
- 위치:
src/Infrastructure/Database/ExperionDbContext.cs- 줄 1309, 1313, 1316, 1323, 1332, 1335, 1342, 1345, 1377, 1397, 1407
- 문제:
ExecuteSqlRawAsync+ 문자열 보간법 조합var createHypertableSql = $"SELECT create_hypertable('{request.TableName}'::regclass, ..."; - 현황:
#pragma warning disable EF1002로 경고 억제 → 위험성 은폐 - 평가:
IsValidSqlIdentifier()검증이 있긴 하지만, 컬럼명 일부 검증 안함 - 해결: 모든 동적 식별자를
NpgsqlParameter로 전환, 또는 Raw SQL 대신 EF Core LINQ 사용
3. AssetLoader에 DbContext 이중 관리
- 위치:
src/Infrastructure/Csv/AssetLoader.cs:14~17 - 문제:
- NpgsqlConnection을 직접 생성 (
configuration.GetConnectionString("DefaultConnection")) - ExperionDbService와 동시 접속 시 DB 연결 수 증가
- DbContext 기반 로깅/트랜잭션 무시
- NpgsqlConnection을 직접 생성 (
- 해결: AssetLoader를 DbContext 기반으로 리팩토링
🟠 높음 (High) —尽快修正
4. ExperionControllers.cs 단일 파일 1158줄
- 위치:
src/Web/Controllers/ExperionControllers.cs - 문제: 9개 이상의 컨트롤러가 하나의 파일에 집중 (ExperionCertificate, Connection, Crawl, Database, PointBuilder, TagMetadata, Realtime, History, OpcServer, NodeMap, Hypertable, Fast, ExperionPid)
- 영향: 유지보수困难, Merge 충돌 빈번, 코드 검색 어려움
- 해결: 각 컨트롤러를 별도 파일로 분리
5. ExperionDbContext.cs 단일 파일 1493줄
- 위치:
src/Infrastructure/Database/ExperionDbContext.cs - 문제: 서비스 + DbContext + 엔티티 config + DTO 레코드 + SQL 헬퍼가 모두 하나의 파일
- 해결: DbContext/Service 분리, DTO 레코드 → Core/Application/DTOs/ 이동
6. Console.WriteLine 디버그 코드 잔존
- 위치:
src/Infrastructure/Database/ExperionDbContext.cs약 15개 이상Console.WriteLine($"[DEBUG] 하이퍼테이블 생성 SQL: {createHypertableSql}"); Console.WriteLine($"[ERROR] {errorDetails}"); - 문제: 프로덕션에서 Console 출력이 로그로 출력됨, ILogger 미사용
- 해결: 모든 Console.WriteLine → _logger.LogInformation/Warning/Error로 교체
7. ExperionFastService MonitorLoop에서 매 반복마다 Scope 생성
- 위치:
src/Infrastructure/OpcUa/ExperionFastService.cs:204~220 - 문제:
foreach (var kvp in _sessions.ToList()) { using var scope = _scopeFactory.CreateScope(); // 매 세션마다 new scope var db = scope.ServiceProvider.GetRequiredService<IExperionDbService>(); } - 영향: 3개 세션 × 1초 간격 = 초당 3개 scope + 3개 connection
- 해결: 모니터 루프 수준에서 scoped service를 재사용, 또는 배치 처리
8. McpClient HttpClient Timeout 30분
- 위치:
src/Infrastructure/Mcp/McpClient.cs:26Timeout = TimeSpan.FromSeconds(1800) // 30분! - 문제: Python MCP 서버 응답 지연 시 최대 30분 대기
- 해결: Timeout合理化 (예: 60초), 요청 취소 시 HttpClient 정리 개선
9. ExperionRealtimeService 재진입 방지 로직 불완전
- 위치:
src/Infrastructure/OpcUa/ExperionRealtimeService.cs:198~217 - 문제:
_restarting = true; try { if (_running) { await StopAsync(); } } // StopAsync 안에서 _restarting=false finally { _restarting = false; } // race condition 가능 - 해결:
_restarting플래그 관리 개선, CancellationToken 기반 제어
10. history_table 하이퍼테이블 자동 생성 안 됨
- 위치:
src/Infrastructure/Database/ExperionDbContext.cs:328// history 테이블은 수동으로 하이퍼테이블 생성 필요 - 문제: InitializeAsync()에서 fast_record만 hypertable 생성, history_table은 일반 테이블로 존재
- 영향: TimescaleDB 시계열 최적화 미적용, 연속 집계/압축/보존 정책 미적용
- 해결: InitializeAsync()에 history_table hypertable 생성 로직 추가
🟡 중간 (Medium) — 개선 권장
11. ExperionServerConfig.ApplicationUri 미설정
- 위치:
src/Web/Controllers/ExperionControllers.cs:111~118(MapConfig) - 문제:
MapConfig()에서ApplicationUri매핑 안 함 → 인증서 생성 시에만 수동 조성 - 해결: MapConfig에 ApplicationUri 포함, 또는 ExperionServerConfig 생성자에서 자동 설정
12. Singleton 서비스의 mutable 상태 (Thread-unsafe)
- 위치:
ExperionRealtimeService,ExperionOpcServerService - 문제:
_running,_statusMsg,_subscribedCount,_currentCfg→ thread-unsafe_startedAt,_endpointUrl→ thread-unsafe
- 해결:
volatile키워드 또는lock사용, 또는 Stateless 서비스 설계
13. P&ID 서비스가 MCP에 강결합
- 위치:
PidExtractorService,PidGraphService—McpClient직접 의존 - 문제: MCP 서버 죽으면 P&ID 추출 전체 실패, Fallback mechanism 없음
- 해결: 추출 실패 시 로컬 LLM (vLLM 직접 호출) 또는 파일 기반 백업
14. TextToSqlService 자연어 파싱 불안정
- 위치:
src/Core/Application/Services/TextToSqlService.cs:226~349 - 문제: 350줄에 걸친 한국어 태그명 추출 로직, 테스트 케이스 없음, Edge case 시 null/빈 SQL 반환
- 해결: 단위 테스트 추가, 파싱 로직 분리 (TagExtractor 서비스)
15. HistoryIntervalQuery에서 NpgsqlCommand 직접 사용
- 위치:
ExperionDbContext.cs:810using var cmd = new NpgsqlCommand(sql, _ctx.Database.GetDbConnection() as NpgsqlConnection); - 문제: DbContext connection 직접 cast, EF Core 파이프라인 우회, transaction handling 없음
- 해결: FromSqlRaw 또는 Dapper 사용 고려
16. FastRecord PK 마이그레이션 로직 복잡
- 위치:
ExperionDbContext.cs:219~234— EF Core + Raw SQL 혼용 - 문제: EF Core 마이그레이션 정책과 충돌 가능, EF1002 경고
- 해결: EF Core 마이그레이션으로 통합, 또는 Raw SQL 제거
17. ExperionOpcServerService Dispose 불완전
- 위치:
src/Infrastructure/OpcUa/ExperionOpcServerService.cs:299~315 - 문제:
IHostedService+IDisposable동시 구현- DisposeAsync()와 IDisposable.Dispose() 중복 가능
- deprecated Stop() API 사용 (#pragma warning disable CS0618)
- 해결: IAsyncDisposable 구현, deprecated API 제거
18. McpServerHostedService 프로세스 관리 미흡
- 위치:
src/Infrastructure/Mcp/McpServerHostedService.cs - 문제:
- 프로세스 crash 후 자동 재시작 없음
- Ping 실패 시 warning만 로그, 복구 시도 안 함
- Health check 1초 × 30회 = 30초 후放棄
- 해결: Watchdog 패턴 적용, 재시작 로직 추가
🟢 낮음 (Low) — 리팩토링 시 고려
19. CORS AllowAnyOrigin
- 위치:
Program.cs:121 - 문제: 개발용으로는 OK, 프로덕션에서는 제한 필요
- 해결: AllowedOrigins 명시적 설정
20. appsettings.json에 Password 평문
- 위치:
appsettings.json:20 - 문제: 연결 문자열에 평문 비밀번호
- 해결: 환경변수 또는 .NET Secret Manager 사용
21. JsonSerializerOptions 불일치
- 문제:
- RealtimeService: 기본 JsonSerializer 옵션
- McpClient:
PropertyNameCaseInsensitive = true - McpService:
[JsonPropertyName]미적용
- 해결: 공통 JsonSerializerOptions 정의, 또는 PascalCase 통일
22. v_tag_summary VIEW 재생성 매번
- 위치:
ExperionDbContext.cs:304— 앱 시작마다 DROP+CREATE - 해결: Materialized View + REFRESH USING 또는 조건부 CREATE
23. ExperionHistoryService 재연결 로직 없음
- 문제: RealtimeService 재연결하지만 HistoryService는 없음
- 해결: 연결 lost 감지 후 재연결 로직 추가
24. TagMetadata base_tag 길이 제한 없음
- 문제: 매우 긴 태그명 삽입 시 DB 오버플로우 가능
- 해결: entity 정의에 .HasMaxLength() 추가
25. Logging 수준 불균형
- 문제: HistoryService는 Warning, 나머지는 Information
- 해결: appsettings.json 통일
📊 요약
| Severity | Count | 주요 카테고리 |
|---|---|---|
| 🔴 Critical | 3 | SQL 인젝션, 컨트롤러 중복, DbContext 이중 관리 |
| 🟠 High | 7 | 파일 크기, 디버그 코드 잔존, 리소스 관리, 유실된 설정 |
| 🟡 Medium | 8 | 결합도, threading, transaction, disposal |
| 🟢 Low | 6 | CORS, JSON, logging, naming |
🔥 즉시 수정 우선순위
- #2 (SQL 인젝션) — 보안 취약점, 즉시 패치
- #1 (컨트롤러 충돌) — API 동작 불확실성
- #6 (Console.WriteLine) — 프로덕션 로그 오염
- #10 (history_table hypertable) — 성능/스토리지 최적화 미적용
관련 파일 인덱스
src/Web/
├── Controllers/
│ ├── ExperionControllers.cs ← 1158줄, 다중 컨트롤러 (#4)
│ ├── PidController.cs ← 중복 컨트롤러 (#1)
│ ├── TextToSqlController.cs ← 375줄
│ └── PidGraphController.cs ← 191줄
├── Program.cs ← CORS (#19), DI 注册
└── appsettings.json ← 평문 비밀번호 (#20)
src/Infrastructure/
├── Database/
│ └── ExperionDbContext.cs ← 1493줄, SQL 인젝션 (#2), Console (#6)
├── OpcUa/
│ ├── ExperionRealtimeService.cs ← threading (#9, #12)
│ ├── ExperionOpcServerService.cs ← dispose (#17), threading (#12)
│ └── ExperionFastService.cs ← scope leak (#7)
├── Mcp/
│ ├── McpClient.cs ← timeout (#8)
│ └── McpServerHostedService.cs ← watchdog 없음 (#18)
└── Csv/
└── AssetLoader.cs ← Npgsql 직접 (#3)
src/Core/Application/Services/
├── TextToSqlService.cs ← 파싱 불안정 (#14)
├── PidExtractorService.cs ← MCP 강결합 (#13)
└── PidGraphService.cs ← MCP 강결합 (#13)