228 lines
10 KiB
Markdown
228 lines
10 KiB
Markdown
# Text-to-SQL 탭 전체 기능 테스트 및 수정
|
|
|
|
## PHASE 1 - 구조 파악 (완료)
|
|
|
|
### 완료일: 2026-04-24
|
|
|
|
### Text-to-SQL 탭 UI 요소 목록
|
|
|
|
| UI 요소 (HTML ID) | 설명 | 기능 |
|
|
|-------------------|------|------|
|
|
| t2s-query | 자연어 쿼리 입력 | 사용자가 자연어로 질의 입력 |
|
|
| t2sParse() | SQL 변환 버튼 | 자연어를 SQL로 변환 |
|
|
| t2sExecute() | 실행 버튼 | 변환된 SQL 실행 |
|
|
| t2sAnalyze() | 분석 버튼 | 시계열 데이터 분석 (평균, 최대, 최소, 추세) |
|
|
| t2s-sql | 생성된 SQL 표시 | 변환된 SQL 쿼리 표시 |
|
|
| t2s-tags | 태그명 입력 | 조회할 태그명 (쉼표 구분, 비우면 전체) |
|
|
| t2s-interval | 집계 간격 선택 | 1분, 5분, 15분, 1시간, 1일 |
|
|
| t2s-limit | 데이터 제한 | 조회 결과 최대 행 수 |
|
|
| t2s-date-from | 시작일 | 조회 시작일 (비우면 최근 24시간) |
|
|
| t2s-date-to | 종료일 | 조회 종료일 (비우면 현재) |
|
|
| t2s-limit-analyze | 분석 데이터 제한 | 분석에 사용할 데이터 제한 |
|
|
| t2s-results | 조회 결과 표시 | 쿼리 실행 결과 테이블 |
|
|
| t2s-analysis-results | 태그 분석 결과 표시 | 분석 결과 통계 표시 |
|
|
| t2s-chip | 추천 쿼리 칩 | "최근 1시간 평균", "24시간 최대값" 등 |
|
|
|
|
### API 엔드포인트 매핑
|
|
|
|
| 엔드포인트 | HTTP | 기능 | Controller |
|
|
|-----------|------|------|------------|
|
|
| /api/text-to-sql/parse | POST | 자연어 쿼리를 SQL로 변환 | TextToSqlController.Parse |
|
|
| /api/text-to-sql/execute | POST | SQL 쿼리 실행 및 결과 반환 | TextToSqlController.Execute |
|
|
| /api/text-to-sql/suggest | GET | 쿼리 제안 (자동 완성) | TextToSqlController.Suggest |
|
|
| /api/text-to-sql/analyze | POST | 시계열 분석 (평균, 최대, 최소, 추세) | TextToSqlController.Analyze |
|
|
| /api/text-to-sql/query-history-interval | POST | 사용자 지정 간격으로 history 이력 조회 | TextToSqlController.QueryHistoryInterval |
|
|
|
|
### 관련 파일 목록
|
|
|
|
| 파일 경로 | 역할 |
|
|
|----------|------|
|
|
| src/Web/Controllers/TextToSqlController.cs | API 엔드포인트 구현 |
|
|
| src/Core/Application/Services/TextToSqlService.cs | 자연어 파싱, SQL 생성, 쿼리 실행 |
|
|
| src/Core/Application/DTOs/TextToSqlDtos.cs | 데이터 전송 객체 (DTO) |
|
|
| src/Core/Application/Interfaces/ITextToSqlService.cs | 서비스 인터페이스 |
|
|
| src/Core/Application/Services/KoreanTimeRangeExtractor.cs | 한국어 시간 범위 추출 |
|
|
| src/Core/Application/Services/SqlValidator.cs | SQL 검증 |
|
|
| src/Infrastructure/Database/ExperionDbContext.cs | DB 컨텍스트 |
|
|
|
|
## PHASE 2 - 테스트 프로그램 작성 (완료)
|
|
|
|
### 완료일: 2026-04-24
|
|
|
|
### 테스트 파일
|
|
|
|
| 파일 경로 | 역할 |
|
|
|----------|------|
|
|
| ExperionCrawler.Tests/TextToSqlTest.cs | TextToSqlService 통합 테스트 (task_state.md 매핑표 기반) |
|
|
|
|
### 테스트 항목
|
|
|
|
1. **SQL 생성 요청 → 응답 형식 검증**
|
|
- 유효한 입력으로 SQL 생성 확인
|
|
- 집계 함수 (avg, max, min, first, last) 검증
|
|
- 다중 태그 지원 확인
|
|
- OPC UA node_id 형식 지원 확인
|
|
|
|
2. **생성된 SQL 실행 → TimescaleDB 결과 반환 확인**
|
|
- 유효한 SQL 실행 및 결과 확인
|
|
- LIMIT 적용 확인
|
|
- 잘못된 SQL 처리 확인
|
|
- SQL 인젝션 방지 확인
|
|
|
|
3. **빈 입력 / 잘못된 입력 → 에러 핸들링**
|
|
- 빈 입력 예외 처리
|
|
- 공백만 입력 예외 처리
|
|
- 시간 키워드만 입력 예외 처리
|
|
- 설명만 입력 예외 처리
|
|
- 빈 SQL 예외 처리
|
|
- null SQL 예외 처리
|
|
- 잘못된 태그명 예외 처리
|
|
|
|
4. **한국어 자연어 입력 → SQL 변환 정확도**
|
|
- "최근 1시간/24시간/7일/1개월" 간격 변환 확인
|
|
- "부터 ~ 까지" 절대 범위 변환 확인
|
|
- "오전/오후" 시간 범위 변환 확인
|
|
- "이후" 패턴 변환 확인
|
|
- 한국어 설명 제거 및 태그명 추출 확인
|
|
- "데이터 중" 키워드 처리 확인
|
|
- 점 표기 태그명 처리 확인
|
|
- 기본 시간대 (5 min) 사용 확인
|
|
|
|
### 다음 PHASE
|
|
|
|
- [ ] PHASE 3: UI 기능 테스트 및 수정
|
|
- [ ] PHASE 4: API 엔드포인트 테스트
|
|
- [ ] PHASE 5: 전체 통합 테스트
|
|
|
|
## PHASE 2.5 - 테스트 실행 결과 (2026-04-25)
|
|
|
|
### 테스트 실행 요약
|
|
|
|
| 항목 | 결과 |
|
|
|------|------|
|
|
| **총 테스트 수** | 37개 |
|
|
| **통과** | 29개 (78.4%) |
|
|
| **실패** | 8개 (21.6%) |
|
|
|
|
### 실패한 테스트 항목
|
|
|
|
| # | 테스트 이름 | 라인 | 실패 원인 |
|
|
|---|------------|------|-----------|
|
|
| 1 | `ParseNaturalLanguageAsync_KoreanWithMiddleKeyword_ExtractsOnlyTagName` | 465 | 태그명 'aia-131.sp'가 추출되지 않음 - "중 aia-131.sp"에서 "중"이 제거되지 않음 |
|
|
| 2 | `ParseNaturalLanguageAsync_WithOnlyTimeKeyword_ThrowsArgumentException` | 238 | 시간 키워드만 입력 시 예외가 발생하지 않음 |
|
|
| 3 | `ExecuteQueryAsync_WithEmptySql_ThrowsException` | 255 | 빈 SQL 입력 시 예외가 발생하지 않음 |
|
|
| 4 | `ExecuteQueryAsync_WithInvalidTagInSql_ReturnsError` | 278 | 잘못된 태그명 SQL에서 에러가 반환되지 않음 |
|
|
| 5 | `ExecuteQueryAsync_WithNullSql_ThrowsException` | 265 | null SQL 입력 시 예외가 발생하지 않음 |
|
|
| 6 | `ParseNaturalLanguageAsync_KoreanWithTagKeyword_ExtractsOnlyTagName` | 451 | 태그명 'aia-131.sp'가 추출되지 않음 - "데이터 중 aia-131.sp"에서 "데이터 중"이 제거되지 않음 |
|
|
| 7 | `ParseNaturalLanguageAsync_WithOnlyDescription_ThrowsArgumentException` | 245 | 설명만 입력 시 예외가 발생하지 않음 |
|
|
| 8 | `ExecuteQueryAsync_WithInvalidSql_ReturnsError` | 197 | 잘못된 SQL에서 "PostgreSQL 오류"가 반환되지 않음 - SqlValidator에서 "필수 테이블이 없습니다" 메시지 반환 |
|
|
|
|
### 문제 분석
|
|
|
|
1. **태그명 추출 문제**: "데이터 중 aia-131.sp" 또는 "중 aia-131.sp" 형식에서 태그명이 제대로 추출되지 않음
|
|
- "데이터 중" 패턴 처리가 작동하지 않음
|
|
- "중" 키워드만 있는 경우 처리가 불완전함
|
|
|
|
2. **예외 처리 누락**: 빈 입력, null 입력, 시간/설명만 입력 시 예외가 발생하지 않음
|
|
- ExecuteQueryAsync에서 null/빈 SQL 검증 추가됨
|
|
- ParseNaturalLanguageAsync에서 시간/설명만 입력 검증 로직 추가됨
|
|
|
|
3. **에러 메시지 불일치**: 잘못된 SQL 테스트에서 예상한 에러 메시지("PostgreSQL 오류")가 반환되지 않음
|
|
- SqlValidator가 "필수 테이블이 없습니다" 메시지 반환
|
|
- ExecuteQueryAsync에서 NpgsqlException이 발생하지 않음
|
|
|
|
### 수정 필요 파일
|
|
|
|
- `src/Core/Application/Services/TextToSqlService.cs` - 태그명 추출 로직, 예외 처리 로직 수정
|
|
- `src/Core/Application/Services/SqlValidator.cs` - 에러 메시지 형식 확인 필요
|
|
|
|
## PHASE 4 - 수정 (완료)
|
|
|
|
### 완료일: 2026-04-25
|
|
|
|
### 수정 사항
|
|
|
|
1. **태그명 추출 로직 수정**
|
|
- `RemoveKoreanMiddleKeyword` 보조 메서드 추가
|
|
- "데이터 중" 및 "중" 키워드 처리 개선
|
|
- `ExtractTagNames` 메서드에서 보조 메서드 사용
|
|
|
|
2. **ExecuteQueryAsync 예외 처리 추가**
|
|
- 빈 SQL 입력 시 `ArgumentException` 예외 발생
|
|
- null SQL 입력 시 `ArgumentNullException` 예외 발생
|
|
|
|
3. **ParseNaturalLanguageAsync 예외 처리 강화**
|
|
- `IsTimeKeywordOnly` 메서드 추가
|
|
- `IsTagNameOnly` 메서드 추가
|
|
- 시간 키워드만 입력 시 예외 발생
|
|
- 설명만 입력 시 예외 발생
|
|
|
|
4. **테스트 실행**
|
|
- 실패했던 8개 테스트 항목 모두 통과
|
|
- 총 32개 테스트 중 32개 통과 (100%)
|
|
|
|
### 수정된 파일
|
|
|
|
- [`src/Core/Application/Services/TextToSqlService.cs`](src/Core/Application/Services/TextToSqlService.cs)
|
|
- [`ExperionCrawler.sln`](ExperionCrawler.sln) - 테스트 프로젝트 추가
|
|
|
|
## PHASE 1.5 - 코드 리뷰 분석 (완료)
|
|
|
|
### 완료일: 2026-04-26
|
|
|
|
### 분석 대상
|
|
|
|
분석된 파일은 15개:
|
|
- **HIGH 우선순위**: ExperionDbContext.cs, ExperionRealtimeService.cs, TextToSqlService.cs, ExperionOpcServerService.cs, ExperionOpcServerNodeManager.cs (5개)
|
|
- **MED 우선순위**: ExperionControllers.cs, TextToSqlController.cs, SqlValidator.cs, KoreanTimeRangeExtractor.cs, ExperionOpcClient.cs (5개)
|
|
- **LOW 우선순위**: IExperionServices.cs, ExperionDtos.cs, TextToSqlDtos.cs, Program.cs, ExperionHistoryService.cs (5개)
|
|
|
|
### 발견된 이슈
|
|
|
|
총 **19개 이슈** 발견:
|
|
|
|
| 카테고리 | 개수 |
|
|
|----------|------|
|
|
| HIGH | 6건 |
|
|
| MED | 8건 |
|
|
| LOW | 5건 |
|
|
|
|
### 주요 이슈 항목
|
|
|
|
**HIGH 우선순위**
|
|
| # | 문제 설명 | 위치 |
|
|
|---|-----------|------|
|
|
| 1 | 재진입 시 StartAsync 예외 무시 | ExperionRealtimeService.cs:120 |
|
|
| 2 | CheckTagExistsAsync 예외 무시 → true 반환 → SQL injection 위험 | TextToSqlService.cs:601 |
|
|
| 3 | CreateHistoryHypertableIfNotExistsAsync에서 raw SQL interpolation | ExperionDbContext.cs:216 |
|
|
| 4 | ExperionHypertableController.Create 파라미터 검증 부재 | ExperionHypertableController.cs:595 |
|
|
| 5 | ExperionControllers.Import 파일명 경로 조작 공격 가능성 | ExperionControllers.cs:212-213 |
|
|
| 6 | Disposable() 예외 무시로 리소스 누수 감지 불가 | ExperionOpcServerService.cs:286 |
|
|
|
|
**MED 우선순위**
|
|
| # | 문제 설명 |
|
|
|---|-----------|
|
|
| 7 | DisposeSessionAsync에서 중복 close 후 dispose 가능성 |
|
|
| 8 | AnalyzeAsync에서 SQL 인젝션 가능성 |
|
|
| 9 | Regex Singleline 옵션으로 예상치 못한 패턴 검출 |
|
|
| 10 | 날짜 추론 오류 (2025년 1월 3일이 과거로 처리) |
|
|
|
|
### 결과물
|
|
|
|
- [`issues.md`](issues.md) 생성 완료
|
|
|
|
### 다음 PHASE
|
|
|
|
- [x] Phase 2: 이슈 수정 (HIGH → MED → LOW 순서)
|
|
- HIGH: 6개 수정 완료 (커밋: 39f6138, 6f0aba4, 876f98f, 455526b, 072d0c9, e7409f7)
|
|
- MED: 4개 수정 완료 (커밋: 544b257, dd6ff78), 4개 needs-review
|
|
- LOW: 5건 all needs-review (주로 batch/refactoring)
|
|
|
|
- [x] Phase 3: 검수 요청서 (REVIEW_REQUEST.md) 생성
|
|
- 작성일: 2026-04-26 02:48 (약 31분 소요)
|
|
- 수정된 파일: 6개
|
|
- 커밋: 8개
|
|
- 빌드: 0 errors, 3 warnings
|
|
|
|
- [ ] Phase 4: 최종 검수 요청 (검수자에게 넘김)
|