# 1.Term-Project 진행 Flow 가이드 및 실습

---

# 1. **프로젝트 목표 (Objective)**

Google Sheets + Apps Script + Gemini API를 활용하여
**마케팅 데이터를 자동 분석하고 인사이트를 제공하는 대시보드 구축**을 목표로 한다.

이 대시보드는

- EDA(기초 분석)

- 상관관계 탐색

- 간단 회귀/군집 분석

- 마케팅 인사이트 자동 생성
- 등을 포함하여,
- 초보자도 수행 가능하고 실무적 흐름을 경험할 수 있는 수준으로 설계한다.

---

# 2. **데이터셋 (Dataset) 선택**

### 선택: **Superstore Sales Dataset 이나 개인별 분석을 원하는 데이터셋**

(단일 테이블 구조, 마케팅 분석 적용 용이, Google Sheets에 적합)

### 주요 특징

- Category, Subcategory

- Region, Segment

- Sales, Profit

- Discount, Quantity

데이터 전처리는 최소화하여 진행하고,

구축을 위해서 **약 100~300행 샘플**을 Google Sheets에 업로드한다.

(구축 후, 전체 데이터 추가 업로드. 10,000행 넘어가면 시간 많이 걸리거나 down가능)

---

# 3. **전체 작업 흐름(Workflow Overview)**

```
1) 데이터셋 선택 및 샘플링
2) Google Sheets에 업로드
3) 기본 EDA 수행 (학생 + Gemini 보조)
4) 분석 범위 확정 (상관관계/회귀/군집 중 선택)
5) Apps Script 대시보드 설계
6) Gemini API 연결
7) 분석 기능(EDA, 상관관계, 인사이트 생성) 구현
8) 대시보드 완성
9) 최종 마케팅 인사이트 보고서 도출
```

---

# 4. **프로젝트 구성 요소 (Components)**

## **4-1. 데이터 시트 구성**

- 소스 데이터가 있는 시트

- 전처리가 된 데이터 시트(학생에 따라 필요시)

- 분석 요청 시 Apps Script가 Sheets에서 데이터를 읽어 분석 진행토록 함.

---

## 4-2. **프로젝트 산출물 (Deliverables) - 아래 예시**

1. Google Sheets 데이터

2. Apps Script 코드

3. HTML 대시보드

4. AI(Gemini API) 기반 분석 기능

5. 최종 마케팅 인사이트 요약 보고서 (5~10개 인사이트)

---

# 5. **진행 단계 로드맵 (Phase Plan)**

### **Phase 1 — 데이터 준비**

- Kaggle dataset 다운로드

- 100-300행 샘플링하여 Google Sheets로 업로드

- 데이터 이해 및 변수 설명 정리

### **Phase 2 — 간단 EDA**

- 기초 통계

- 카테고리/지역/세그먼트별 패턴 탐색

- 학생이 직접 EDA + Gemini에게 추가 보조 EDA 요청

### **Phase 3 — 대시보드 기획**

- 화면 구성 요소 스케치

- 버튼/입력UI 확정

- Apps Script 함수 맵핑

### **Phase 4 — 기본 기능 구현**

- EDA

- 상관분석

- LLM 기반 인사이트 기능 구현

### **Phase 5 — 고급 분석 추가(선택)**

- 회귀

- 군집

- A/B 비교

### **Phase 6 — 최종 보고서 제작**

- 인사이트 정리

- 액션 아이디어 제시

- 대시보드 스크린샷 포함

---

---

## 🎓 [실습 가이드 1단계] Apps Script 프로젝트 생성 및 기본 설정

### 1️⃣ 구글 시트 준비

1. 브라우저 주소창에 `[sheets.new](https://sheets.new)`를 입력하여 새 스프레드시트를 만듭니다.

2. 좌측 상단 제목을 `마케팅 분석 대시보드 실습`으로 변경합니다.

3. 시트 하단의 탭 이름을 `Data_Lake`로 변경합니다. (대소문자 정확히)

### 2️⃣ 데이터 준비 (실습용)

- Google DocsAdvertising_Data > `사본 만들기` > 각자 드라이브에 저장

- 데이터 시트를 `Data_Lake`로 변경 (대소문자 정확히)

- 실습 데이터

