Files
ExperionCrawler/프로젝트진단-MiniMax-M2.5-Free.md

11 KiB
Raw Blame History

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 기반 로깅/트랜잭션 무시
  • 해결: 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:26
    Timeout = 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, PidGraphServiceMcpClient 직접 의존
  • 문제: 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:810
    using 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

🔥 즉시 수정 우선순위

  1. #2 (SQL 인젝션) — 보안 취약점, 즉시 패치
  2. #1 (컨트롤러 충돌) — API 동작 불확실성
  3. #6 (Console.WriteLine) — 프로덕션 로그 오염
  4. #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)