# 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`)

`</body>` 직전의 `<script src="/js/app.js">` 태그 **앞에** CDN 스크립트 태그 삽입:

```html
<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` 호출 후 데이터를 잃지 않도록 모듈 스코프 변수에 저장한다.

파일 상단 전역 변수 영역에 추가:

```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 = '<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` 함수 바로 다음에 삽입:

```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 `<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>` 태그 앞
- **코드**:
  ```html
  <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 ────────────────────────────────────────── */` 전)
- **코드**:
  ```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`)을 통해 디버깅
