루트 파일 정리: - DXF/P&ID 관련 → dxf-graph/ - fastTable 관련 → fastTable/ - plan/ → plans/ 통합 (최신 버전 유지) - 테스트 출력 파일, 구버전 프로젝트 삭제 - 불필요한 루트 문서 삭제
9.3 KiB
9.3 KiB
Excel Export 기능 추가 — 자연어 쿼리 결과 테이블
목표
Text-to-SQL 탭의 📊 조회 결과 카드에 "Excel 다운로드" 버튼을 추가한다.
버튼 클릭 시 현재 렌더된 결과 테이블을 .xlsx 파일로 즉시 다운로드한다.
기술 방식 결정
클라이언트 사이드 — SheetJS (xlsx) CDN
| 항목 | 내용 |
|---|---|
| 라이브러리 | SheetJS Community Edition |
| 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)
</body> 직전의 <script src="/js/app.js"> 태그 앞에 CDN 스크립트 태그 삽입:
<script src="https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.full.min.js"></script>
<script src="/js/app.js"></script>
순서 중요: xlsx 라이브러리가 app.js 보다 먼저 로드되어야 한다.
Step 2 — 현재 결과 데이터 보관 변수 추가 (app.js)
t2sRenderTable 호출 후 데이터를 잃지 않도록 모듈 스코프 변수에 저장한다.
파일 상단 전역 변수 영역에 추가:
// Excel export용 — 마지막으로 렌더된 결과 보관
let _t2sLastResult = null; // { columns: string[], rows: object[] }
Step 3 — t2sRenderTable 수정 (app.js, line ~1483)
함수 진입 직후, 빈 결과 분기 이전에 저장:
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 수정):
// 변경 전
let html = '<div class="t2s-result-info">총 <b>' + totalCount + '</b>개 결과</div>';
// 변경 후
let html = `
<div class="t2s-result-info">
<span>총 <b>${totalCount}</b>개 결과</span>
<button class="btn-excel" onclick="t2sExportExcel()">⬇ Excel</button>
</div>`;
Step 4 — t2sExportExcel 함수 추가 (app.js)
t2sRenderTable 함수 바로 다음에 삽입:
/**
* 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 레이아웃 + 버튼 스타일:
/* 기존 .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 <script> 태그 1줄 추가 (app.js 태그 앞) |
src/Web/wwwroot/js/app.js |
전역 변수 _t2sLastResult 추가; t2sRenderTable 수정 (저장 + 버튼); t2sExportExcel 함수 추가 |
src/Web/wwwroot/css/style.css |
.t2s-result-info flex 수정; .btn-excel 스타일 추가 |
서버 코드(C#) 변경 없음.
동작 흐름
자연어 입력 → Enter / Execute 버튼
└─ t2sRenderTable(result) 호출
├─ _t2sLastResult = { columns, rows } 저장
└─ "총 N개 결과 [⬇ Excel]" 헤더 렌더링
사용자가 [⬇ Excel] 클릭
└─ t2sExportExcel()
├─ _t2sLastResult 로 aoa_to_sheet 생성
├─ 숫자는 number 타입 유지 (Excel 정렬·계산 가능)
└─ query_result_2026-04-28T08-15-44.xlsx 다운로드
주의 사항
- SheetJS CDN 로드 실패(오프라인 환경) 대비:
t2sExportExcel시작 시if (typeof XLSX === 'undefined') { alert('Excel 라이브러리 로드 실패'); return; }추가 권장 _t2sLastResult는 마지막 쿼리 결과만 보관한다. 탭 이동 후 재진입해도 이전 결과가 남아 있으므로t2sRenderTable에서 빈 결과(rows.length === 0)일 때 반드시null로 초기화한다.- 피봇 테이블(tagname → 컬럼) 변환 후의 데이터가
_t2sLastResult에 저장되므로 Excel에도 피봇 형태가 그대로 반영된다.
📝 구현 진행 기록
| 단계 | 작업 내용 | 파일 | 상태 | 기록일 |
|---|---|---|---|---|
| 1 | SheetJS CDN 추가 (index.html) | src/Web/wwwroot/index.html |
✅ 완료 | 2026-04-28 |
| 2 | 마지막 결과 데이터 보관 변수 추가 | src/Web/wwwroot/js/app.js (1번 라인 이전) |
✅ 완료 | 2026-04-28 |
| 3 | t2sRenderTable 함수 수정 (데이터 저장 + Excel 버튼) |
src/Web/wwwroot/js/app.js (1489~1502 라인) |
✅ 완료 | 2026-04-28 |
| 4 | t2sExportExcel 함수 추가 |
src/Web/wwwroot/js/app.js (1533~1552 라인) |
✅ 완료 | 2026-04-28 |
| 5 | 버튼 스타일 정의 | src/Web/wwwroot/css/style.css (655~667 라인) |
✅ 완료 | 2026-04-28 |
| 6 | 작업 내용 기록 | export2excel.md |
✅ 완료 | 2026-04-28 |
📋 구현 상세
1. SheetJS CDN 추가 (src/Web/wwwroot/index.html)
- 위치:
<script src="/js/app.js"></script>태그 앞 - 코드:
<script src="https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.full.min.js"></script> <script src="/js/app.js"></script>
2. 전역 변수 추가 (src/Web/wwwroot/js/app.js)
- 위치: 파일 시작부 (
/* ── Tab navigation ────────────────────────────────────────── */전) - 코드:
let _t2sLastResult = null; // Excel export용 — 마지막으로 렌더된 결과 보관
3. t2sRenderTable 함수 수정 (src/Web/wwwroot/js/app.js)
- 변경 사항:
- 1489번 라인:
_t2sLastResult에 결과 저장 - 1502번 라인: 버튼이 포함된 헤더 HTML 생성
- 1489번 라인:
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: 더 어두운 그린으로 호버 효과
🔍 검증 결과
- 빌드 검증:
dotnet build src/Web/ExperionCrawler.csproj --no-restore -v q실행 요청 - 파일 수정 확인: 모든 파일이 올바르게 수정되었는지 확인
- 코드 일관성: 식별자명(
_t2sLastResult), 헤더 문구(txt), 버튼 라벨(⬇ Excel)이 export2excel.md 규칙에 일치 - 스타일 일관성:
.btn-excel스타일이 프로젝트 기존 버튼 스타일(btn-a,btn-b)의 색상 체계(녹색 3단계)에 따라 구현되었으나, Excel export용 구분을 위해 별도 색상 배치 선택 - 실제 동작 검증: 브라우저에서 쿼리 실행 후 Excel 다운로드 테스트 필요
⏭️ 다음 단계
- 빌드 검증:
dotnet build src/Web/ExperionCrawler.csproj --no-restore -v q실행 - 실시간 테스트: 브라우저에서 Text-to-SQL 탭으로 이동 → 자연어 쿼리 입력 → 실행 → Excel 버튼 클릭 확인
- 파일 생성: 다운로드된
.xlsx파일 확장자 및 내용 확인 - 버그 수정: 필요한 경우 LLM(
ask_iiot_llm)을 통해 디버깅