# 8-3. 회귀분석+Apps Script+구글시트(대시보드)

---

# 회귀분석 + Apps Script를 활용하여 전사에서 사용할 수 있는 Google Sheet 만들기 

[8-2.Advertising_Data_Regression](https://docs.google.com/spreadsheets/d/1elhy_6hv01AGH2rvOyaTXc-S-wmwiKhwsjoXQJOBED4/edit?usp=sharing)

🧩 구글 시트 구성 개요

- 시트1: raw_data – 분석할 데이터셋 : Google Docs8-2.Advertising_Data_Regression 

- 시트2: model_summary – 변수명, 회귀계수, 분석 리포트

- 시트3: optimizer_ui – 총 예산($) 입력하면

1. 최적의 마케팅 비용 집행 계획 수립 (마케팅 믹스 모델링-MMM) 

- 캠페인별 `추천 예산 ($)` 제안

- 최적의 `예상 판매량`  제안

2. 전략 유형별 3가지 마케팅 비용 집행안 제언

- ROI 극대화형, 리스크 분산형, 잠재력 실험형

---

### 📋 전체 시스템 흐름

1. `**raw_data**`: 분석할 데이터세트

2. **Apps Script (Backend)**:

- 데이터를 읽어서 **Gemini API**로 전송합니다.

- Gemini에게 "회귀분석 수행 + 회귀계수 추출 + 분석 리포트 작성"을 요청합니다.

3. `**model_summary**`: Gemini가 응답한 계수(숫자)와 분석 보고서(텍스트)를 자동으로 기록합니다.

4. `**optimizer_ui**`: `model_summary`의 계수를 참조하여 예산 배분 시뮬레이션을 돌립니다.

---

### 🛠️ 단계별 구현 가이드

### 1단계: 준비 사항 (Gemini API 키 발급)

가장 먼저 AI를 사용할 수 있는 '열쇠'가 필요합니다.

1. [Google AI Studio](https://aistudio.google.com/)에 접속합니다.

2. **Get API Key**를 클릭하여 키를 생성하고 복사해둡니다.

### 2단계: 시트 구조 잡기 (탭 3개 생성)

1. Google DocsAdvertising_Data 

    1. `파일`  > `사본 만들기`  > 각자 구글 드라이브에 저장

    2. `8-2.Advertising_Data`  > `raw_data`로 이름 변경 (대소문자 정확히)

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

2. `**model_summary**`: 빈 시트 추가하기

3. `**optimizer_ui**`: 빈 시트 추가하기

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

---

### 3단계: Apps Script 작성

구글 시트 상단 메뉴 **확장 프로그램  Apps Script**를 열고, 기존 내용을 지운 뒤 아래 Code.gs 전문을 복사해서 붙여넣고 > 저장

Code.gs - 펼쳐보기

```
// ==========================================
// 🚀 AI 마케팅 분석 시스템 (Gemini 2.5 Flash)
// ==========================================

// 1. 설정 변수
const GEMINI_MODEL_ID = 'gemini-2.5-flash'; // 최신 모델
const GEMINI_API_URL = `https://generativelanguage.googleapis.com/v1beta/models/${GEMINI_MODEL_ID}:generateContent`;

// 2. Gemini API 호출 함수 (핵심 엔진)
function callGemini(prompt) {
  // 스크립트 속성에서 API 키 가져오기
  const apiKey = PropertiesService.getScriptProperties().getProperty('GEMINI_API_KEY');
  
  if (!apiKey) {
    throw new Error("API 키가 없습니다. [설정 > 스크립트 속성]에 'GEMINI_API_KEY'를 추가하세요.");
  }
  
  if (!prompt || prompt.trim() === "") {
    throw new Error("프롬프트가 비어 있습니다.");
  }

  // API 요청 본문 (Payload)
  const payload = {
    contents: [
      {
        role: "user",
        parts: [{ text: prompt }]
      }
    ]
  };
  
  const options = {
    method: 'post',
    contentType: 'application/json',
    payload: JSON.stringify(payload),
    muteHttpExceptions: true
  };

  // API 호출
  const urlWithKey = `${GEMINI_API_URL}?key=${apiKey}`;
  const response = UrlFetchApp.fetch(urlWithKey, options);
  const responseCode = response.getResponseCode();
  const responseText = response.getContentText();

  // 에러 처리
  if (responseCode !== 200) {
    throw new Error(`Gemini API 오류 (${responseCode}): ${responseText}`);
  }

  const json = JSON.parse(responseText);
  
  // 응답 추출
  if (json.candidates && json.candidates.length > 0 && json.candidates[0].content) {
    return json.candidates[0].content.parts[0].text;
  } else {
    if (json.promptFeedback && json.promptFeedback.blockReason) {
       throw new Error(`AI 응답 차단됨 (사유: ${json.promptFeedback.blockReason})`);
    }
    throw new Error("AI 응답이 비어 있습니다.");
  }
}

// 3. 데이터 분석 실행 함수 (시트 연동)
function runAIAnalysis() {
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const rawSheet = ss.getSheetByName('raw_data');
  const summarySheet = ss.getSheetByName('model_summary');
  
  if (!rawSheet || !summarySheet) {
    SpreadsheetApp.getUi().alert("필수 시트 누락: 'raw_data', 'model_summary' 시트가 필요합니다.");
    return;
  }
  
  // 데이터 가져오기
  const data = rawSheet.getDataRange().getValues();
  if (data.length <= 1) {
    SpreadsheetApp.getUi().alert("'raw_data' 시트에 데이터가 없습니다.");
    return;
  }
  
  // CSV 변환
  const csvString = data.map(row => row.join(",")).join("\n");
  
  // 프롬프트 작성
  const prompt = `
  당신은 데이터 분석 전문가입니다. 아래 데이터를 분석하여 'Product_Sold' 판매량 예측을 위한 회귀분석 리포트를 작성하세요.
  
  [데이터]
  ${csvString}

  [요청사항]
  1. 'Product_Sold'를 종속변수로 하는 다중회귀분석을 수행하여 각 채널의 계수(Coefficient)와 절편(Intercept)을 도출하세요.
  2. 경영진을 위한 핵심 인사이트 리포트를 작성하세요.
  3. 결과는 반드시 아래 JSON 포맷으로만 출력하세요. (마크다운 없음, 순수 JSON)

  {
    "coefficients": {
      "TV": 0.0,
      "Billboards": 0.0,
      "Google_Ads": 0.0,
      "Social_Media": 0.0,
      "Influencer_Marketing": 0.0,
      "Affiliate_Marketing": 0.0,
      "Intercept": 0.0
    },
    "report": "여기에 줄바꿈을 사용하여 리포트 내용을 작성하세요."
  }
  `;

  try {
    ss.toast("Gemini 2.5 Flash가 분석 중입니다...", "🚀 분석 시작", 10);
    const resultText = callGemini(prompt);
    
    // JSON 파싱 (마크다운 제거)
    const cleanJson = resultText.replace(/```json/g, "").replace(/```/g, "").trim();
    let result;
    try {
      result = JSON.parse(cleanJson);
    } catch (e) {
      throw new Error("AI 응답 형식이 올바르지 않습니다. 원본: " + resultText.substring(0, 50) + "...");
    }
    
    // 시트 초기화
    summarySheet.clear();
    
    // 헤더 작성
    summarySheet.getRange("A1:C1").setValues([["변수명", "계수", "분석 리포트"]]);
    summarySheet.getRange("A1:C1").setFontWeight("bold").setBackground("#EFEFEF");
    
    // 계수 데이터 채우기
    let row = 2;
    if (result.coefficients) {
      for (const key in result.coefficients) {
        summarySheet.getRange(row, 1).setValue(key);
        summarySheet.getRange(row, 2).setValue(result.coefficients[key]);
        row++;
      }
    }
    
    // 리포트 채우기
    if (result.report) {
      const reportCell = summarySheet.getRange("C2");
      reportCell.setValue(result.report);
      reportCell.setWrapStrategy(SpreadsheetApp.WrapStrategy.WRAP);
      reportCell.setVerticalAlignment("top");
    }
    
    // 시트 디자인 정리
    summarySheet.getRange(2, 3, 15, 6).merge(); 
    summarySheet.setColumnWidth(1, 150);
    summarySheet.setColumnWidth(2, 100);
    summarySheet.setColumnWidth(3, 600);
    
    
    // 🔁 여기서 optimizer_ui 템플릿 자동 생성
    setupOptimizerUISheet();

    ss.toast("분석이 완료되었습니다!", "✅ 성공", 5);
    
  } catch (e) {
    SpreadsheetApp.getUi().alert("오류 발생:\n" + e.toString());
  }
}

// 🔧 optimizer_ui 기본 UI 자동 생성
function setupOptimizerUISheet() {
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  let sheet = ss.getSheetByName('optimizer_ui');

  // 시트가 없으면 새로 만들고, 있으면 내용만 비우기
  if (!sheet) {
    sheet = ss.insertSheet('optimizer_ui');
  } else {
    // 🔹 시트 전체가 아니라 1~20행만 초기화
    const maxCols = sheet.getMaxColumns();   // 시트 전체 컬럼 수
    sheet.getRange(1, 1, 20, maxCols).clear();   // 또는 clearContent()만 써도 됨
  }

  // ===== 1행: 총 예산 =====
  sheet.getRange('A1').setValue('총 예산 ($)').setFontWeight('bold');
  sheet.getRange('B1').setValue(0);
  sheet.getRange('C1').setValue('← 여기에 예산 금액 입력');

  // ===== 3~5행: 채널 / 최소비중 / 최대비중 =====
  // 채널 목록 (원하면 나중에 시트에서 직접 수정 가능)
  const channels = [
    'TV',
    'Billboards',
    'Google_Ads',
    'Social_Media',
    'Influencer_Marketing',
    'Affiliate_Marketing'
  ];

  // 3행: 채널명 헤더 + 각 채널 이름
  sheet.getRange('A3').setValue('채널명').setFontWeight('bold');
  sheet.getRange(3, 2, 1, channels.length).setValues([channels]).setFontWeight('bold');

  // 4행: 최소 비중 (기본 5%)
  sheet.getRange('A4').setValue('최소 비중').setFontWeight('bold');
  const minShares = [0.05, 0.05, 0.05, 0.05, 0.05, 0.05]; // 5%
  sheet.getRange(4, 2, 1, channels.length).setValues([minShares]);
  sheet.getRange(4, 2, 1, channels.length).setNumberFormat('0%');

  // 5행: 최대 비중 (기본 50%)
  sheet.getRange('A5').setValue('최대 비중').setFontWeight('bold');
  const maxShares = [0.5, 0.5, 0.5, 0.5, 0.5, 0.5]; // 50%
  sheet.getRange(5, 2, 1, channels.length).setValues([maxShares]);
  sheet.getRange(5, 2, 1, channels.length).setNumberFormat('0%');

  // ===== 7행: 최적화 결과 제목 + 수식 =====
  sheet.getRange('A7').setValue('[최적화 결과]').setFontWeight('bold');

  // B7: ArrayFormula로 OPTIMIZE_BUDGET 호출
  //   총 예산: B1
  //   채널 범위: B3 ~ 마지막 채널
  //   최소/최대 비중: B4/B5 ~ 마지막 채널
  const lastColLetter = sheet.getRange(3, 1 + channels.length).getA1Notation().replace(/[0-9]/g, ''); // 예: G
  const formula = `=ArrayFormula(OPTIMIZE_BUDGET(B1, B3:${lastColLetter}3, B4:${lastColLetter}4, B5:${lastColLetter}5))`;
  sheet.getRange('B7').setFormula(formula);

  // 보기 좋게 약간 포맷팅
  sheet.setColumnWidths(1, 1, 140);  // A열
  sheet.setColumnWidths(2, channels.length, 120);  // B~G열
  sheet.getRange('A1:C1').setVerticalAlignment('middle');
  sheet.getRange('A3:A5').setVerticalAlignment('middle');

  // 안내 문구가 잘 보이도록 줄바꿈
  sheet.getRange('C1').setWrapStrategy(SpreadsheetApp.WrapStrategy.WRAP);

  ss.toast('optimizer_ui 기본 템플릿이 생성되었습니다.', '✅ 완료', 5);
}

// 💰 4. 예산 최적화 UI 함수 (범위 인식 오류 해결 버전)
function OPTIMIZE_BUDGET(totalBudget, channelNames, minShares, maxShares) {
  // 1. 필수 입력값 체크
  if (!totalBudget || !channelNames || !minShares || !maxShares) {
    return [["💡 안내: 수식을 완성해 주세요.\n사용법: =OPTIMIZE_BUDGET(총예산, 채널명범위, 최소비중범위, 최대비중범위)"]];
  }

  // 2. model_summary 시트에서 계수 읽어오기
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('model_summary');
  if (!sheet) return [["Error: 'model_summary' 시트가 없습니다."]];

  const data = sheet.getDataRange().getValues();
  let coeffs = {};
  let intercept = 0;
  
  // 계수 파싱 (데이터가 있는 곳까지 순회)
  for (let i = 1; i < data.length; i++) {
    // 빈 행이면 패스
    if (!data[i][0]) continue;

    let key = data[i][0].toString().trim();
    let val = data[i][1];
    
    // 숫자가 아니면 패스
    if (typeof val !== 'number') continue;

    if (key.toLowerCase().includes('intercept') || key === '상수항' || key === '절편') {
      intercept = val;
    } else {
      coeffs[key] = val;
    }
  }

  // 3. [핵심 수정] 입력 범위 평탄화 (Flatten) 처리
  // 2차원 배열([[A,B,C]])이든 1차원이든 무조건 1줄로 폄
  var channels = flattenInput(channelNames);
  var mins = flattenInput(minShares);
  var maxs = flattenInput(maxShares);

  // 길이 체크 (채널명 개수와 비중 개수가 다르면 경고)
  if (channels.length !== mins.length || channels.length !== maxs.length) {
     return [["Error: 채널명 개수와 비중(최소/최대) 입력 칸 수가 다릅니다. 범위를 똑같이 맞춰주세요."]];
  }
  
  var allocation = {};
  var currentSpend = 0;
  var positiveCoeffSum = 0;
  
  // 4. 최적화 로직 실행
  for (var i = 0; i < channels.length; i++) {
    // 빈 셀은 건너뜀
    if (channels[i] === "" || channels[i] === null) continue;

    var ch = channels[i].toString().trim();
    var cf = coeffs[ch];

    // model_summary에 없는 채널명이면 0 처리 (에러 방지)
    if (cf === undefined) {
      cf = 0; 
      // 디버깅용: return [["Error: '" + ch + "' 채널의 계수를 찾을 수 없습니다. model_summary를 확인하세요."]];
    }

    var minAmt = totalBudget * (Number(mins[i]) || 0);
    
    allocation[ch] = { 
      budget: minAmt, 
      coeff: cf, 
      maxShare: (maxs[i] !== undefined && maxs[i] !== "") ? Number(maxs[i]) : 1.0 
    };
    currentSpend += minAmt;
    if (cf > 0) positiveCoeffSum += cf;
  }
  
  // 잔여 예산 배분
  var remain = totalBudget - currentSpend;
  if (remain > 0 && positiveCoeffSum > 0) {
    for (var key in allocation) {
      var item = allocation[key];
      if (item.coeff > 0) {
        var extra = remain * (item.coeff / positiveCoeffSum);
        // Max Cap 체크
        var maxLimit = totalBudget * item.maxShare;
        if ((item.budget + extra) > maxLimit) {
           extra = Math.max(0, maxLimit - item.budget);
        }
        item.budget += extra;
      }
    }
  }
  
  // 5. 결과 테이블 생성
  var output = [["채널명", "추천 예산 ($)", "비중 (%)", "효율 계수"]];
  var totalSales = intercept;
  var sumBudget = 0;
  
  for (var i = 0; i < channels.length; i++) {
    if (channels[i] === "" || channels[i] === null) continue;
    
    var ch = channels[i].toString().trim();
    var item = allocation[ch];
    
    if (item) {
      totalSales += item.budget * item.coeff;
      sumBudget += item.budget;
      
      output.push([
        ch, 
        Math.round(item.budget), 
        (totalBudget > 0 ? (item.budget/totalBudget*100).toFixed(1) : 0) + "%", 
        item.coeff.toFixed(4)
      ]);
    }
  }
  
  output.push(["합계", Math.round(sumBudget), "100%", "-"]);
  output.push(["📈 예상 판매량", Math.round(totalSales), "-", "-"]);
  
  return output;
}

// [도우미 함수] 2차원 배열을 1차원으로 펴주는 함수 (에러 해결사)
function flattenInput(input) {
  if (!input) return [];
  if (Array.isArray(input)) {
    // 배열 안에 배열이 있으면(2D) 펴고, 아니면 그대로
    return input.reduce(function(acc, val) {
      return acc.concat(Array.isArray(val) ? flattenInput(val) : val);
    }, []);
  }
  return [input]; // 단일 값이면 배열로 감싸서 리턴
}

// 5. 메뉴 생성 (시트 열릴 때 자동 실행)
function onOpen() {
  SpreadsheetApp.getUi()
    .createMenu('🚀 AI 분석 시스템')
    .addItem('데이터 분석 실행 (Gemini 2.5 Flash)', 'runAIAnalysis')
    .addToUi();
}

// 🧪 5. AI 다중 시나리오 생성 함수 (Gemini가 3가지 전략 제안)
// 🧪 5. AI 다중 시나리오 생성 함수 (예상 판매량 자동 계산 추가)
function runScenarioAnalysis() {
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const summarySheet = ss.getSheetByName('model_summary');
  const uiSheet = ss.getSheetByName('optimizer_ui');

  // 1. 데이터 읽기 (총 예산, 계수, 절편)
  const totalBudget = uiSheet.getRange("B1").getValue(); 
  const summaryData = summarySheet.getDataRange().getValues();
  
  let coeffs = {};
  let intercept = 0;
  let coeffInfo = "";
  
  // 계수와 절편 분리해서 저장
  for (let i = 1; i < summaryData.length; i++) {
    let key = summaryData[i][0].toString().trim();
    let val = summaryData[i][1];
    
    if (typeof val === 'number') {
      if (key.toLowerCase().includes('intercept') || key === '상수항' || key === '절편') {
        intercept = val;
      } else {
        coeffs[key] = val;
        coeffInfo += `- ${key}: ${val}\n`;
      }
    }
  }

  if (!coeffInfo) {
    SpreadsheetApp.getUi().alert("먼저 'model_summary' 시트에 계수 데이터가 있어야 합니다.");
    return;
  }

  // 2. 프롬프트 작성
  const prompt = `
  당신은 마케팅 최고 전략가(CMO)입니다. 
  현재 우리 회사의 광고 채널별 효율(회귀계수)은 아래와 같습니다.
  
  [채널 효율 데이터]
  ${coeffInfo}
  
  총 예산: $${totalBudget}
  
  위 데이터를 바탕으로 **3가지 서로 다른 마케팅 예산 배분 시나리오**를 제안해주세요.
  
  1. **ROI 극대화형**: 효율이 높은 채널에 예산을 집중 ("선택과 집중")
  2. **리스크 분산형**: 효율이 낮아도 골고루 배분하여 안정성 확보 ("안전 제일")
  3. **잠재력 실험형**: 효율이 중간인 채널 비중을 높여 성장 가능성 테스트 ("기회 탐색")
  
  결과는 반드시 아래 **JSON 형식**으로만 출력하세요. 설명 없이 JSON만 주세요.
  
  {
    "scenarios": [
      {
        "name": "1. ROI 극대화형",
        "description": "설명...",
        "allocation": { "TV": 0, "Billboards": 0, ... }
      },
      {
        "name": "2. 리스크 분산형",
        "description": "설명...",
        "allocation": { "TV": 0, "Billboards": 0, ... }
      },
      {
        "name": "3. 잠재력 실험형",
        "description": "설명...",
        "allocation": { "TV": 0, "Billboards": 0, ... }
      }
    ]
  }
  `;

  try {
    ss.toast("Gemini가 3가지 시나리오를 구상 중입니다...", "🤔 전략 수립 중", 10);
    const resultText = callGemini(prompt);
    
    // JSON 추출 및 파싱
    let cleanJson = resultText.replace(/```json/g, "").replace(/```/g, "").trim();
    const firstBrace = cleanJson.indexOf("{");
    const lastBrace = cleanJson.lastIndexOf("}");
    if (firstBrace !== -1 && lastBrace !== -1) {
      cleanJson = cleanJson.substring(firstBrace, lastBrace + 1);
    }

    let result;
    try {
      result = JSON.parse(cleanJson);
    } catch (e) {
      throw new Error("AI 응답을 JSON으로 읽을 수 없습니다. 원본:\n" + resultText);
    }
    
    // 4. optimizer_ui 시트에 결과 뿌리기
    let startRow = 21;
    uiSheet.getRange(startRow, 1).setValue("🤖 AI 추천 시나리오 (3 Options)");
    uiSheet.getRange(startRow, 1).setFontWeight("bold").setFontSize(12);
    
    let currentRow = startRow + 2;
    
    // 헤더 생성 (예상 판매량 컬럼 추가!)
    const channelKeys = Object.keys(result.scenarios[0].allocation);
    const headers = ["시나리오명", "전략 설명", "📈 예상 판매량", "채널별 배분($) -->", ...channelKeys];
    
    // 헤더 쓰기
    uiSheet.getRange(currentRow, 1, 1, headers.length).setValues([headers]).setBackground("#EFEFEF").setFontWeight("bold");
    currentRow++;

    // 데이터 쓰기 (판매량 직접 계산)
    result.scenarios.forEach(scenario => {
      // 1. 예상 판매량 계산: (각 채널 예산 * 계수)의 합 + 절편
      let predictedSales = intercept;
      
      channelKeys.forEach(ch => {
        let budget = scenario.allocation[ch] || 0;
        let coeff = coeffs[ch] || 0;
        predictedSales += budget * coeff;
      });

      // 2. 행 데이터 구성
      let rowData = [
        scenario.name, 
        scenario.description, 
        Math.round(predictedSales), // 계산된 판매량 (반올림)
        "" // 구분용 빈칸
      ]; 
      
      // 채널별 예산 추가
      for (let i = 4; i < headers.length; i++) {
        const chName = headers[i];
        rowData.push(scenario.allocation[chName] || 0);
      }
      
      uiSheet.getRange(currentRow, 1, 1, rowData.length).setValues([rowData]);
      currentRow++;
    });
    
    // 숫자 서식 적용 (판매량과 예산 열에 콤마 포맷)
    // C열(판매량)과 E열 이후(예산) 전체
    uiSheet.getRange(startRow + 3, 3, 3, 1).setNumberFormat("#,##0"); // 판매량
    uiSheet.getRange(startRow + 3, 5, 3, channelKeys.length).setNumberFormat("$#,##0"); // 예산
    
    ss.toast("예상 판매량이 포함된 시나리오가 완성되었습니다!", "✅ 성공", 5);

  } catch (e) {
    SpreadsheetApp.getUi().alert("시나리오 생성 중 오류:\n" + e.toString());
  }
}

// 메뉴에 함수 추가
function onOpen() {
  SpreadsheetApp.getUi()
    .createMenu('🚀 AI 분석 시스템')
    .addItem('데이터 분석 실행 (Gemini 2.5 Flash)', 'runAIAnalysis')
    .addSeparator() // 구분선
    .addItem('💡 3가지 전략 시나리오 제안받기', 'runScenarioAnalysis') // 추가됨
    .addToUi();
}

// End of Code.gs
```

> **주의:** `프로젝트 설정`  > `스크립트 속성` 에 개인별 발급받은 Gemini API 키를 우선 입력하고 저장!!! 
> 

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

 

---

### 4단계: 시스템 사용 시나리오

1. **데이터 입력:** `raw_data` 시트에 데이터를 확인

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

1. **분석 실행:** 구글 시트 상단 메뉴 **🚀 AI 분석 시스템  데이터 분석 실행**을 클릭합니다.

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

- 메뉴를 클릭하면 "승인 필요 (Authorization Required)" 팝업이 뜹니다. -> [계속] 클릭.

- `model_summary` 결과: Gemini가 데이터를 읽고 회귀분석을 한 뒤, `model_summary` 시트에 **계수**와 **보고서**를 작성 

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

- `optimizer_ui`  결과: `총 예산` , 채널별 예산 `최소` / `최대` 비중을 입력하면, 최적 예산안 시나리오 알려주는 템플릿 완성

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

1. **최적화 시뮬레이션:**

- `optimizer_ui` 시트로 이동합니다.

- 예산안 입력하면, 방금 AI가 분석한 따끈따끈한 계수를 바탕으로 최적 예산안과 더불어 `예상 판매량` 까지 알려줌

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

1. 추가 시나리오 분석

- `3가지 전략 시나리오 제안받기`  : 추가적인 마케팅 예산안 받기

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

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

- 하단에 3개 시나리오 추가 생성

1. ROI 극대화형

2. 리스크 분산형

3. 잠재력 실험형

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

---

---

## 참고용. 라이언의 최초 시작 Prompt

```
Apps Script를 통해서 회귀분석을 통한 판매량 예측 시스템을 아래와 같이 구현하려고 해.
가능여부 우선 알려주고, 어떻게 진행하면 좋을 지 단계별로 하나씩 안내해줘.
---
raw_data : 분석용 데이터 업로드하는 시트
model_summary : Gemini API를 통해서 raw_data 회귀분석 및 그 회사 보고용 결과 정리
optimizer_ui : model_summary 시트의 계수를 참조하고, 총 가용 예산, 채널별 최소/최대 비중을 입력하면  가장 효율적인 예산 배분표와 예측 판매량이 **자동으로 생성(Spill)**
```

- ChatGPT, Gemini 등을 활용하여 티키타카

- 시나리오 추가 기능을 위한 프롬프트 예시

```
AI가 회귀계수를 보고 추천 예산을 여러 개 생성할 수 있도록 해줘.
- "공격형", "안정형", "균형형"처럼 시나리오별 예산안 제안
```

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