[Term_project-advertising_data-v.3](https://docs.google.com/spreadsheets/d/1aBDv_pzStUjRjJdDUrrRm6z3-iumK3tB_qpFZLfx-VI/edit?usp=sharing)

> 텀플용 데이터 

- 몇 천행을 넘어가는 데이터는 토큰 제한으로 AI가 버거워할 수 있음

- 따라서, 대시보드 구축할 때에는 100-300행 정도의 데이터로 축소시켜서 진행토록 함

### 3️⃣ Apps Script 열기

1. 상단 메뉴에서 **[확장 프로그램]**  [Apps Script]를 클릭합니다.

2. 새 탭이 열리면 프로젝트 제목을 `Marketing Dashboard App`으로 변경합니다.

---

> `프로젝트 설정`  > `스크립트 속성` 에 개인별 Gemini API 키를 입력하고 저장하세요. 
> 

![Image](https://upload.cafenono.com/image/slashpageHome/20251128/174401_W1qYA9KnESJqy91K3f?q=80&s=1280x180&t=outside&f=webp)

## 🎓 [실습 가이드 2단계] 서버 코드 (`[Code.gs](https://Code.gs)`) 작성

- 데이터를 처리하고 HTML 화면을 띄워줄 서버 코드를 작성합니다. 

- 기존에 있는 코드를 모두 지우고, 아래 코드를 복사 & 붙여넣기

[Code.gs](https://Code.gs) - 펼쳐보기

```
// ==========================================
// 🚀 통합 분석 센터 (Server: Code.gs) v4.0 - ERD, 상관관계
// ==========================================

// 1. 설정: Gemini API 키 (스크립트 속성에서 가져옴)
const GEMINI_API_KEY = PropertiesService.getScriptProperties().getProperty('GEMINI_API_KEY');
const GEMINI_MODEL = 'gemini-2.5-flash'; 

// 2. 메뉴 생성 (시트 열릴 때 자동 실행)
function onOpen() {
  SpreadsheetApp.getUi()
    .createMenu('🚀 통합 분석 센터')
    .addItem('📊 대시보드 열기', 'showDashboard')
    .addToUi();
}

// 3. HTML 대시보드 띄우기
function showDashboard() {
  const html = HtmlService.createTemplateFromFile('Dashboard')
      .evaluate()
      .setTitle('📊 마케팅 데이터 종합 분석')
      .setWidth(1200)
      .setHeight(900);
  SpreadsheetApp.getUi().showModalDialog(html, ' ');
}

// 4. [API] 데이터 전송 함수 (프론트엔드 요청 시 실행)
function getFullData() {
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const dataSheet = ss.getSheetByName('Data_Lake');
  
  if (!dataSheet) throw new Error("'Data_Lake' 시트가 없습니다.");

  const range = dataSheet.getDataRange();
  const rawValues = range.getValues();
  
  if (rawValues.length < 2) throw new Error("데이터가 부족합니다. (헤더 포함 2행 이상)");

  const headers = rawValues[0];
  const rows = rawValues.slice(1).filter(r => r[0] !== "");

  // 기초 통계 계산
  const stats = calculateBasicStats(headers, rows);

  return {
    headers: headers,
    rows: rows,
    stats: stats
  };
}

// 5. 기초 통계 계산 로직
function calculateBasicStats(headers, rows) {
  let stats = {};
  headers.forEach((col, idx) => {
    if (typeof rows[0][idx] === 'number') {
      let vals = rows.map(r => r[idx]).filter(v => typeof v === 'number');
      if (vals.length > 0) {
        let mean = vals.reduce((a, b) => a + b, 0) / vals.length;
        let sorted = [...vals].sort((a, b) => a - b);
        let min = sorted[0];
        let max = sorted[sorted.length - 1];
        let median = sorted[Math.floor(sorted.length / 2)];
        
        let variance = vals.reduce((a, b) => a + Math.pow(b - mean, 2), 0) / vals.length;
        let std = Math.sqrt(variance);

        stats[col] = {
          mean: mean.toFixed(2),
          median: median,
          min: min,
          max: max,
          std: std.toFixed(2),
          count: vals.length
        };
      }
    }
  });
  return stats;
}

// 6. AI 리포트 생성 함수
function askGeminiInsight(mode, contextData) {
  if (!GEMINI_API_KEY) return "⚠️ API 키가 설정되지 않았습니다. [프로젝트 설정 > 스크립트 속성]에서 키를 추가하세요.";
  
  const url = `https://generativelanguage.googleapis.com/v1beta/models/${GEMINI_MODEL}:generateContent?key=${GEMINI_API_KEY}`;
  
  // 데이터 길이 제한 (에러 방지)
  const dataString = JSON.stringify(contextData).substring(0, 3000);
  
  const prompt = `
  데이터 분석가로서 아래 통계 데이터를 보고 핵심을 요약해주세요.
  모드: ${mode}
  데이터: ${dataString}
  
  [작성 규칙]
  1. 서론/결론 제외, 바로 본론 시작.
  2. 다음 4가지 관점에서 각각 1~2줄로 요약:
     - 분포 특성
     - 이상치(Outlier) 존재 여부
     - 변수 간 상관관계 핵심
     - 판매량(Target) 영향 요인
  3. 마크다운 형식으로 작성.
  `;
  
  try {
    const payload = { contents: [{ parts: [{ text: prompt }] }] };
    const options = { method: 'post', contentType: 'application/json', payload: JSON.stringify(payload), muteHttpExceptions: true };
    const response = UrlFetchApp.fetch(url, options);
    const json = JSON.parse(response.getContentText());
    if (json.error) return "AI 오류: " + json.error.message;
    return json.candidates[0].content.parts[0].text;
  } catch (e) { return "AI 호출 실패: " + e.toString(); }
}
```

## 🎓 [실습 가이드 3단계] 대시보드 화면 (Dashboard.html) 구현

- 사용자가 실제로 보게 될 **화면(Frontend)**을 구현

- 이 코드에는 Chart.js(차트 그리기), 이미지 다운로드 기능, 그리고 초보자를 위한 통계 가이드가 모두 포함

1️⃣ HTML 파일 생성

    - Apps Script 편집기 왼쪽 파일 목록에서 + (파일 추가) 버튼을 클릭

    - HTML을 선택하고, 파일 이름을 **Dashboard**로 입력(확장자 .html은 자동 입력됨)

- 기존 코드를 모두 지우고, 아래 html 코드를 복사 & 붙여넣기

[Dashboard.html](https://Dashboard.html) - 펼쳐보기

```
<!DOCTYPE html>
<html>
<head>
  <base target="_top">
  <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
  <script src="https://unpkg.com/@sgratzl/chartjs-chart-boxplot@3.6.0/build/index.umd.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;800&display=swap" rel="stylesheet">
  <style>
    body { font-family: 'Inter', sans-serif; background: #f3f4f6; padding: 20px; color: #1f2937; margin: 0; }
    
    /* 헤더 */
    header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; }
    h1 { margin: 0; font-size: 1.5rem; color: #111827; }
    
    /* 탭 메뉴 */
    .tabs { display: flex; gap: 10px; margin-bottom: 20px; background: white; padding: 8px; border-radius: 12px; }
    .tab-btn { flex: 1; padding: 12px; border: none; background: transparent; cursor: pointer; font-weight: 600; color: #6b7280; border-radius: 8px; transition: 0.2s; }
    .tab-btn:hover { background: #f9fafb; }
    .tab-btn.active { background: #3b82f6; color: white; box-shadow: 0 4px 6px -1px rgba(59, 130, 246, 0.5); }

    /* 컨텐츠 섹션 */
    .view-section { display: none; }
    .view-section.active { display: block; }
    
    .section-title { font-size: 1.2rem; font-weight: 800; margin: 30px 0 15px 0; border-left: 5px solid #3b82f6; padding-left: 10px; color: #1f2937; }
    .card { background: white; border-radius: 16px; padding: 20px; box-shadow: 0 2px 4px rgba(0,0,0,0.05); margin-bottom: 20px; position: relative; }
    
    /* 차트 그리드 */
    .chart-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 15px; }
    .chart-box { height: 250px; position: relative; }

    /* 다운로드 버튼 */
    .download-btn {
      position: absolute; top: 10px; right: 10px;
      background: white; border: 1px solid #e5e7eb; border-radius: 6px;
      padding: 4px 8px; font-size: 0.75rem; cursor: pointer; color: #6b7280;
      z-index: 10;
    }
    .download-btn:hover { background: #f3f4f6; color: #3b82f6; }
    
    /* 히트맵 테이블 */
    .heatmap-table { width: 100%; border-collapse: collapse; font-size: 0.8rem; text-align: center; }
    .heatmap-table th, .heatmap-table td { padding: 8px; border: 1px solid #eee; }
    .heatmap-table th { background: #f9fafb; font-weight: 600; }

    /* AI 박스 */
    .ai-box { background: #eff6ff; border: 1px solid #bfdbfe; padding: 20px; border-radius: 12px; line-height: 1.6; margin-bottom: 20px; }
    .ai-header { font-weight: bold; color: #1e40af; margin-bottom: 10px; display: flex; align-items: center; gap: 8px; }

    /* 로딩 */
    #loading { display: none; text-align: center; padding: 50px; }
    .spinner { border: 4px solid #f3f3f3; border-top: 4px solid #3b82f6; border-radius: 50%; width: 40px; height: 40px; animation: spin 1s linear infinite; margin: 0 auto; }
    @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }

    /* 도움말 탭 스타일 */
    .guide-box { background: #fff; padding: 20px; border-radius: 12px; margin-bottom: 15px; border-left: 4px solid #10b981; }
    .guide-title { font-weight: bold; font-size: 1.1rem; color: #059669; margin-bottom: 5px; }
  </style>
</head>
<body>

  <header>
    <h1>🚀 마케팅 데이터 종합 분석</h1>
    <div style="font-size:0.8rem; color:#6b7280;">Live Connected</div>
  </header>

  <div class="tabs">
    <button class="tab-btn active" onclick="switchTab('basic')">📋 기초 통계 (종합)</button>
    <button class="tab-btn" onclick="switchTab('guide')">📘 분석 가이드 (Help)</button>
  </div>

  <div id="loading">
    <div class="spinner"></div>
    <p style="margin-top:10px; color:#6b7280;">데이터를 분석하고 차트를 생성 중입니다...</p>
  </div>

  <div id="view-basic" class="view-section">
    
    <div class="card">
      <div class="ai-box">
        <div class="ai-header">🤖 AI 데이터 인사이트</div>
        <div id="ai-basic-report">분석 중...</div>
      </div>
    </div>

    <div class="section-title">1. 데이터 분포 (Distributions)</div>
    <div class="chart-grid" id="dist-grid"></div>

    <div class="section-title">2. 이상치 확인 (Boxplots)</div>
    <div class="chart-grid" id="box-grid"></div>

    <div class="section-title">3. 판매량과의 관계 (Scatter vs Product_Sold)</div>
    <div class="chart-grid" id="scatter-grid"></div>

    <div class="section-title">4. 상관관계 히트맵 (Correlation Matrix)</div>
    <div class="card" style="overflow-x:auto;">
      <table class="heatmap-table" id="heatmap-table"></table>
    </div>
  </div>

  <div id="view-guide" class="view-section">
    <div class="guide-box">
      <div class="guide-title">📊 히스토그램 (Histogram)</div>
      <p>데이터가 어디에 많이 몰려있는지 보여줍니다. 막대가 높은 구간이 가장 흔한 값입니다. 만약 한쪽으로 치우쳐 있다면(비대칭), 평균값 사용에 주의해야 합니다.</p>
    </div>
    <div class="guide-box">
      <div class="guide-title">📦 박스 플롯 (Box Plot)</div>
      <p>데이터의 퍼짐 정도와 '이상치(Outlier)'를 찾습니다. 박스 위아래에 점이 찍혀 있다면, 다른 값들과 동떨어진 특이한 데이터가 있다는 뜻입니다.</p>
    </div>
    <div class="guide-box">
      <div class="guide-title">📉 산점도 (Scatter Plot)</div>
      <p>두 변수의 관계를 점으로 찍어 보여줍니다. 점들이 우상향하면 '양의 상관관계(비례)', 우하향하면 '음의 상관관계(반비례)'입니다. 점들이 뭉쳐있을수록 관계가 강합니다.</p>
    </div>
    <div class="guide-box">
      <div class="guide-title">🔗 상관계수 (Correlation Coefficient)</div>
      <p><strong>+1에 가까울수록:</strong> 아주 강한 양의 관계 (하나가 늘면 다른 것도 확실히 늠)<br>
      <strong>0에 가까울수록:</strong> 관계 없음<br>
      <strong>-1에 가까울수록:</strong> 아주 강한 음의 관계 (하나가 늘면 다른 건 줄어듦)</p>
    </div>
  </div>

  <script>
    let globalData = null;

    // 초기 로딩
    window.onload = function() {
      document.getElementById('loading').style.display = 'block';
      google.script.run.withSuccessHandler(initDashboard).getFullData();
    };

    function initDashboard(data) {
      globalData = data;
      document.getElementById('loading').style.display = 'none';
      switchTab('basic');
    }

    function switchTab(tabName) {
      document.querySelectorAll('.view-section').forEach(el => el.classList.remove('active'));
      document.getElementById('view-' + tabName).classList.add('active');
      document.querySelectorAll('.tab-btn').forEach(el => el.classList.remove('active'));
      event.target.classList.add('active');
      
      if (tabName === 'basic') renderBasicAnalysis();
    }

    // === 종합 분석 렌더링 ===
    function renderBasicAnalysis() {
      const headers = globalData.headers;
      const rows = globalData.rows;
      const numericHeaders = headers.filter(h => globalData.stats[h]); 
      const targetVar = 'Product_Sold';

      callAI('EDA_SUMMARY', globalData.stats, 'ai-basic-report');

      // 1. 분포 (Histograms)
      const distGrid = document.getElementById('dist-grid');
      distGrid.innerHTML = '';
      numericHeaders.forEach(col => {
        const div = createChartCard(`dist-${col}`);
        distGrid.appendChild(div);
        const vals = rows.map(r => r[headers.indexOf(col)]);
        drawHistogram(`dist-${col}`, vals, col);
      });

      // 2. 이상치 (Boxplots)
      const boxGrid = document.getElementById('box-grid');
      boxGrid.innerHTML = '';
      numericHeaders.forEach(col => {
        const div = createChartCard(`box-${col}`);
        boxGrid.appendChild(div);
        const vals = rows.map(r => r[headers.indexOf(col)]);
        drawBoxplot(`box-${col}`, vals, col);
      });

      // 3. 산점도 (Scatter)
      const scatterGrid = document.getElementById('scatter-grid');
      scatterGrid.innerHTML = '';
      numericHeaders.forEach(col => {
        if (col === targetVar) return; 
        const div = createChartCard(`scat-${col}`);
        scatterGrid.appendChild(div);
        const xVals = rows.map(r => r[headers.indexOf(col)]);
        const yVals = rows.map(r => r[headers.indexOf(targetVar)]);
        drawScatter(`scat-${col}`, xVals, yVals, col, targetVar);
      });

      renderHeatmap(numericHeaders, rows, headers);
    }

    // 차트 카드 생성 (다운로드 버튼 포함)
    function createChartCard(canvasId) {
      const div = document.createElement('div');
      div.className = 'card chart-box';
      div.innerHTML = `
        <button class="download-btn" onclick="downloadChart('${canvasId}')">📷 저장</button>
        <canvas id="${canvasId}"></canvas>
      `;
      return div;
    }

    // 차트 이미지 다운로드 함수
    function downloadChart(canvasId) {
      const link = document.createElement('a');
      link.download = canvasId + '.png';
      link.href = document.getElementById(canvasId).toDataURL();
      link.click();
    }

    // --- 차트 그리기 헬퍼 함수들 ---
    function drawHistogram(canvasId, data, label) {
      const ctx = document.getElementById(canvasId).getContext('2d');
      const bins = 10;
      const min = Math.min(...data);
      const max = Math.max(...data);
      const step = (max - min) / bins;
      let freq = new Array(bins).fill(0);
      let labels = [];
      for(let i=0; i<bins; i++) labels.push((min + step*i).toFixed(0));
      data.forEach(v => {
        let bucket = Math.floor((v - min) / step);
        if (bucket >= bins) bucket = bins - 1;
        freq[bucket]++;
      });

      new Chart(ctx, {
        type: 'bar',
        data: {
          labels: labels,
          datasets: [{ label: label, data: freq, backgroundColor: '#3b82f6', borderRadius: 4 }]
        },
        options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false }, title: { display: true, text: label } }, scales: { x: { display: false } } }
      });
    }

    function drawBoxplot(canvasId, data, label) {
      const ctx = document.getElementById(canvasId).getContext('2d');
      new Chart(ctx, {
        type: 'boxplot',
        data: {
          labels: [label],
          datasets: [{ label: label, data: [data], backgroundColor: 'rgba(59, 130, 246, 0.5)', borderColor: '#3b82f6', borderWidth: 1 }]
        },
        options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false }, title: { display: true, text: label } } }
      });
    }

    function drawScatter(canvasId, xData, yData, xLabel, yLabel) {
      const ctx = document.getElementById(canvasId).getContext('2d');
      const points = xData.map((x, i) => ({ x: x, y: yData[i] }));
      new Chart(ctx, {
        type: 'scatter',
        data: { datasets: [{ label: `${xLabel} vs Sales`, data: points, backgroundColor: 'rgba(16, 185, 129, 0.6)', pointRadius: 3 }] },
        options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false }, title: { display: true, text: `${xLabel} vs Sales` } }, scales: { x: { display: true, title: {display:false} }, y: { display: false } } }
      });
    }

    function renderHeatmap(vars, rows, allHeaders) {
      const table = document.getElementById('heatmap-table');
      let html = '<thead><tr><th></th>';
      vars.forEach(v => html += `<th>${v}</th>`);
      html += '</tr></thead><tbody>';
      vars.forEach((v1) => {
        html += `<tr><th>${v1}</th>`;
        vars.forEach((v2) => {
          const val1 = rows.map(r => r[allHeaders.indexOf(v1)]);
          const val2 = rows.map(r => r[allHeaders.indexOf(v2)]);
          const corr = calculateCorr(val1, val2);
          const color = corr > 0 ? `rgba(239, 68, 68, ${corr})` : `rgba(59, 130, 246, ${Math.abs(corr)})`;
          const textColor = Math.abs(corr) > 0.5 ? 'white' : 'black';
          html += `<td style="background:${color}; color:${textColor}">${corr.toFixed(2)}</td>`;
        });
        html += '</tr>';
      });
      html += '</tbody>';
      table.innerHTML = html;
    }

    function calculateCorr(x, y) {
      let n = x.length;
      let sumX = x.reduce((a, b) => a + b, 0);
      let sumY = y.reduce((a, b) => a + b, 0);
      let sumXY = x.reduce((s, xi, i) => s + xi * y[i], 0);
      let sumX2 = x.reduce((s, xi) => s + xi * xi, 0);
      let sumY2 = y.reduce((s, yi) => s + yi * yi, 0);
      let num = (n * sumXY) - (sumX * sumY);
      let den = Math.sqrt((n * sumX2 - sumX*sumX) * (n * sumY2 - sumY*sumY));
      return den === 0 ? 0 : num / den;
    }

    function callAI(mode, context, elementId) {
      google.script.run.withSuccessHandler(text => {
        document.getElementById(elementId).innerHTML = marked.parse(text); 
      }).askGeminiInsight(mode, context);
    }
  </script>
</body>
</html>
```

- 시트 사용 방법

    - 메뉴에서 `🚀 통합 분석 센터` > `📊대시보드 열기`  클릭

![Image](https://upload.cafenono.com/image/slashpageHome/20251130/165502_6wOTV6KXwDW8HPCKuz?q=80&s=1280x180&t=outside&f=webp)

    - `📋 기초 통계 (종합)` 클릭

    - 팝업(modal) 형태로 대시보드 생성

        - 인사이트 확인 및 이미지 `저장` 기능과 마우스 오버 인터렉티브 기능 등 확인

![Image](https://upload.cafenono.com/image/slashpageHome/20251130/170639_D2Ucb4yywjNOEOm86C?q=80&s=1280x180&t=outside&f=webp)

---

---

## 개인별로 시작하는 Prompt 가이드

- 위와 같은 결과물을 위해서 라이언도 아주 간단한 프롬프트로 시작했어요.

- 아래와 같이 본인들의 데이터를 가지고 시작해 보세요. 

    - 다양한(?) 티키타카가 필요합니다.

- Gemini 3.0이 아주 영리해져서 Gemini Pro 사용하시는 분은 Gemini 사용도 같이 추천드립니다. 

    - 같은 구글 생태계라서 오히려 더 좋은 케미가 있어요

    - ChatGPT와 Gemini를 교차로 사용해 보시면 일이 줄어 드는 경험을 하실 수~~~ ^^

```
첨부의 구글 시트 파일을 소스로 Gemini API를 통해 데이터 분석을 진행하는 Apps Script와 Dashboard를 만들려고 해. 
일단 EDA 분석만 진행한다고 할때, 어떤 분석들을 진행하고 시각화할 수 있을까?
```

> GPT

EDA(탐색적 데이터 분석)를 진행할 때, 데이터의 구조를 파악하고 변수 간의 관계를 이해하기 위해 다음과 같은 단계별 분석과 시각화를 진행할 수 있습니다.

### 1 기초 통계 분석 (Descriptive Statistics)

데이터의 전반적인 분포와 중심 경향성을 확인합니다.

- **분석 내용**: 평균, 표준편차, 최소값, 최대값, 4분위수 등을 확인합니다.

- **결과 요약**:

    - 데이터 개수: 300개

    - 각 광고 매체의 평균 지출액은 약 460520 수준입니다.

    - `Product_Sold`(판매량)의 평균은 약 7,031건입니다.

### 2 단변량 분석 (Univariate Analysis)

각 변수가 개별적으로 어떤 분포를 가지는지 확인합니다.

- **히스토그램 (Histogram) & KDE Plot**: 각 변수의 데이터가 정규분포를 따르는지, 치우침이 있는지 확인합니다.
- 

[Image of histograms showing distributions of ad spend]

- **박스 플롯 (Boxplot)**: 데이터의 이상치(Outlier)가 존재하는지 확인합니다.
- 

[Image of boxplots for detecting outliers]

### 3 이변량 분석 (Bivariate Analysis)

광고비 지출과 판매량 간의 관계를 파악합니다.

- **산점도 (Scatter Plot)**: 각 광고 채널(`TV`, `Billboards` 등)과 `Product_Sold` 간의 관계를 점으로 찍어 선형 관계가 있는지 확인합니다.
- 

    - 대부분의 변수가 판매량과 양의 상관관계를 보이는 것으로 나타납니다.

### 4 다변량 분석 (Multivariate Analysis)

변수들 간의 상관관계를 종합적으로 파악합니다.

- **상관관계 히트맵 (Correlation Heatmap)**: 변수 간의 피어슨 상관계수를 색상으로 표현하여 어떤 채널이 판매량과 가장 강한 관계가 있는지 한눈에 파악합니다.

![Image](https://upload.cafenono.com/image/slashpageHome/20251129/022044_JKwyt0vFazrLrZhvcq?q=80&s=1280x180&t=outside&f=webp)

![Image](https://upload.cafenono.com/image/slashpageHome/20251129/022107_lTqGqoqUYl38N443yL?q=80&s=1280x180&t=outside&f=webp)

![Image](https://upload.cafenono.com/image/slashpageHome/20251129/022222_Yp5lqAW4nzDN4OHSXZ?q=80&s=1280x180&t=outside&f=webp)

![Image](https://upload.cafenono.com/image/slashpageHome/20251129/022048_Q3kJhczcQOzOloMIyU?q=80&s=1280x180&t=outside&f=webp)

![Image](https://upload.cafenono.com/image/slashpageHome/20251129/022110_cBXVKqTw6ba4RNYzZ0?q=80&s=1280x180&t=outside&f=webp)

![Image](https://upload.cafenono.com/image/slashpageHome/20251129/022226_XEJcOqmuazS5wfHz6c?q=80&s=1280x180&t=outside&f=webp)

- 아래와 같은 티키타카 개인별로 진행 with AI

```
이 시각화 결과물들을 apps script를 통해서 깔끔하고 멋진 대시보드 html로 구현하고 싶어. 
어떻게 구조화하면 될까?
```

```
마우스를 올리면 숫자가 뜨는(Hover) 애니메이션 차트를 구현해주고,
반응형 레이아웃를 사용해서 창 크기에 따라 배치가 예쁘고 적절하게 바뀔 수 있게 해줘.
사용자 경험(UX)을 위해서 엑셀 시트를 스크롤할 필요 없이, 앱처럼 탭 버튼만 누르면 화면이 즉시 전환되면 좋겠어.
html은 팝업(modal) 방식으로 일단 구현하자.
```

---

### Dashboard를 기획하는 가장 효율적인 방법

> 가장 좋은 방법 :
> - "맘에 드는 대시보드 화면을 캡쳐해서 '이렇게 구성해줘'라고 프롬프팅하기"

> 손 스케치도 가능:
> - 예쁜 디자인이 없어도 무방
> - A4 용지에 네모, 동그라미로 "여기엔 차트, 여기엔 버튼" 이라고 그려서 사진 찍어 올려도 코드로 짜줌.  (이게 진짜 마법!)

```
[ 📸 이미지와 함께 입력하는 메시지 예시 ]

[목표] 첨부한 이미지의 레이아웃과 디자인 스타일을 참고하여, 나의 Dashboard.html 코드를 작성해 주세요.

[디자인 요구사항 예시]

레이아웃: 이미지처럼 좌측 메뉴바 / 상단 카드 / 중앙 차트 배치를 잡아주세요. (이미지에 따라 변경)

색감: 이미지에 사용된 **메인 컬러(파란색 계열 등)**와 폰트 스타일을 비슷하게 적용해 주세요.

반응형: 화면 크기에 따라 배치가 자연스럽게 바뀌도록 해주세요.

[기능 제약 조건 (필수)] 단, 디자인은 이미지를 따르되 기능 구현은 아래 규칙을 지켜야 합니다.

차트는 이미지 그림 대신 Chart.js 라이브러리를 사용해서 구현해주세요.

```

- 태블로 공유 페이지

    - [https://public.tableau.com/app/discover/viz-of-the-day](https://public.tableau.com/app/discover/viz-of-the-day)

    - 다른 사람들이 작업한 태블로 대시보드를 레퍼런스로 삼아서 AI에게 이런 스타일과 구성으로 작업하고 싶다고 얘기하기

### 대시보드(html) 팝업(modal)으로 우선 구현하기

- 위 라이언의 예시 대시보드는 팝업(modal, 모달) 방식으로 구현되었어요.

- 지난 번에 실습했던 독립된 URL 페이지를 생성하는 웹앱 방식은 코드를 수정할 때마다, 배포를 계속해야 하는 번거로움이 있습니다.

- 팝업(모달) 방식은 코드 수정하고, 페이지 새로고침(F5)만 하면 바로 수정 결과를 팝업으로 확인할 수 있어서, 배포의 번거로움이 없어집니다.

- 팝업(모달) 방식을 위해 아래와 같은 프롬프트 포함하면 됩니다.

```
  html은 팝업(modal) 방식으로 구현해줘
```

- 웹앱을 구현하고 싶으신 분은 팝업으로 개발이 완료된 이후에, 코드를 웹앱으로 변경해 달라고 하면 큰 어려움없이 변경해 줍니다. 

> 단, 변환시에 코드 백업은 꼭 먼저하고, 웹앱 변환 진행!!!

> 대시보드 작업 중에도 백업 작업 진행해두는 것 추천 (시트 사본을 만들기 or 코드 백업)

| 구분 | 팝업 (Modal Dialog) | 웹앱 (Web App) |
| --- | --- | --- |
| 장점 | "1. 데이터 연동 최적화: 현재 열린 시트 데이터를 즉시 가져올 수 있음. | "1. 전문성(UX): 독립된 URL이 생성되어 진짜 SaaS 소프트웨어처럼 보임 (전체 화면 활용). |
|  | 2. 개발 속도: 코드를 저장만 하면 바로 실행 가능 (배포 과정 없음). | 2. 모바일 지원: 스마트폰 브라우저에서도 깔끔하게 볼 수 있음. |
|  | 3. 접근성: 시트 메뉴에서 바로 실행하므로 작업 흐름이 끊기지 않음." | 3. 독립성: 시트를 켜지 않고도 링크만으로 접속하여 데이터 확인 가능." |
| 단점 | "1. 화면 제약: 구글 시트 위의 작은 창(Iframe)으로만 떠서 답답할 수 있음. | "1. 연동 복잡성: getActiveSpreadsheet() 사용 불가 (특정 시트 ID를 코드에 고정해야 함). |
|  | 2. 휘발성: 창을 닫거나 새로고침하면 모든 상태가 초기화됨. | 2. 배포 번거로움: 코드를 수정할 때마다 **[새 버전 배포]**를 눌러야 사용자에게 반영됨. |
|  | 3. 모바일 불가: 모바일 구글 시트 앱에서는 작동하지 않음." | 3. 권한 설정: 사용자 접근 권한 처리가 조금 더 까다로울 수 있음." |

---

## 라이언의 예시 대시보드 구현을 위한 통합 Prompt (펼쳐보기)

```
**[당신의 역할]**
당신은 데이터 시각화와 웹 개발 능력을 갖춘 **'스마트 연구 보조원'**입니다.
제가 가진 데이터를 분석해서, 연구 논문에 활용할 수 있는 수준의 **'웹 대시보드'**를 만들어 주세요.

**1. [내가 원하는 도구: 무엇을 만드나요?]**
구글 시트에 있는 데이터를 가져와서, 웹 브라우저 팝업창에서 바로 볼 수 있는 **'마케팅 분석 리포트 시스템'**을 구축해 주세요.
`Code.gs`(서버)와 `Dashboard.html`(화면) 두 개의 코드를 작성해 주면 됩니다.

* **기능 1 (화면 구성):**
    * 상단에는 `[📋 기초 통계 분석]`, `[📘 분석 가이드]` 두 개의 탭을 만들어 주세요.
    * 데이터를 불러오는 동안에는 "분석 중입니다..."라는 로딩 표시를 띄워주세요.

* **기능 2 (기초 통계 탭 - 핵심!):**
    * 화면 맨 위에는 **'AI 연구 요약'** 박스를 만들어서, 데이터의 핵심 내용을 글로 정리해 주세요.
    * 그 아래에는 **4가지 핵심 분석 차트**를 바둑판 모양(그리드)으로 배치해 주세요.
        * ① 데이터 분포 (히스토그램)
        * ② 이상치 탐색 (박스 플롯)
        * ③ 상관관계 분석 (산점도)
        * ④ 상관계수 표 (히트맵)
    * **중요:** 논문에 바로 쓸 수 있도록, 각 차트 옆에 **[📷 이미지 저장]** 버튼을 꼭 만들어 주세요.

* **기능 3 (분석 가이드 탭):**
    * 통계가 낯선 사람들을 위해 '히스토그램', '박스 플롯' 등이 무엇인지 설명해 주는 도움말 페이지를 만들어 주세요.

**2. [데이터 설명: 어떤 데이터를 쓰나요?]**
* 데이터는 `Data_Lake`라는 시트에 있습니다.
* 첫 번째 줄은 제목(변수명)이고, 두 번째 줄부터 실제 데이터입니다.
* **변수 정보:**
    * `TV`, `Billboards`, `Google_Ads` 등 광고비 변수 (숫자)
    * `Product_Sold` (판매량, 숫자)
    * `Group` (실험군/대조군 구분, 문자 A/B)

**3. [제작 규칙: 이렇게 만들어 주세요]**
* html은 팝업(modal) 방식으로 구현해 주세요.
* 차트는 예쁘고 반응이 빠른 `Chart.js` 라이브러리를 사용해 주세요.
* 디자인은 깔끔하고 현대적인 스타일(CSS Flexbox 활용)로 해주세요.
* AI 기능을 쓸 때 필요한 'API 키'는 코드에 직접 적지 말고, 안전하게 설정(PropertiesService)에서 불러오도록 해주세요.
* 만약 데이터가 없거나 오류가 나면, 그냥 멈추지 말고 "데이터를 확인해 주세요"라고 친절하게 알려주세요.

```

For the site tree, see the [root Markdown](https://slashpage.com/lion.md).
