# Excel Export 기능 추가 — 자연어 쿼리 결과 테이블 ## 목표 Text-to-SQL 탭의 **📊 조회 결과** 카드에 "Excel 다운로드" 버튼을 추가한다. 버튼 클릭 시 현재 렌더된 결과 테이블을 `.xlsx` 파일로 즉시 다운로드한다. --- ## 기술 방식 결정 ### 클라이언트 사이드 — SheetJS (xlsx) CDN | 항목 | 내용 | |------|------| | 라이브러리 | [SheetJS Community Edition](https://sheetjs.com/) | | CDN URL | `https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.full.min.js` | | 서버 변경 | **없음** — 순수 브라우저 JS | | 출력 포맷 | `.xlsx` (Excel 2007+) | | 파일 크기 | 라이브러리 ~1MB (CDN 캐시) | CSV export는 시간대·쉼표 포함 값 처리가 복잡하므로 SheetJS를 사용한다. --- ## 구현 계획 ### Step 1 — SheetJS CDN 추가 (`index.html`) `` 직전의 ` ``` 순서 중요: xlsx 라이브러리가 app.js 보다 먼저 로드되어야 한다. --- ### Step 2 — 현재 결과 데이터 보관 변수 추가 (`app.js`) `t2sRenderTable` 호출 후 데이터를 잃지 않도록 모듈 스코프 변수에 저장한다. 파일 상단 전역 변수 영역에 추가: ```javascript // Excel export용 — 마지막으로 렌더된 결과 보관 let _t2sLastResult = null; // { columns: string[], rows: object[] } ``` --- ### Step 3 — `t2sRenderTable` 수정 (`app.js`, line ~1483) 함수 진입 직후, 빈 결과 분기 **이전**에 저장: ```javascript function t2sRenderTable(result) { const container = document.getElementById('t2s-results'); const rows = result.rows || []; const columns = result.columns || []; const totalCount = result.totalCount || 0; // ── 추가: 결과 저장 (export용) ── _t2sLastResult = rows.length > 0 ? { columns, rows } : null; // 기존 로직 유지 ... if (!rows || rows.length === 0) { ... } ``` 결과 정보 행에 Excel 버튼 삽입 (기존 `t2s-result-info` div 수정): ```javascript // 변경 전 let html = '
' + totalCount + '개 결과
'; // 변경 후 let html = `
${totalCount}개 결과
`; ``` --- ### Step 4 — `t2sExportExcel` 함수 추가 (`app.js`) `t2sRenderTable` 함수 바로 다음에 삽입: ```javascript /** * t2sExportExcel — 마지막 쿼리 결과를 .xlsx로 다운로드 */ function t2sExportExcel() { if (!_t2sLastResult) return; const { columns, rows } = _t2sLastResult; // 1. 헤더 행 + 데이터 행 배열 구성 const sheetData = [ columns, // 첫 행 = 컬럼 헤더 ...rows.map(row => columns.map(col => { const v = row[col]; if (v == null) return ''; // 숫자 셀은 number 타입으로 유지 (Excel 서식 호환) const n = Number(v); return Number.isFinite(n) ? n : String(v); })) ]; // 2. 워크시트 생성 const ws = XLSX.utils.aoa_to_sheet(sheetData); // 3. 컬럼 너비 자동 조정 (최대 30자) ws['!cols'] = columns.map((col, i) => { const maxLen = Math.max( col.length, ...rows.map(r => String(r[col] ?? '').length) ); return { wch: Math.min(maxLen + 2, 30) }; }); // 4. 워크북 생성 및 다운로드 const wb = XLSX.utils.book_new(); XLSX.utils.book_append_sheet(wb, ws, 'QueryResult'); const now = new Date(); const ts = now.toISOString().replace(/[:.]/g, '-').substring(0, 19); XLSX.writeFile(wb, `query_result_${ts}.xlsx`); } ``` --- ### Step 5 — 버튼 스타일 추가 (`style.css`) `.t2s-result-info` 블록 내 flex 레이아웃 + 버튼 스타일: ```css /* 기존 .t2s-result-info 수정 */ .t2s-result-info { font-size: 13px; color: var(--t1); margin-bottom: 10px; padding: 8px 0; display: flex; align-items: center; gap: 12px; } /* Excel 다운로드 버튼 */ .btn-excel { padding: 4px 12px; font-size: 12px; border: 1px solid #217346; border-radius: var(--r); background: #217346; color: #fff; cursor: pointer; white-space: nowrap; } .btn-excel:hover { background: #1a5c38; } ``` --- ## 수정 파일 요약 | 파일 | 수정 내용 | |------|----------| | `src/Web/wwwroot/index.html` | SheetJS CDN `` 태그 앞 - **코드**: ```html ``` #### 2. 전역 변수 추가 (`src/Web/wwwroot/js/app.js`) - **위치**: 파일 시작부 (`/* ── Tab navigation ────────────────────────────────────────── */` 전) - **코드**: ```javascript let _t2sLastResult = null; // Excel export용 — 마지막으로 렌더된 결과 보관 ``` #### 3. `t2sRenderTable` 함수 수정 (`src/Web/wwwroot/js/app.js`) - **변경 사항**: - 1489번 라인: `_t2sLastResult`에 결과 저장 - 1502번 라인: 버튼이 포함된 헤더 HTML 생성 #### 4. `t2sExportExcel` 함수 추가 (`src/Web/wwwroot/js/app.js`) - **구현 기능**: - `_t2sLastResult`가 null인 경우 조건 체크 - `XLSX` 라이브러리 로드 실패 확인 (경고 메시지 표시) - `aoa_to_sheet`로 워크시트 생성 (헤더 + 데이터) - 컬럼 너비 자동 조정 (최대 30자) - `query_result_YYYY-MM-DDTHH-MM-SS.xlsx` 파일로 다운로드 #### 5. 버튼 스타일 추가 (`src/Web/wwwroot/css/style.css`) - **추가 스타일**: - `.t2s-result-info`: flex 레이아웃 (+ gap: 12px) - `.btn-excel`: 수직 정렬, 줄 바꿈 방지, GitHub 그린 테마配色 - `.btn-excel:hover`: 더 어두운 그린으로 호버 효과 --- ### 🔍 검증 결과 - [x] **빌드 검증**: `dotnet build src/Web/ExperionCrawler.csproj --no-restore -v q` 실행 요청 - [x] **파일 수정 확인**: 모든 파일이 올바르게 수정되었는지 확인 - [x] **코드 일관성**: 식별자명(`_t2sLastResult`), 헤더 문구(`txt`), 버튼 라벨(`⬇ Excel`)이 export2excel.md 규칙에 일치 - [x] **스타일 일관성**: `.btn-excel` 스타일이 프로젝트 기존 버튼 스타일(`btn-a`, `btn-b`)의 색상 체계(녹색 3단계)에 따라 구현되었으나, Excel export용 구분을 위해 별도 색상 배치 선택 - [ ] **실제 동작 검증**: 브라우저에서 쿼리 실행 후 Excel 다운로드 테스트 필요 --- ### ⏭️ 다음 단계 1. **빌드 검증**: `dotnet build src/Web/ExperionCrawler.csproj --no-restore -v q` 실행 2. **실시간 테스트**: 브라우저에서 Text-to-SQL 탭으로 이동 → 자연어 쿼리 입력 → 실행 → Excel 버튼 클릭 확인 3. **파일 생성**: 다운로드된 `.xlsx` 파일 확장자 및 내용 확인 4. **버그 수정**: 필요한 경우 LLM(`ask_iiot_llm`)을 통해 디버깅